Last active
April 3, 2024 17:10
-
-
Save ngquerol/33e099eceae527b368bdf196c95d1ba3 to your computer and use it in GitHub Desktop.
Patch to make emacs (emacs-28 branch) aware of the macOS 10.14+ system appearance changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 2fd6ee35bbcdd0a07e882e07706edaa01815853b Mon Sep 17 00:00:00 2001 | |
From: "Nicolas G. Querol" <[email protected]> | |
Date: Wed, 11 Nov 2020 12:35:47 +0100 | |
Subject: [PATCH] Add `ns-system-appearance-change-functions' hook | |
This implements a new hook, effective only on macOS >= 10.14 (Mojave), | |
that is called when the system changes its appearance (e.g. from light | |
to dark). Users can then implement functions that take this change | |
into account, for instance to load a particular theme. | |
Minor changes are also made to select the right "dark" appearance | |
(NSAppearanceNameDarkAqua) on macOS versions >= 10.14, the previous one | |
(NSAppearanceNameVibrantDark) being deprecated. | |
* src/frame.h (enum ns_appearance_type): Add new | |
"ns_appearance_dark_aqua" case. | |
* src/nsfns.m (defun x-create-frame): Use "dark aqua" appearance on | |
macOS >= 10.14. | |
* src/nsterm.m: | |
- (ns_set_appearance): Use "dark aqua" appearance on | |
macOS >= 10.14, reset appearance to the system one | |
if `ns-appearance' frame parameter is not set to | |
either `dark' or `light'. | |
- (initFrameFromEmacs): Use "dark aqua" appearance on | |
macOS >= 10.14. | |
- (EmacsApp) Add the `systemDidChangeAppearance' private method, | |
as well as the appropriate Key-Value Observing calls to update | |
the frame's appearance when the system (and thus the app's) | |
appearance changes. | |
- Add `ns-system-appearance-change-functions' hook variable and | |
symbol, to allow users to add functions that react to the | |
change of the system's appearance. | |
- Add `ns-system-appearance' variable, to allow users to consult | |
the current system appearance. | |
Here is an example on how to use this new feature: | |
(defun my/load-theme (appearance) | |
"Load theme, taking current system APPEARANCE into consideration." | |
(mapc #'disable-theme custom-enabled-themes) | |
(pcase appearance | |
('light (load-theme 'tango t)) | |
('dark (load-theme 'tango-dark t)))) | |
(add-hook 'ns-system-appearance-change-functions #'my/load-theme) | |
The hook being run on each system appearance change as well as at | |
startup time, Emacs should then always load the appropriate theme. | |
--- | |
src/frame.h | 3 +- | |
src/nsfns.m | 13 ++++- | |
src/nsterm.m | 153 ++++++++++++++++++++++++++++++++++++++++++++++----- | |
3 files changed, 153 insertions(+), 16 deletions(-) | |
diff --git a/src/frame.h b/src/frame.h | |
index b75ef79cff2..ebb992cbde9 100644 | |
--- a/src/frame.h | |
+++ b/src/frame.h | |
@@ -71,7 +71,8 @@ #define EMACS_FRAME_H | |
{ | |
ns_appearance_system_default, | |
ns_appearance_aqua, | |
- ns_appearance_vibrant_dark | |
+ ns_appearance_vibrant_dark, | |
+ ns_appearance_dark_aqua | |
}; | |
#endif | |
#endif /* HAVE_WINDOW_SYSTEM */ | |
diff --git a/src/nsfns.m b/src/nsfns.m | |
index d11d4146ebd..c06cf12d8b6 100644 | |
--- a/src/nsfns.m | |
+++ b/src/nsfns.m | |
@@ -1315,14 +1315,25 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. | |
store_frame_param (f, Qundecorated, FRAME_UNDECORATED (f) ? Qt : Qnil); | |
#ifdef NS_IMPL_COCOA | |
+#ifndef NSAppKitVersionNumber10_14 | |
+#define NSAppKitVersionNumber10_14 1671 | |
+#endif | |
tem = gui_display_get_arg (dpyinfo, parms, Qns_appearance, NULL, NULL, | |
RES_TYPE_SYMBOL); | |
if (EQ (tem, Qdark)) | |
- FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark; | |
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14) | |
+ { | |
+ FRAME_NS_APPEARANCE (f) = ns_appearance_dark_aqua; | |
+ } | |
+ else | |
+ { | |
+ FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark; | |
+ } | |
else if (EQ (tem, Qlight)) | |
FRAME_NS_APPEARANCE (f) = ns_appearance_aqua; | |
else | |
FRAME_NS_APPEARANCE (f) = ns_appearance_system_default; | |
+ | |
store_frame_param (f, Qns_appearance, | |
(!NILP (tem) && !EQ (tem, Qunbound)) ? tem : Qnil); | |
diff --git a/src/nsterm.m b/src/nsterm.m | |
index ea8770c93e9..3a4d9dca8ec 100644 | |
--- a/src/nsterm.m | |
+++ b/src/nsterm.m | |
@@ -1878,11 +1878,25 @@ Hide the window (X11 semantics) | |
return; | |
if (EQ (new_value, Qdark)) | |
- FRAME_NS_APPEARANCE (f) = ns_appearance_vibrant_dark; | |
- else if (EQ (new_value, Qlight)) | |
- FRAME_NS_APPEARANCE (f) = ns_appearance_aqua; | |
+ { | |
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 | |
+#ifndef NSAppKitVersionNumber10_14 | |
+#define NSAppKitVersionNumber10_14 1671 | |
+#endif | |
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14) | |
+ FRAME_NS_APPEARANCE(f) = ns_appearance_dark_aqua; | |
+ else | |
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 */ | |
+ FRAME_NS_APPEARANCE(f) = ns_appearance_vibrant_dark; | |
+ } | |
+ else if (EQ(new_value, Qlight)) | |
+ { | |
+ FRAME_NS_APPEARANCE (f) = ns_appearance_aqua; | |
+ } | |
else | |
- FRAME_NS_APPEARANCE (f) = ns_appearance_system_default; | |
+ { | |
+ FRAME_NS_APPEARANCE (f) = ns_appearance_system_default; | |
+ } | |
[window setAppearance]; | |
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */ | |
@@ -5375,6 +5389,7 @@ Needs to be here because ns_initialize_display_info () uses AppKit classes. | |
========================================================================== */ | |
+static const void *kEmacsAppKVOContext = &kEmacsAppKVOContext; | |
@implementation EmacsApp | |
@@ -5620,6 +5635,18 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification | |
object:nil]; | |
#endif | |
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 | |
+ [self addObserver:self | |
+ forKeyPath:NSStringFromSelector(@selector(effectiveAppearance)) | |
+ options:NSKeyValueObservingOptionInitial|NSKeyValueObservingOptionNew | |
+ context:&kEmacsAppKVOContext]; | |
+ | |
+ pending_funcalls = Fcons(list3(Qrun_hook_with_args, | |
+ Qns_system_appearance_change_functions, | |
+ Vns_system_appearance), | |
+ pending_funcalls); | |
+#endif | |
+ | |
#ifdef NS_IMPL_COCOA | |
/* Some functions/methods in CoreFoundation/Foundation increase the | |
maximum number of open files for the process in their first call. | |
@@ -5658,6 +5685,68 @@ - (void)antialiasThresholdDidChange:(NSNotification *)notification | |
#endif | |
} | |
+- (void)observeValueForKeyPath:(NSString *)keyPath | |
+ ofObject:(id)object | |
+ change:(NSDictionary *)change | |
+ context:(void *)context | |
+{ | |
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 | |
+ if (context == kEmacsAppKVOContext | |
+ && object == self | |
+ && [keyPath isEqualToString: | |
+ NSStringFromSelector (@selector(effectiveAppearance))]) | |
+ [self systemAppearanceDidChange: | |
+ [change objectForKey:NSKeyValueChangeNewKey]]; | |
+ else | |
+#endif /* (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 */ | |
+ [super observeValueForKeyPath:keyPath | |
+ ofObject:object | |
+ change:change | |
+ context:context]; | |
+} | |
+ | |
+#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 | |
+#ifndef NSAppKitVersionNumber10_14 | |
+#define NSAppKitVersionNumber10_14 1671 | |
+#endif | |
+- (void)systemAppearanceDidChange:(NSAppearance *)newAppearance | |
+{ | |
+ | |
+ if (NSAppKitVersionNumber < NSAppKitVersionNumber10_14) | |
+ return; | |
+ | |
+ NSAppearanceName appearance_name = | |
+ [newAppearance bestMatchFromAppearancesWithNames:@[ | |
+ NSAppearanceNameAqua, NSAppearanceNameDarkAqua | |
+ ]]; | |
+ | |
+ BOOL is_dark_appearance = | |
+ [appearance_name isEqualToString:NSAppearanceNameDarkAqua]; | |
+ Vns_system_appearance = is_dark_appearance ? Qdark : Qlight; | |
+ | |
+ run_system_appearance_change_hook (); | |
+} | |
+ | |
+static inline void run_system_appearance_change_hook (void) | |
+{ | |
+ if (NILP (Vns_system_appearance_change_functions)) | |
+ return; | |
+ | |
+ block_input (); | |
+ | |
+ bool owfi = waiting_for_input; | |
+ waiting_for_input = false; | |
+ | |
+ safe_call2 (Qrun_hook_with_args, | |
+ Qns_system_appearance_change_functions, | |
+ Vns_system_appearance); | |
+ Fredisplay(Qt); | |
+ | |
+ waiting_for_input = owfi; | |
+ | |
+ unblock_input (); | |
+} | |
+#endif /* (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 */ | |
/* Termination sequences: | |
C-x C-c: | |
@@ -5822,6 +5911,14 @@ - (void)applicationDidResignActive: (NSNotification *)notification | |
ns_send_appdefined (-1); | |
} | |
+- (void)applicationWillTerminate:(NSNotification *)notification | |
+{ | |
+ NSTRACE ("[EmacsApp applicationWillTerminate:]"); | |
+ | |
+ [self removeObserver:self | |
+ forKeyPath:NSStringFromSelector(@selector(effectiveAppearance)) | |
+ context:&kEmacsAppKVOContext]; | |
+} | |
/* ========================================================================== | |
@@ -8819,17 +8916,26 @@ - (void)setAppearance | |
#define NSAppKitVersionNumber10_10 1343 | |
#endif | |
- if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10) | |
- return; | |
- | |
- if (FRAME_NS_APPEARANCE (f) == ns_appearance_vibrant_dark) | |
- appearance = | |
- [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]; | |
- else if (FRAME_NS_APPEARANCE (f) == ns_appearance_aqua) | |
- appearance = | |
- [NSAppearance appearanceNamed:NSAppearanceNameAqua]; | |
+ if (NSAppKitVersionNumber < NSAppKitVersionNumber10_10) | |
+ return; | |
- [self setAppearance:appearance]; | |
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 | |
+#ifndef NSAppKitVersionNumber10_14 | |
+#define NSAppKitVersionNumber10_14 1671 | |
+#endif | |
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14 | |
+ && FRAME_NS_APPEARANCE(f) == ns_appearance_dark_aqua) | |
+ appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; | |
+ else | |
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 */ | |
+ if (FRAME_NS_APPEARANCE(f) == ns_appearance_vibrant_dark) | |
+ appearance = | |
+ [NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]; | |
+ else if (FRAME_NS_APPEARANCE (f) == ns_appearance_aqua) | |
+ appearance = | |
+ [NSAppearance appearanceNamed:NSAppearanceNameAqua]; | |
+ | |
+ [self setAppearance:appearance]; | |
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 */ | |
} | |
@@ -9996,6 +10102,25 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with | |
This variable is ignored on macOS < 10.7 and GNUstep. Default is t. */); | |
ns_use_mwheel_momentum = YES; | |
+ DEFVAR_LISP ("ns-system-appearance", Vns_system_appearance, | |
+ doc: /* Current system appearance, i.e. `dark' or `light'. | |
+ | |
+This variable is ignored on macOS < 10.14 and GNUstep. Default is nil. */); | |
+ Vns_system_appearance = Qnil; | |
+ DEFSYM(Qns_system_appearance, "ns-system-appearance"); | |
+ | |
+ DEFVAR_LISP ("ns-system-appearance-change-functions", | |
+ Vns_system_appearance_change_functions, | |
+ doc: /* List of functions to call when the system appearance changes. | |
+Each function is called with a single argument, which corresponds to the new | |
+system appearance (`dark' or `light'). | |
+ | |
+This hook is also run once at startup. | |
+ | |
+This variable is ignored on macOS < 10.14 and GNUstep. Default is nil. */); | |
+ Vns_system_appearance_change_functions = Qnil; | |
+ DEFSYM(Qns_system_appearance_change_functions, "ns-system-appearance-change-functions"); | |
+ | |
/* TODO: Move to common code. */ | |
DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars, | |
doc: /* SKIP: real doc in xterm.c. */); | |
-- | |
2.44.0 |
@muffinmad, could you try the latest revision of this patch ? Hopefully that resolves your issues.
Sure.
Just for the record, that was a single crash.
@muffinmad, any crashes to report? If not, I'll make a PR to the formulas that use it.
@ngquerol No crashes so far.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@muffinmad, could you try the latest revision of this patch ? Hopefully that resolves your issues.