Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Windows dark mode caption is not changed #1544

Open
emako opened this issue Dec 28, 2023 · 15 comments
Open

[Feature] Windows dark mode caption is not changed #1544

emako opened this issue Dec 28, 2023 · 15 comments
Assignees
Labels
desktop MQTTX Desktop enhancement New feature or request feature This pr is a feature UI/UX Improve some UI \ UX
Milestone

Comments

@emako
Copy link

emako commented Dec 28, 2023

Motivation

Fix the dark mode in windows OS.

Detailed design

Use Win32API to change the caption background color.

Here are some C# sample code:

// NativeMethods
using System.Runtime.InteropServices;

namespace DarkModeForWindow;

internal static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool IsWindow(IntPtr hWnd);

    [DllImport("dwmapi.dll", PreserveSig = true)]
    public static extern int DwmSetWindowAttribute(nint hwnd, DwmWindowAttribute attr, ref int attrValue, int attrSize);

    [DllImport("ntdll.dll", SetLastError = true)]
    static extern int RtlGetVersion(ref RTL_OSVERSIONINFOEX lpVersionInformation);

    public static bool IsWindows10Version1809OrAbove()
    {
        RTL_OSVERSIONINFOEX versionInfo = new()
        {
            dwOSVersionInfoSize = (uint)Marshal.SizeOf(typeof(RTL_OSVERSIONINFOEX)),
        };

        if (RtlGetVersion(ref versionInfo) == 0)
        {
            // Windows 10 1809
            return versionInfo.dwMajorVersion >= 10 && versionInfo.dwBuildNumber >= 17763;
        }

        return false;
    }

    public static bool EnableDarkModeForWindow(nint hWnd, bool enable)
    {
        if (IsWindows10Version1809OrAbove())
        {
            int darkMode = enable ? 1 : 0;
            int hr = DwmSetWindowAttribute(hWnd, DwmWindowAttribute.UseImmersiveDarkMode, ref darkMode, sizeof(int));
            return hr >= 0;
        }
        return true;
    }

    public enum DwmWindowAttribute : uint
    {
        NCRenderingEnabled = 1,
        NCRenderingPolicy,
        TransitionsForceDisabled,
        AllowNCPaint,
        CaptionButtonBounds,
        NonClientRtlLayout,
        ForceIconicRepresentation,
        Flip3DPolicy,
        ExtendedFrameBounds,
        HasIconicBitmap,
        DisallowPeek,
        ExcludedFromPeek,
        Cloak,
        Cloaked,
        FreezeRepresentation,
        PassiveUpdateMode,
        UseHostBackdropBrush,
        UseImmersiveDarkMode = 20,
        WindowCornerPreference = 33,
        BorderColor,
        CaptionColor,
        TextColor,
        VisibleFrameBorderThickness,
        SystemBackdropType,
        Last,
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct RTL_OSVERSIONINFOEX
    {
        public uint dwOSVersionInfoSize;
        public uint dwMajorVersion;
        public uint dwMinorVersion;
        public uint dwBuildNumber;
        public uint dwPlatformId;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string szCSDVersion;
    }
}

// How to use win32api
nint hWnd = NativeMethods.FindWindow(null!, "MQTTX"); // May you can get the hWnd by Electron API

if (NativeMethods.IsWindow(hWnd))
{
    NativeMethods.EnableDarkModeForWindow(hWnd, true);
}

Alternatives

empty

More detail (optional)

The result of the c# sample code:

Before
image

After
image

@emako emako added the feature This pr is a feature label Dec 28, 2023
@emako
Copy link
Author

emako commented Dec 28, 2023

Full sample code here:

DarkModeForWindow.zip

You can try it using DarkModeForWindow\bin\Release\net472\DarkModeForWindow.exe.

Launch MQTTX and then launch DarkModeForWindow.exe.

@ysfscream ysfscream self-assigned this Dec 29, 2023
@ysfscream
Copy link
Member

Thank you very much for bringing up this issue and for the solution you've provided. @emako

I want to point out that MQTTX is primarily developed using JavaScript within the Electron framework. As such, I need to become more familiar with C#. In light of this, is there another possible approach to change the dark mode caption color, especially one that could be implemented within the JavaScript or Electron framework?

Also, your participation would be most welcome if you're interested in contributing to this feature enhancement. Your expertise and experience would be a valuable asset, and we look forward to your further involvement.

Thanks again for your contribution and support!

@emako
Copy link
Author

emako commented Jan 2, 2024

This requires me to take some time to try electron.

Meet something error on yarn install:

D:\Github\MQTTX>yarn install
yarn install v1.22.19
[1/6] Validating package.json...
[2/6] Resolving packages...
warning Resolution field "electron-builder@23.0.2" is incompatible with requested version "electron-builder@^22.2.0"
[3/6] Fetching packages...
[4/6] Linking dependencies...
warning "@vue/cli-plugin-unit-mocha > mocha-webpack@2.0.0-beta.0" has unmet peer dependency "webpack@^4.0.0".
warning " > monaco-editor-webpack-plugin@4.0.0" has unmet peer dependency "webpack@^4.5.0 || 5.x".
warning " > sass-loader@8.0.2" has unmet peer dependency "webpack@^4.36.0 || ^5.0.0".
warning "typeorm-uml > @oclif/command@1.8.0" has unmet peer dependency "@oclif/config@^1".
[5/6] Building fresh packages...
[10/11] ⠂ husky
[-/11] ⠂ waiting...
[3/11] ⠂ sqlite3
[11/11] ⠂ electron
error D:\Github\MQTTX\node_modules\electron: Command failed.
Exit code: 1
Command: node install.js
Arguments:
Directory: D:\Github\MQTTX\node_modules\electron
Output:
RequestError: unable to verify the first certificate
    at ClientRequest.<anonymous> (D:\Github\MQTTX\node_modules\got\source\request-as-event-emitter.js:178:14)
    at Object.onceWrapper (node:events:629:26)
    at ClientRequest.emit (node:events:526:35)
    at origin.emit (D:\Github\MQTTX\node_modules\@szmarczak\http-timer\source\index.js:37:11)
    at TLSSocket.socketErrorListener (node:_http_client:495:9)
    at TLSSocket.emit (node:events:514:28)

