From 25ec63b9e6dfc411837aa936c06a9a848c690f49 Mon Sep 17 00:00:00 2001
From: Vince Salvino <salvino@coderedcorp.com>
Date: Mon, 25 Oct 2021 22:18:46 -0400
Subject: [PATCH] Support system dark mode on Windows 10 version 2004 and
 higher.

---
 src/w32.c    | 11 +++++--
 src/w32.h    |  3 ++
 src/w32fns.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 91 insertions(+), 4 deletions(-)

diff --git a/src/w32.c b/src/w32.c
index 9fe698d28d..f27c47bba5 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -2822,6 +2822,13 @@ #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
 
 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 ffa145b148..69468580b4 100644
--- a/src/w32.h
+++ b/src/w32.h
@@ -164,6 +164,9 @@ #define FILE_SERIAL             0x0800
 /* 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 14d1154a2b..54c03a4a1f 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -73,6 +73,15 @@ #define _WIN32_WINNT 0x0600
 #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"
+#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 @@ DECLARE_HANDLE(HMONITOR);
 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 @@ DECLARE_HANDLE(HMONITOR);
 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 @@ #define MENU_FREE_DELAY 1000
 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
-- 
2.33.0.windows.2

