summaryrefslogtreecommitdiff
path: root/src/w32fns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/w32fns.c')
-rw-r--r--src/w32fns.c158
1 files changed, 155 insertions, 3 deletions
diff --git a/src/w32fns.c b/src/w32fns.c
index 14d1154a2bc..02a6d78b51c 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -73,6 +73,20 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <imm.h>
#include <windowsx.h>
+/*
+ Internal/undocumented constants for Windows Dark mode.
+ See: https://github.com/microsoft/WindowsAppSDK/issues/41
+*/
+#define DARK_MODE_APP_NAME L"DarkMode_Explorer"
+/* For Windows 10 version 1809, 1903, 1909. */
+#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_OLD
+#define DWMWA_USE_IMMERSIVE_DARK_MODE_OLD 19
+#endif
+/* For Windows 10 version 2004 and higher, and Windows 11. */
+#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 +199,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 +218,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 +273,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 +2303,36 @@ w32_init_class (HINSTANCE hinst)
}
}
+/* Applies the Windows system theme (light or dark) to the window
+ handle HWND. */
+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)
+ {
+ /* Windows 10 version 2004 and up, Windows 11. */
+ DWORD attr = DWMWA_USE_IMMERSIVE_DARK_MODE;
+ /* Windows 10 older than 2004. */
+ if (w32_build_number < 19041)
+ attr = DWMWA_USE_IMMERSIVE_DARK_MODE_OLD;
+ DwmSetWindowAttribute_fn (hwnd, attr,
+ &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 +2341,15 @@ 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 +2358,9 @@ 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 +2446,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);
@@ -5114,6 +5173,13 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
goto dflt;
+ case WM_SETTINGCHANGE:
+ /* Inform the Lisp thread that some system-wide setting has
+ changed, so if Emacs is interested in some of them, it could
+ update its internal values. */
+ my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+ goto dflt;
+
case WM_SETFOCUS:
dpyinfo->faked_key = 0;
reset_modifiers ();
@@ -7459,7 +7525,8 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
/* Calculate size of tooltip window. */
size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
- make_fixnum (w->pixel_height), Qnil);
+ make_fixnum (w->pixel_height), Qnil,
+ Qnil);
/* Add the frame's internal border to calculated size. */
width = XFIXNUM (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
height = XFIXNUM (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
@@ -10257,6 +10324,60 @@ to be converted to forward slashes by the caller. */)
}
#endif /* WINDOWSNT */
+
+/* Query a value from the Windows Registry (under HKCU and HKLM),
+ where `key` is the registry key, `name` is the name, and `lpdwtype`
+ is a pointer to the return value's type. `lpwdtype` can be NULL if
+ you do not care about the type.
+
+ Returns: pointer to the value, or null pointer if the key/name does
+ not exist. */
+LPBYTE
+w32_get_resource (const char *key, const char *name, LPDWORD lpdwtype)
+{
+ LPBYTE lpvalue;
+ HKEY hrootkey = NULL;
+ DWORD cbData;
+
+ /* Check both the current user and the local machine to see if
+ we have any resources. */
+
+ if (RegOpenKeyEx (HKEY_CURRENT_USER, key, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
+ {
+ lpvalue = NULL;
+
+ if (RegQueryValueEx (hrootkey, name, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
+ && (lpvalue = xmalloc (cbData)) != NULL
+ && RegQueryValueEx (hrootkey, name, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
+ {
+ RegCloseKey (hrootkey);
+ return (lpvalue);
+ }
+
+ xfree (lpvalue);
+
+ RegCloseKey (hrootkey);
+ }
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS)
+ {
+ lpvalue = NULL;
+
+ if (RegQueryValueEx (hrootkey, name, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS
+ && (lpvalue = xmalloc (cbData)) != NULL
+ && RegQueryValueEx (hrootkey, name, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS)
+ {
+ RegCloseKey (hrootkey);
+ return (lpvalue);
+ }
+
+ xfree (lpvalue);
+
+ RegCloseKey (hrootkey);
+ }
+
+ return (NULL);
+}
/***********************************************************************
Initialization
@@ -11028,6 +11149,37 @@ globals_of_w32fns (void)
set_thread_description = (SetThreadDescription_Proc)
get_proc_addr (hm_kernel32, "SetThreadDescription");
+ /* Support OS dark mode on Windows 10 version 1809 and higher.
+ See `w32_applytheme` which uses appropriate APIs per version of Windows.
+ For future wretches who may need to understand Windows build numbers:
+ https://docs.microsoft.com/en-us/windows/release-health/release-information
+ */
+ if (os_subtype == OS_SUBTYPE_NT
+ && w32_major_version >= 10 && w32_build_number >= 17763)
+ {
+ /* Load dwmapi.dll and uxtheme.dll, 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 and set w32_darkmode.
+ TODO: "Nice to have" would be to create a lisp setting (which
+ defaults to this Windows Registry value), then read that lisp
+ value here instead. This would allow the user to forcibly
+ override the system theme (which is also user-configurable in
+ Windows settings; see MS-Windows section in Emacs manual). */
+ LPBYTE val =
+ w32_get_resource ("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
+ "AppsUseLightTheme",
+ NULL);
+ if (val && *val == 0)
+ w32_darkmode = TRUE;
+ }
+
except_code = 0;
except_addr = 0;
#ifndef CYGWIN