@ysfscream
Copy link
Member

This issue may be due to network problems. As suggested, you can try manually installing it if you like. Go to your project's node_modules\electron directory and then run node install.js. This should manually initiate the installation process for Electron.

@ysfscream ysfscream added desktop MQTTX Desktop enhancement New feature or request labels Jan 3, 2024
@emako
Copy link
Author

emako commented Jan 3, 2024

D:\Github\MQTTX>cd node_modules\electron

D:\Github\MQTTX\node_modules\electron>node install.js
RequestError: unable to verify the first certificate
    at ClientRequest.<anonymous> (D:\Github\MQTTX\node_modules\got\source\request-as-event-emitter.js:178:14)
    at Object.onceWrapper (node:events:629:26)
    at ClientRequest.emit (node:events:526:35)
    at origin.emit (D:\Github\MQTTX\node_modules\@szmarczak\http-timer\source\index.js:37:11)
    at TLSSocket.socketErrorListener (node:_http_client:495:9)
    at TLSSocket.emit (node:events:514:28)
    at emitErrorNT (node:internal/streams/destroy:151:8)
    at emitErrorCloseNT (node:internal/streams/destroy:116:3)
    at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

I have no idea above it.

@ysfscream
Copy link
Member

Network issues likely cause this error. To resolve this, please make sure you are using Node.js v16. Sometimes, specific versions of Node.js handle SSL/TLS certificate verification better.

If the problem persists after updating to Node.js v16, you could modify npm's configuration to ignore SSL certificate validation. This can be done with the following command:

npm config set strict-ssl false

Just to let you know, while this method might solve your immediate issue, it will cause your npm client to trust all SSL certificates, which could pose security risks. Therefore, it should be considered a temporary solution rather than a long-term practice. Once the issue is resolved, it is advisable to revert this setting to its default state:

npm config set strict-ssl true

Hope this helps you resolve the issue.

@emako
Copy link
Author

emako commented Jan 4, 2024

D:\Github\MQTTX>node -v
v20.10.0

npm config set strict-ssl false not work for node v20.10.0.

Have a try with v16 ...

@emako
Copy link
Author

emako commented Jan 4, 2024

electron/electron#26910

@emako
Copy link
Author

emako commented Jan 4, 2024

  1. set ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/ in environment variables
  2. execute: yarn install
  3. npm install sqlite3 --registry=https://registry.npm.taobao.org/ --node_sqlite3_binary_host_mirror=http://npm.taobao.org/mirrors

@emako
Copy link
Author

emako commented Jan 4, 2024

I'm not familiar with how to call the Win32 API in an electron environment.

Some demo code record following...

in package.json

"ffi-napi": "4.0.3",
 "ref-napi": "3.0.3"

in background.ts

// ...
win = new BrowserWindow
// ...

  console.warn(process.platform)
  console.warn(os.release())
  if (process.platform === 'win32' ) {
    win?.on('ready-to-show', () => {
      const hWnd = win?.getNativeWindowHandle()
      console.log('Window handle:', hWnd)
      WindowsDarkCaption.makeUp(hWnd);
    })
  }


class WindowsDarkCaption {
  public static makeUp(hWnd : Buffer | undefined | number) : void {
    if (!hWnd) {
      return
    }

    const ffi = require('ffi-napi')
    const ref = require('ref-napi')

    if (typeof hWnd === 'object') {
      hWnd = ref.address(hWnd);
    }

    const DwmWindowAttribute = {
      UseImmersiveDarkMode: 20,
    };

    const dwmapi = new ffi.Library('dwmapi', {
      'DwmSetWindowAttribute': ['int', ['pointer', 'uint', 'pointer', 'int']],
    });

    function dwmSetWindowAttribute(hWnd: number, attr: number, attrValue: number, attrSize: number) {
      return dwmapi.DwmSetWindowAttribute(hWnd, attr, attrValue, attrSize);
    }

    function enableDarkModeForWindow(hWnd : number, enable : boolean) {
        const darkMode = enable ? 1 : 0;
        return dwmSetWindowAttribute(hWnd, DwmWindowAttribute.UseImmersiveDarkMode, darkMode, 4) >= 0;
    }

    enableDarkModeForWindow(<number>hWnd, true);
  }
}

image

@emako
Copy link
Author

emako commented May 29, 2024

This software support dark mode title bar https://github.com/revezone/revezone

@emako
Copy link
Author

emako commented May 29, 2024

image

@emako
Copy link
Author

emako commented May 29, 2024

@emako
Copy link
Author

emako commented May 31, 2024

No one wants to fix it.

@emako emako closed this as not planned Won't fix, can't repro, duplicate, stale May 31, 2024
@ysfscream ysfscream reopened this May 31, 2024
@ysfscream ysfscream added this to the v1.10.0 milestone May 31, 2024
@ysfscream ysfscream added the UI/UX Improve some UI \ UX label May 31, 2024
@ysfscream
Copy link
Member

Sorry for the late reply. I have scheduled it for a future version, but currently, due to other high-priority tasks, we do not have the time to perfect it. Please be patient, and thank you for your suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
desktop MQTTX Desktop enhancement New feature or request feature This pr is a feature UI/UX Improve some UI \ UX
Projects
Status: Backlog
Development

No branches or pull requests

2 participants