unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Vince Salvino <salvino@coderedcorp.com>
To: "51404@debbugs.gnu.org" <51404@debbugs.gnu.org>
Subject: bug#51404: Support system dark mode on Windows 10
Date: Sun, 23 Jan 2022 00:00:32 +0000	[thread overview]
Message-ID: <CH2PR12MB42312B6F6237B1FBC6C67D3CA55D9@CH2PR12MB4231.namprd12.prod.outlook.com> (raw)
In-Reply-To: <CH2PR12MB4231758A937F6FB9F33291CFA5849@CH2PR12MB4231.namprd12.prod.outlook.com>

[-- Attachment #1: Type: text/plain, Size: 473 bytes --]

Attached is a patch which listens to the OS settings change, to dynamically change light/dark GUI during runtime.

Disclaimer here, I am not actually a C nor Win32 developer. I am not currently happy with the g_hwnds[256] implementation - that is purely a sloppy hack as a proof of concept. There is probably a much better way to track the window handles (all outlined in the TODO comment). However, this works if anyone wants to play around with it.

Vince Salvino


[-- Attachment #2: 0002-Support-MS-Windows-light-dark-mode-theme-change-duri.patch --]
[-- Type: application/octet-stream, Size: 7271 bytes --]

From 89ef390ca06397c21be0deea42dcc5eb0f8acfad Mon Sep 17 00:00:00 2001
From: Vince Salvino <salvino@coderedcorp.com>
Date: Sat, 22 Jan 2022 18:39:41 -0500
Subject: [PATCH] Support MS-Windows light/dark mode theme change during
 runtime. (Bug#51404)

---
 src/w32fns.c | 116 +++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 94 insertions(+), 22 deletions(-)

diff --git a/src/w32fns.c b/src/w32fns.c
index 37f9b813c6..429986051c 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -78,6 +78,7 @@ #define _WIN32_WINNT 0x0600
   See: https://github.com/microsoft/WindowsAppSDK/issues/41
 */
 #define DARK_MODE_APP_NAME L"DarkMode_Explorer"
+#define LIGHT_MODE_APP_NAME L"Explorer"
 /* For Windows 10 version 1809, 1903, 1909. */
 #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_OLD
 #define DWMWA_USE_IMMERSIVE_DARK_MODE_OLD 19
@@ -273,9 +274,25 @@ #define MENU_FREE_DELAY 1000
 int w32_minor_version;
 int w32_build_number;
 
-/* If the OS is set to use dark mode.  */
+/* If the OS supports light/dark mode. */
+BOOL w32_supports_darkmode = FALSE;
+/* If Emacs should use the OS's dark mode.  */
 BOOL w32_darkmode = FALSE;
 
+/* Track ALL window handles so they can be updated if the Windows
+   light/dark mode theme is changed. Each frame could have somehwere
+   between 1-6 HWNDs depending on which GUI features are enabled by
+   the user.
+
+   TODO: Convert this to something more dynamic:
+   * Remove upper limit (256) of HWNDs.
+   * When a HWND is destroyed it should be removed from this list
+     (just for sake of memory management cleanliness; it does not
+     actually cause a problem to make w32 calls to dead HWNDs).
+*/
+HWND g_hwnds[256];
+int g_hwnds_idx = -1;
+
 /* Distinguish between Windows NT and Windows 95.  */
 int os_subtype;
 
@@ -2303,19 +2320,50 @@ w32_init_class (HINSTANCE hinst)
     }
 }
 
+
+/* Gets the preferred Windows app mode:
+   * FALSE = Light mode (this is equivalent to the user specifying
+             Light, or the absence of any setting)
+   * TRUE = Dark mode (added in Windows 10 1809). */
+static BOOL
+w32_querydarkmode (void)
+{
+  if (w32_supports_darkmode)
+    {
+      /* Check Windows Registry for system theme.
+	 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);
+      return val && *val == 0;
+    }
+  return FALSE;
+}
+
 /* Applies the Windows system theme (light or dark) to the window
-   handle HWND.  */
+   handle HWND. `track` should generally be TRUE to keep a reference
+   to this HWND for future use. */
 static void
-w32_applytheme (HWND hwnd)
+w32_applytheme (HWND hwnd, bool track)
 {
-  if (w32_darkmode)
+  if (w32_supports_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);
+	{
+	  if (w32_darkmode)
+	    SetWindowTheme_fn (hwnd, DARK_MODE_APP_NAME, NULL);
+	  else
+	    SetWindowTheme_fn (hwnd, LIGHT_MODE_APP_NAME, NULL);
+	}
 
-      /* Set the titlebar to system dark mode.  */
+      /* Toggle darkmode titlebar on or off.  */
       if (DwmSetWindowAttribute_fn)
 	{
 	  /* Windows 10 version 2004 and up, Windows 11.  */
@@ -2323,9 +2371,26 @@ w32_applytheme (HWND hwnd)
 	  /* Windows 10 older than 2004.  */
 	  if (w32_build_number < 19041)
 	    attr = DWMWA_USE_IMMERSIVE_DARK_MODE_OLD;
+	  /* Toggle dark mode flag based on value of `w32_darkmode` */
 	  DwmSetWindowAttribute_fn (hwnd, attr,
 				    &w32_darkmode, sizeof (w32_darkmode));
 	}
+
+      /* After applying the theme, add the HWND to our global list so
+	 it can be changed later if the OS light/dark mode theme is
+	 changed. */
+      if(track)
+	{
+	  if(g_hwnds_idx < 256)
+	    {
+	      g_hwnds_idx++;
+	      g_hwnds[g_hwnds_idx] = hwnd;
+	    }
+	  else
+	    {
+	      printf("Number of window handles has exceeded capacity!");
+	    }
+	}
     }
 }
 
@@ -2342,7 +2407,7 @@ w32_createvscrollbar (struct frame *f, struct scroll_bar * bar)
 		       bar->left, bar->top, bar->width, bar->height,
 		       FRAME_W32_WINDOW (f), NULL, hinst, NULL);
   if (hwnd)
-    w32_applytheme (hwnd);
+    w32_applytheme (hwnd, TRUE);
   return hwnd;
 }
 
