diff --git a/src/w32.c b/src/w32.c index 9fe698d28d72..f27c47bba5c8 100644 --- a/src/w32.c +++ b/src/w32.c @@ -2822,6 +2822,13 @@ sys_putenv (char *str) LPBYTE w32_get_resource (const char *key, LPDWORD lpdwtype) +{ + return w32_query_registry(REG_ROOT, key, lpdwtype); +} + +/* Enables reading any key/name from the Windows Registry */ +LPBYTE +w32_query_registry (const char *root, const char *key, LPDWORD lpdwtype) { LPBYTE lpvalue; HKEY hrootkey = NULL; @@ -2830,7 +2837,7 @@ w32_get_resource (const char *key, LPDWORD lpdwtype) /* Check both the current user and the local machine to see if we have any resources. */ - if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS) + if (RegOpenKeyEx (HKEY_CURRENT_USER, root, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS) { lpvalue = NULL; @@ -2847,7 +2854,7 @@ w32_get_resource (const char *key, LPDWORD lpdwtype) RegCloseKey (hrootkey); } - if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS) + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, root, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS) { lpvalue = NULL; diff --git a/src/w32.h b/src/w32.h index ffa145b1484a..69468580b451 100644 --- a/src/w32.h +++ b/src/w32.h @@ -164,6 +164,9 @@ extern void reset_standard_handles (int in, int out, /* Return the string resource associated with KEY of type TYPE. */ extern LPBYTE w32_get_resource (const char * key, LPDWORD type); +/* Utility to query [HKCU|HKLM]\root\key from the Windows Registry */ +extern LPBYTE w32_query_registry (const char * root, const char * key, LPDWORD type); + extern void release_listen_threads (void); extern void init_ntproc (int); extern void term_ntproc (int); diff --git a/src/w32fns.c b/src/w32fns.c index 14d1154a2bc5..54c03a4a1f8b 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -73,6 +73,15 @@ along with GNU Emacs. If not, see . */ #include #include +/* + Internal/undocumented constants for Windows Dark mode. + See: https://github.com/microsoft/WindowsAppSDK/issues/41 +*/ +#define DARK_MODE_APP_NAME L"DarkMode_Explorer" +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + #ifndef FOF_NO_CONNECTED_ELEMENTS #define FOF_NO_CONNECTED_ELEMENTS 0x2000 #endif @@ -185,6 +194,11 @@ typedef BOOL (WINAPI *IsDebuggerPresent_Proc) (void); typedef HRESULT (WINAPI *SetThreadDescription_Proc) (HANDLE hThread, PCWSTR lpThreadDescription); +typedef HRESULT (WINAPI * SetWindowTheme_Proc) + (IN HWND hwnd, IN LPCWSTR pszSubAppName, IN LPCWSTR pszSubIdList); +typedef HRESULT (WINAPI * DwmSetWindowAttribute_Proc) + (HWND hwnd, DWORD dwAttribute, IN LPCVOID pvAttribute, DWORD cbAttribute); + TrackMouseEvent_Proc track_mouse_event_fn = NULL; ImmGetCompositionString_Proc get_composition_string_fn = NULL; ImmGetContext_Proc get_ime_context_fn = NULL; @@ -199,6 +213,8 @@ EnumDisplayMonitors_Proc enum_display_monitors_fn = NULL; GetTitleBarInfo_Proc get_title_bar_info_fn = NULL; IsDebuggerPresent_Proc is_debugger_present = NULL; SetThreadDescription_Proc set_thread_description = NULL; +SetWindowTheme_Proc SetWindowTheme_fn = NULL; +DwmSetWindowAttribute_Proc DwmSetWindowAttribute_fn = NULL; extern AppendMenuW_Proc unicode_append_menu; @@ -252,6 +268,9 @@ int w32_major_version; int w32_minor_version; int w32_build_number; +/* If the OS is set to use dark mode. */ +BOOL w32_darkmode = FALSE; + /* Distinguish between Windows NT and Windows 95. */ int os_subtype; @@ -2279,10 +2298,31 @@ w32_init_class (HINSTANCE hinst) } } +/* Applies the Windows system theme (light or dark) to a window handle. */ +static void +w32_applytheme(HWND hwnd) +{ + if (w32_darkmode) { + /* Set window theme to that of a built-in Windows app (Explorer) + because it has dark scroll bars and other UI elements. */ + if(SetWindowTheme_fn) { + SetWindowTheme_fn(hwnd, DARK_MODE_APP_NAME, NULL); + } + /* Set the titlebar to system dark mode. */ + if (DwmSetWindowAttribute_fn) { + DwmSetWindowAttribute_fn + (hwnd, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &w32_darkmode, + sizeof(w32_darkmode)); + } + } +} + static HWND w32_createvscrollbar (struct frame *f, struct scroll_bar * bar) { - return CreateWindow ("SCROLLBAR", "", + HWND hwnd = CreateWindow ("SCROLLBAR", "", /* Clip siblings so we don't draw over child frames. Apparently this is not always sufficient so we also try to make bar windows @@ -2291,12 +2331,16 @@ w32_createvscrollbar (struct frame *f, struct scroll_bar * bar) /* Position and size of scroll bar. */ bar->left, bar->top, bar->width, bar->height, FRAME_W32_WINDOW (f), NULL, hinst, NULL); + if(hwnd) { + w32_applytheme(hwnd); + } + return hwnd; } static HWND w32_createhscrollbar (struct frame *f, struct scroll_bar * bar) { - return CreateWindow ("SCROLLBAR", "", + HWND hwnd = CreateWindow ("SCROLLBAR", "", /* Clip siblings so we don't draw over child frames. Apparently this is not always sufficient so we also try to make bar windows @@ -2305,6 +2349,10 @@ w32_createhscrollbar (struct frame *f, struct scroll_bar * bar) /* Position and size of scroll bar. */ bar->left, bar->top, bar->width, bar->height, FRAME_W32_WINDOW (f), NULL, hinst, NULL); + if(hwnd) { + w32_applytheme(hwnd); + } + return hwnd; } static void @@ -2390,6 +2438,9 @@ w32_createwindow (struct frame *f, int *coords) /* Enable drag-n-drop. */ DragAcceptFiles (hwnd, TRUE); + /* Enable system light/dark theme. */ + w32_applytheme(hwnd); + /* Do this to discard the default setting specified by our parent. */ ShowWindow (hwnd, SW_HIDE); @@ -11028,6 +11079,32 @@ globals_of_w32fns (void) set_thread_description = (SetThreadDescription_Proc) get_proc_addr (hm_kernel32, "SetThreadDescription"); + /* + Support OS dark mode on Windows 10 version 2004 and higher. + For future wretches who may need to understand Windows build numbers: + https://docs.microsoft.com/en-us/windows/release-health/release-information + */ + if (w32_major_version >= 10 && w32_build_number >= 19041 + && os_subtype == OS_SUBTYPE_NT) { + + /* Load dwmapi and uxtheme, which will be needed to set window themes. */ + HMODULE dwmapi_lib = LoadLibrary("dwmapi.dll"); + DwmSetWindowAttribute_fn = (DwmSetWindowAttribute_Proc) + get_proc_addr (dwmapi_lib, "DwmSetWindowAttribute"); + HMODULE uxtheme_lib = LoadLibrary("uxtheme.dll"); + SetWindowTheme_fn = (SetWindowTheme_Proc) + get_proc_addr (uxtheme_lib, "SetWindowTheme"); + + /* Check Windows Registry for system theme. DWORD set to 0 or 1. */ + LPBYTE val = w32_query_registry + ("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", + "AppsUseLightTheme", + NULL); + if (val && (DWORD)*val == 0) { + w32_darkmode = TRUE; + } + } + except_code = 0; except_addr = 0; #ifndef CYGWIN