diff --git a/meson.build b/meson.build index 36bcf19f60025..c19da3b76b67c 100644 --- a/meson.build +++ b/meson.build @@ -477,6 +477,7 @@ if features['win32-desktop'] cc.find_library('gdi32'), cc.find_library('ole32'), cc.find_library('uuid'), + cc.find_library('uxtheme'), cc.find_library('version'), cc.find_library('winmm')] dependencies += win32_desktop_libs diff --git a/video/out/w32_common.c b/video/out/w32_common.c index 64d69d8242c8b..17c0bed85857e 100644 --- a/video/out/w32_common.c +++ b/video/out/w32_common.c @@ -1000,6 +1000,47 @@ static void reinit_window_state(struct vo_w32_state *w32) update_window_state(w32); } +// Follow Windows settings and update dark mode state +// Microsoft documented how to enable dark mode for title bar: +// https://learn.microsoft.com/windows/apps/desktop/modernize/apply-windows-themes +// https://learn.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +// Documentation says to set the DWMWA_USE_IMMERSIVE_DARK_MODE attribute to +// TRUE to honor dark mode for the window, FALSE to always use light mode. While +// in fact setting it to TRUE causes dark mode to be always enabled, regardless +// of the settings. Since it is quite unlikely that it will be fixed, just use +// UxTheme API to check if dark mode should be applied and while at it enable it +// fully. Ideally this function should only call the DwmSetWindowAttribute(), +// but it just doesn't work as documented. +static void update_dark_mode(HWND hWnd) +{ + HANDLE uxtheme = GetModuleHandle(L"uxtheme.dll"); + BOOLEAN (WINAPI *pShouldAppsUseDarkMode)(void); + DWORD (WINAPI *pSetPreferredAppMode)(DWORD mode); + + pShouldAppsUseDarkMode = (void *) GetProcAddress(uxtheme, MAKEINTRESOURCEA(132)); + pSetPreferredAppMode = (void *) GetProcAddress(uxtheme, MAKEINTRESOURCEA(135)); + + if (pSetPreferredAppMode) + pSetPreferredAppMode(1); // allow dark mode + + HIGHCONTRAST hc = {sizeof(hc)}; + SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0); + bool high_contrast = hc.dwFlags & HCF_HIGHCONTRASTON; + + // if pShouldAppsUseDarkMode is not available, just assume it to be true + const BOOL use_dark_mode = !high_contrast && (!pShouldAppsUseDarkMode || + pShouldAppsUseDarkMode()); + + SetWindowTheme(hWnd, use_dark_mode ? L"DarkMode_Explorer" : L"", NULL); + +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + + DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, + &use_dark_mode, sizeof(use_dark_mode)); +} + static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -1293,6 +1334,9 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, case WM_DISPLAYCHANGE: force_update_display_info(w32); break; + case WM_SETTINGCHANGE: + update_dark_mode(w32->window); + break; } if (message == w32->tbtnCreatedMsg) { @@ -1543,6 +1587,8 @@ static void *gui_thread(void *ptr) goto done; } + update_dark_mode(w32->window); + if (SUCCEEDED(OleInitialize(NULL))) { ole_ok = true; diff --git a/wscript b/wscript index fafd15b0e1a08..c0c53f1abc531 100644 --- a/wscript +++ b/wscript @@ -201,7 +201,14 @@ main_dependencies = [ 'name': 'win32-desktop', 'desc': 'win32 desktop APIs', 'deps': '(os-win32 || os-cygwin) && !uwp', - 'func': check_cc(lib=['winmm', 'gdi32', 'ole32', 'uuid', 'avrt', 'dwmapi', 'version']), + 'func': check_cc(lib=['avrt', + 'dwmapi', + 'gdi32', + 'ole32', + 'uuid', + 'uxtheme', + 'version', + 'winmm']), }, { 'name': '--win32-internal-pthreads', 'desc': 'internal pthread wrapper for win32 (Vista+)',