From bc0f662573684919af7440b41531e9861a86ce6f Mon Sep 17 00:00:00 2001 From: Ahmed Khanzada Date: Sun, 16 Nov 2025 20:08:59 -0800 Subject: [PATCH] Add `toolkit-theme' and 'toolkit-theme-set-functions' * etc/NEWS: Document `toolkit-theme' and 'toolkit-theme-set-functions' * lisp/frame.el: Add 'toolkit-theme-set-functions' * src/frame.c: Add `toolkit-theme' Elisp variable * src/gtkutil.c: (xg_update_dark_mode_for_all_displays): Set `toolkit-theme' and call 'toolkit-theme-set-functions' * src/w32fns.c: (w32_applytheme): Send WM_EMACS_SET_TOOLKIT_THEME message to Lisp thread. * src/w32term.h: Declare WM_EMACS_SET_TOOLKIT_THEME * src/w32term.c: (w32_read_socket): Receive WM_EMACS_SET_TOOLKIT_THEME message and set `toolkit-theme' and call 'toolkit-theme-set-functions' --- etc/NEWS | 7 +++++++ lisp/frame.el | 6 ++++++ src/frame.c | 13 +++++++++++++ src/gtkutil.c | 10 ++++++++++ src/w32fns.c | 3 +++ src/w32term.c | 14 ++++++++++++++ src/w32term.h | 1 + 7 files changed, 54 insertions(+) diff --git a/etc/NEWS b/etc/NEWS index d5a0c847142..201c4abb3df 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3603,6 +3603,13 @@ Emacs when built with the pure GTK toolkit now respects desktop dark and light modes for drawing a GTK toolbar and widgets, automatically toggling between them. +--- +** toolkit-theme-set-functions called when the toolkit theme is set for Emacs. +When the theme is set on PGTK or MS-Windows builds, +'toolkit-theme-set-functions' is called. Result is stored in var +'toolkit-theme' as either symbol 'dark' or 'light', but may be +expanded to future toolkit-specific symbols in the future. + * Changes in Emacs 31.1 on Non-Free Operating Systems diff --git a/lisp/frame.el b/lisp/frame.el index c48b2c4af1e..307312ef71a 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -1528,6 +1528,12 @@ the `background-mode' terminal parameter." ;; :background (face-attribute 'default :foreground)) ;; (frame-set-background-mode (selected-frame)))) +(defvar toolkit-theme-set-functions nil + "Abnormal hook run when the system theme is applied to Emacs. +Functions on this hook are called with the theme name as a symbol: +`light' or `dark'. By the time the hook is called, `toolkit-theme' will +already be set to one of these values as well.") + ;;;; Frame configurations diff --git a/src/frame.c b/src/frame.c index 8112af5acba..65900665dd5 100644 --- a/src/frame.c +++ b/src/frame.c @@ -7618,4 +7618,17 @@ The default is \\+`inhibit' in NS builds and nil everywhere else. */); Fprovide (Qmove_toolbar, Qnil); #endif /* !HAVE_EXT_TOOL_BAR || USE_GTK */ #endif /* HAVE_WINDOW_SYSTEM */ + +#if (defined(HAVE_PGTK) && defined(HAVE_GSETTINGS)) || defined (WINDOWSNT) + DEFVAR_LISP ("toolkit-theme", Vtoolkit_theme, + doc: /* The current toolkit theme. +Either the symbol `light' or the symbol `dark', reflecting the system's +current theme preference. This variable is updated automatically when +the system theme changes. + +This variable is only available on PGTK and MS-Windows builds. */); + Vtoolkit_theme = Qnil; + DEFSYM (Qlight, "light"); + DEFSYM (Qdark, "dark"); +#endif } diff --git a/src/gtkutil.c b/src/gtkutil.c index 329ad6c33b5..5e9e8254506 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1475,6 +1475,16 @@ xg_update_dark_mode_for_all_displays (bool dark_mode_p) = gtk_settings_get_for_screen (screen); xg_set_gtk_theme_dark_mode (dark_mode_p, settings); } + + Vtoolkit_theme = dark_mode_p ? Qdark : Qlight; + Lisp_Object hook = intern ("toolkit-theme-set-functions"); + if (!NILP (Fboundp (hook))) + { + Lisp_Object args[2]; + args[0] = hook; + args[1] = Vtoolkit_theme; + Frun_hook_with_args (2, args); + } } /* Set initial dark mode for a new frame (called during frame diff --git a/src/w32fns.c b/src/w32fns.c index 679e8197fd8..21f1a967321 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -2440,6 +2440,8 @@ w32_applytheme (HWND hwnd) &w32_darkmode, sizeof (w32_darkmode)); } } + WPARAM dark_mode_p = w32_darkmode ? 1 : 0; + PostThreadMessage (dwMainThreadId, WM_EMACS_SET_TOOLKIT_THEME, dark_mode_p, 0); } static HWND @@ -3645,6 +3647,7 @@ w32_name_of_message (UINT msg) M (WM_CHAR), M (WM_EMACS_DRAGOVER), M (WM_EMACS_DROP), + M (WM_EMACS_SET_TOOLKIT_THEME), #undef M { 0, 0 } }; diff --git a/src/w32term.c b/src/w32term.c index c9e7ac39379..56e1fca9333 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -6083,6 +6083,20 @@ w32_read_socket (struct terminal *terminal, check_visibility = 1; break; + case WM_EMACS_SET_TOOLKIT_THEME: + { + Vtoolkit_theme = msg.msg.wParam ? Qdark : Qlight; + Lisp_Object hook = intern ("toolkit-theme-set-functions"); + if (!NILP (Fboundp (hook))) + { + Lisp_Object args[2]; + args[0] = hook; + args[1] = Vtoolkit_theme; + Frun_hook_with_args (2, args); + } + } + break; + #if HAVE_W32NOTIFY case WM_EMACS_FILENOTIFY: f = w32_window_to_frame (dpyinfo, msg.msg.hwnd); diff --git a/src/w32term.h b/src/w32term.h index 12adc72f01c..d0d7d75f5cd 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -715,6 +715,7 @@ do { \ #define WM_EMACS_DRAGOVER (WM_EMACS_START + 27) #define WM_EMACS_DROP (WM_EMACS_START + 28) #define WM_EMACS_END (WM_EMACS_START + 29) +#define WM_EMACS_SET_TOOLKIT_THEME (WM_EMACS_START + 30) #define WND_FONTWIDTH_INDEX (0) #define WND_LINEHEIGHT_INDEX (4)