@@ -2359,7 +2424,7 @@ w32_createhscrollbar (struct frame *f, struct scroll_bar * bar)
 		       bar->left, bar->top, bar->width, bar->height,
 		       FRAME_W32_WINDOW (f), NULL, hinst, NULL);
   if (hwnd)
-    w32_applytheme (hwnd);
+    w32_applytheme (hwnd, TRUE);
   return hwnd;
 }
 
@@ -2447,7 +2512,7 @@ w32_createwindow (struct frame *f, int *coords)
       DragAcceptFiles (hwnd, TRUE);
 
       /* Enable system light/dark theme.  */
-      w32_applytheme (hwnd);
+      w32_applytheme (hwnd, TRUE);
 
       /* Do this to discard the default setting specified by our parent. */
       ShowWindow (hwnd, SW_HIDE);
@@ -5178,6 +5243,23 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 	 changed, so if Emacs is interested in some of them, it could
 	 update its internal values.  */
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+
+      /* Check if settings changed Light/Dark mode.
+	 Re-lookup the setting and update the HWNDs accordingly. */
+      if(w32_supports_darkmode)
+	{
+	  BOOL new_darkmode = w32_querydarkmode();
+	  if (w32_darkmode != new_darkmode)
+	    {
+	      w32_darkmode = new_darkmode;
+	      /* Loop through all known HWNDs and apply theme */
+	      for(int i=0; i<=g_hwnds_idx; i++)
+		{
+		  w32_applytheme(g_hwnds[i], false);
+		}
+	    }
+	}
+
       goto dflt;
 
     case WM_SETFOCUS:
@@ -11157,6 +11239,7 @@ globals_of_w32fns (void)
   if (os_subtype == OS_SUBTYPE_NT
       && w32_major_version >= 10 && w32_build_number >= 17763)
     {
+      w32_supports_darkmode = TRUE;
       /* Load dwmapi.dll and uxtheme.dll, which will be needed to set
 	 window themes.  */
       HMODULE dwmapi_lib = LoadLibrary("dwmapi.dll");
@@ -11165,19 +11248,8 @@ globals_of_w32fns (void)
       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;
+      /* Set the preferred mode from OS settings. */
+      w32_darkmode = w32_querydarkmode();
     }
 
   except_code = 0;
-- 
2.34.1.windows.1


  parent reply	other threads:[~2022-01-23  0:00 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-26  4:46 bug#51404: Support system dark mode on Windows 10 Vince Salvino
2021-10-26 14:01 ` Eli Zaretskii
2021-10-26 16:18   ` Eli Zaretskii
2021-10-26 16:49   ` Vince Salvino
2021-10-26 17:05     ` Eli Zaretskii
2021-10-26 18:20       ` Vince Salvino
2021-10-27 21:41         ` Vince Salvino
2021-10-28  7:15           ` Eli Zaretskii
2021-10-30 10:34             ` Eli Zaretskii
2021-10-30 17:13               ` Vince Salvino
2021-10-30 17:39                 ` Eli Zaretskii
2021-11-11  5:36                   ` bug#47291: " Lars Ingebrigtsen
2021-11-11  7:51                     ` Eli Zaretskii
2021-11-11 12:15                       ` Lars Ingebrigtsen
2021-11-11 15:08                         ` Eli Zaretskii
2021-11-12  3:00                           ` Lars Ingebrigtsen
2021-11-12  6:19                             ` Eli Zaretskii
2022-01-14  6:00 ` Vince Salvino
2022-01-23  0:00 ` Vince Salvino [this message]
2022-01-29  3:34   ` bug#51404: " Vince Salvino
2022-01-29  8:40     ` bug#51404: " Eli Zaretskii
2022-01-29 20:27       ` Vince Salvino

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CH2PR12MB42312B6F6237B1FBC6C67D3CA55D9@CH2PR12MB4231.namprd12.prod.outlook.com \
    --to=salvino@coderedcorp.com \
    --cc=51404@debbugs.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).