1- #include " SystemHotkey.h "
1+ #include < pthread.h >
22
33#import < Foundation/Foundation.h>
44#import < Carbon/Carbon.h>
1515
1616extern JavaVM *jvm;
1717
18+ @interface SystemHotkey : NSObject
19+ + (void )subscribeToChanges ;
20+ @end
21+
1822enum LOG_LEVEL {
1923 LL_TRACE,
2024 LL_DEBUG,
2327 LL_ERROR
2428};
2529
26- void plog (int logLevel, const char *formatMsg, ...) {
30+ static void plog (int logLevel, const char *formatMsg, ...) {
2731 if (!jvm)
2832 return ;
2933 if (logLevel < LL_TRACE || logLevel > LL_ERROR || formatMsg == NULL )
@@ -268,12 +272,20 @@ static int symbolicHotKeysModifiers2java(int mask) {
268272
269273static const int numSymbolicHotkeys = sizeof (defaultSymbolicHotKeys) / sizeof (defaultSymbolicHotKeys[0 ]);
270274
271- // Current state of system shortcuts.
272- // Should only be read and written inside a @synchronized([SystemHotkey class]) block
273- static struct SymbolicHotKey currentSymbolicHotkeys[numSymbolicHotkeys];
275+ static struct SystemHotkeyState {
276+ bool symbolicHotkeysFilled;
277+ struct SymbolicHotKey currentSymbolicHotkeys[numSymbolicHotkeys];
278+
279+ bool initialized;
280+ bool enabled;
274281
275- // Should only be read and written inside a @synchronized([SystemHotkey class]) block
276- static bool subscribedToShortcutUpdates = false ;
282+ pthread_mutex_t mutex;
283+ } state = {
284+ .symbolicHotkeysFilled = false ,
285+ .initialized = false ,
286+ .enabled = false ,
287+ .mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER,
288+ };
277289
278290@interface DefaultParams : NSObject
279291@property (assign ) BOOL enabled;
@@ -457,9 +469,10 @@ static void updateAppleSymbolicHotkeysCache() {
457469 struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
458470 readAppleSymbolicHotkeys (hotkeys);
459471
460- @synchronized ([SystemHotkey class ]) {
461- memcpy (currentSymbolicHotkeys, hotkeys, numSymbolicHotkeys * sizeof (struct SymbolicHotKey));
462- }
472+ pthread_mutex_lock (&state.mutex );
473+ memcpy (state.currentSymbolicHotkeys , hotkeys, sizeof (hotkeys));
474+ state.symbolicHotkeysFilled = true ;
475+ pthread_mutex_unlock (&state.mutex );
463476}
464477
465478static void iterateAppleSymbolicHotkeys (struct SymbolicHotKey hotkeys[numSymbolicHotkeys], Visitor visitorBlock) {
@@ -559,19 +572,46 @@ static void readPbsHotkeys(Visitor visitorBlock) {
559572 }
560573}
561574
575+ static bool ensureInitializedAndEnabled () {
576+ pthread_mutex_lock (&state.mutex );
577+ if (state.initialized ) {
578+ bool enabled = state.enabled ;
579+ pthread_mutex_unlock (&state.mutex );
580+ return enabled;
581+ }
582+
583+ // JBR-8422
584+ const NSOperatingSystemVersion macOSVersion = [[NSProcessInfo processInfo ] operatingSystemVersion ];
585+ if (macOSVersion.majorVersion > 15 || (macOSVersion.majorVersion == 15 && macOSVersion.minorVersion >= 4 )) {
586+ state.initialized = true ;
587+ state.enabled = false ;
588+ pthread_mutex_unlock (&state.mutex );
589+ return false ;
590+ }
591+
592+ state.initialized = true ;
593+ state.enabled = true ;
594+ [SystemHotkey subscribeToChanges ];
595+ pthread_mutex_unlock (&state.mutex );
596+ updateAppleSymbolicHotkeysCache ();
597+
598+ return true ;
599+ }
600+
562601static void readAppleSymbolicHotkeysCached (struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
563- @synchronized ([SystemHotkey class ]) {
564- if (!subscribedToShortcutUpdates) {
565- [SystemHotkey setUp ];
566- }
602+ memset (hotkeys, 0 , sizeof (struct SymbolicHotKey) * numSymbolicHotkeys);
567603
568- memcpy (hotkeys, currentSymbolicHotkeys, numSymbolicHotkeys * sizeof (struct SymbolicHotKey));
604+ pthread_mutex_lock (&state.mutex );
605+ if (state.symbolicHotkeysFilled ) {
606+ memcpy (hotkeys, state.currentSymbolicHotkeys , sizeof (struct SymbolicHotKey) * numSymbolicHotkeys);
569607 }
608+ pthread_mutex_unlock (&state.mutex );
570609}
571610
572611static void readSystemHotkeysImpl (Visitor visitorBlock) {
573- // Normally, SystemHotkey would get initialized in LWCToolkit initialization.
574- // But since we can (theoretically) use this API from headless, let's check again.
612+ if (!ensureInitializedAndEnabled ()) {
613+ return ;
614+ }
575615
576616 struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
577617 readAppleSymbolicHotkeysCached (hotkeys);
@@ -581,40 +621,32 @@ static void readSystemHotkeysImpl(Visitor visitorBlock) {
581621}
582622
583623@implementation SystemHotkey
584- + (void )setUp {
585- // This should be called on LWCToolkit initialization.
586-
587- @synchronized (self) {
588- if (subscribedToShortcutUpdates) {
589- return ;
590- }
591-
592- // Update cached values
593- updateAppleSymbolicHotkeysCache ();
594-
595- // Subscribe to changes
596- NSUserDefaults *symbolicHotKeys = [[NSUserDefaults alloc ] initWithSuiteName: @" com.apple.symbolichotkeys" ];
597- [symbolicHotKeys addObserver: self forKeyPath: @" AppleSymbolicHotKeys" options: NSKeyValueObservingOptionNew
598- context: nil ];
599-
600- NSUserDefaults *pbsHotKeys = [[NSUserDefaults alloc ] initWithSuiteName: @" pbs" ];
601- [pbsHotKeys addObserver: self forKeyPath: @" NSServicesStatus" options: NSKeyValueObservingOptionNew context: nil ];
602-
603- subscribedToShortcutUpdates = true ;
604- }
624+ + (void )subscribeToChanges {
625+ // Subscribe to changes
626+ NSUserDefaults *symbolicHotKeys = [[NSUserDefaults alloc ] initWithSuiteName: @" com.apple.symbolichotkeys" ];
627+ [symbolicHotKeys addObserver: self forKeyPath: @" AppleSymbolicHotKeys" options: NSKeyValueObservingOptionNew
628+ context: nil ];
629+
630+ NSUserDefaults *pbsHotKeys = [[NSUserDefaults alloc ] initWithSuiteName: @" pbs" ];
631+ [pbsHotKeys addObserver: self forKeyPath: @" NSServicesStatus" options: NSKeyValueObservingOptionNew context: nil ];
605632}
606633
607634+ (void )observeValueForKeyPath : (NSString *)keyPath ofObject : (id )object change : (NSDictionary <NSKeyValueChangeKey, id> *)change context : (void *)context {
608635 // Called after AppleSymbolicHotKeys or pbs hotkeys change.
609- // This method can be called from any thread.
610636
611637 if ([keyPath isEqualToString: @" AppleSymbolicHotKeys" ]) {
612638 updateAppleSymbolicHotkeysCache ();
613639 }
614640
641+ // This method should only be called from the main thread, but let's check anyway
642+ if (pthread_main_np () == 0 ) {
643+ return ;
644+ }
645+
615646 // Since this notification is sent *after* the configuration was updated,
616647 // the user can safely re-read the hotkeys info after receiving this callback.
617648 // On the Java side, this simply enqueues the change handler to run on the EDT later.
649+
618650 JNIEnv* env = [ThreadUtilities getJNIEnv ];
619651 DECLARE_CLASS (jc_SystemHotkey, " java/awt/desktop/SystemHotkey" );
620652 DECLARE_STATIC_METHOD (jsm_onChange, jc_SystemHotkey, " onChange" , " ()V" );
@@ -624,12 +656,13 @@ + (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
624656@end
625657
626658bool isSystemShortcut_NextWindowInApplication (NSUInteger modifiersMask, int keyCode, NSString *chars) {
627- struct SymbolicHotKey shortcut;
628- @synchronized ([SystemHotkey class ]) {
629- if (!subscribedToShortcutUpdates) {
630- [SystemHotkey setUp ];
659+ struct SymbolicHotKey shortcut = defaultSymbolicHotKeys[Shortcut_FocusNextApplicationWindow];
660+ if (ensureInitializedAndEnabled ()) {
661+ pthread_mutex_lock (&state.mutex );
662+ if (state.symbolicHotkeysFilled ) {
663+ shortcut = state.currentSymbolicHotkeys [Shortcut_FocusNextApplicationWindow];
631664 }
632- shortcut = currentSymbolicHotkeys[Shortcut_FocusNextApplicationWindow] ;
665+ pthread_mutex_unlock (&state. mutex ) ;
633666 }
634667
635668 int ignoredModifiers = NSAlphaShiftKeyMask | NSFunctionKeyMask | NSNumericPadKeyMask | NSHelpKeyMask ;
0 commit comments