mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2026-01-10 02:21:42 +01:00
JBR-5690: Reload next window shortcut when it changes
(cherry picked from commit be0d27983c)
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
#import "NSApplicationAWT.h"
|
||||
#import "PropertiesUtilities.h"
|
||||
#import "ApplicationDelegate.h"
|
||||
#import "SystemHotkey.h"
|
||||
|
||||
#import "sun_lwawt_macosx_LWCToolkit.h"
|
||||
|
||||
@@ -249,6 +250,8 @@ static void setUpAWTAppKit(BOOL installObservers)
|
||||
CFRelease(notBusyObserver);
|
||||
|
||||
setBusy(YES);
|
||||
|
||||
[SystemHotkey setUp];
|
||||
}
|
||||
|
||||
JNIEnv* env = [ThreadUtilities getJNIEnv];
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2024 JetBrains s.r.o.
|
||||
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
//
|
||||
// This code is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License version 2 only, as
|
||||
// published by the Free Software Foundation. Oracle designates this
|
||||
// particular file as subject to the "Classpath" exception as provided
|
||||
// by Oracle in the LICENSE file that accompanied this code.
|
||||
//
|
||||
// This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
// version 2 for more details (a copy is included in the LICENSE file that
|
||||
// accompanied this code).
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License version
|
||||
// 2 along with this work; if not, write to the Free Software Foundation,
|
||||
// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
//
|
||||
// Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
// or visit www.oracle.com if you need additional information or have any
|
||||
// questions.
|
||||
|
||||
|
||||
#ifndef JBR_SYSTEMHOTKEY_H
|
||||
#define JBR_SYSTEMHOTKEY_H
|
||||
|
||||
#import <objc/NSObject.h>
|
||||
|
||||
@interface SystemHotkey : NSObject
|
||||
+ (void)setUp;
|
||||
@end
|
||||
|
||||
#endif //JBR_SYSTEMHOTKEY_H
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "SystemHotkey.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
@@ -261,6 +263,15 @@ static const struct SymbolicHotKey defaultSymbolicHotKeys[] = {
|
||||
[Shortcut_ToggleTypingFeedback] = { "ToggleTypingFeedback", "Turn typing feedback on or off", YES, 65535, 65535, 0, 14 },
|
||||
};
|
||||
|
||||
static const int numSymbolicHotkeys = sizeof(defaultSymbolicHotKeys) / sizeof(defaultSymbolicHotKeys[0]);
|
||||
|
||||
// Current state of system shortcuts.
|
||||
// Should only be read and written inside a @synchronized([SystemHotkey class]) block
|
||||
static struct SymbolicHotKey currentSymbolicHotkeys[numSymbolicHotkeys];
|
||||
|
||||
// Should only be read and written inside a @synchronized([SystemHotkey class]) block
|
||||
static bool subscribedToShortcutUpdates = false;
|
||||
|
||||
@interface DefaultParams: NSObject
|
||||
@property (assign) BOOL enabled;
|
||||
@property (strong) NSString *key_equivalent;
|
||||
@@ -304,7 +315,7 @@ static int javaModifiers2NS(int jmask) {
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef bool (^ Visitor)(int, const char *, int, const char *, int);
|
||||
typedef void (^ Visitor)(int, const char *, int, const char *, int);
|
||||
|
||||
static void visitServicesShortcut(Visitor visitorBlock, NSString * key_equivalent, NSString * desc) {
|
||||
// @ - command
|
||||
@@ -331,9 +342,8 @@ static void visitServicesShortcut(Visitor visitorBlock, NSString * key_equivalen
|
||||
visitorBlock(-1, keyChar.UTF8String, modifiers, desc.UTF8String, -1);
|
||||
}
|
||||
|
||||
static void readAppleSymbolicHotkeys(Visitor visitorBlock) {
|
||||
static const int numIds = sizeof(defaultSymbolicHotKeys) / sizeof(defaultSymbolicHotKeys[0]);
|
||||
const NSOperatingSystemVersion macOSVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
|
||||
static void readAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
|
||||
// Called from the main thread
|
||||
|
||||
@try {
|
||||
NSDictionary<NSString *, id> *shk = [[NSUserDefaults standardUserDefaults] persistentDomainForName:@"com.apple.symbolichotkeys"];
|
||||
@@ -359,8 +369,7 @@ static void readAppleSymbolicHotkeys(Visitor visitorBlock) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct SymbolicHotKey hotkeys[numIds];
|
||||
memcpy(hotkeys, defaultSymbolicHotKeys, sizeof(hotkeys));
|
||||
memcpy(hotkeys, defaultSymbolicHotKeys, numSymbolicHotkeys * sizeof(struct SymbolicHotKey));
|
||||
|
||||
for (id keyObj in hkObj) {
|
||||
if (![keyObj isKindOfClass:[NSString class]]) {
|
||||
@@ -435,33 +444,45 @@ static void readAppleSymbolicHotkeys(Visitor visitorBlock) {
|
||||
hotkeys[uid].key = p1 == nil ? 0xFFFF : [p1 intValue];
|
||||
hotkeys[uid].modifiers = p2 == nil ? 0 : [p2 intValue];
|
||||
}
|
||||
|
||||
for (int uid = 0; uid < numIds; ++uid) {
|
||||
struct SymbolicHotKey* hotkey = &hotkeys[uid];
|
||||
if (!hotkey->enabled) continue;
|
||||
if (hotkey->macOSVersion > macOSVersion.majorVersion) continue;
|
||||
|
||||
char keyCharBuf[64];
|
||||
const char *keyCharStr = keyCharBuf;
|
||||
if (hotkey->character >= 0 && hotkey->character <= 0xFF) {
|
||||
sprintf(keyCharBuf, "%c", hotkey->character);
|
||||
} else {
|
||||
keyCharStr = NULL;
|
||||
}
|
||||
int modifiers = symbolicHotKeysModifiers2java(hotkey->modifiers);
|
||||
visitorBlock(hotkey->key, keyCharStr, modifiers, hotkey->description, uid);
|
||||
|
||||
if (uid == Shortcut_FocusNextApplicationWindow) {
|
||||
// Derive the "Move focus to the previous window in application" shortcut
|
||||
if (!(modifiers & AWT_SHIFT_DOWN_MASK)) {
|
||||
visitorBlock(hotkey->key, keyCharStr, modifiers | AWT_SHIFT_DOWN_MASK, "Move focus to the previous window in application", -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
NSLog(@"readAppleSymbolicHotkeys: catched exception, reason '%@'", exception.reason);
|
||||
NSLog(@"readCachedAppleSymbolicHotkeys: catched exception, reason '%@'", exception.reason);
|
||||
}
|
||||
}
|
||||
|
||||
static void updateAppleSymbolicHotkeysCache() {
|
||||
struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
|
||||
readAppleSymbolicHotkeys(hotkeys);
|
||||
|
||||
@synchronized ([SystemHotkey class]) {
|
||||
memcpy(currentSymbolicHotkeys, hotkeys, numSymbolicHotkeys * sizeof(struct SymbolicHotKey));
|
||||
}
|
||||
}
|
||||
|
||||
static void iterateAppleSymbolicHotkeys(struct SymbolicHotKey hotkeys[numSymbolicHotkeys], Visitor visitorBlock) {
|
||||
const NSOperatingSystemVersion macOSVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
|
||||
|
||||
for (int uid = 0; uid < numSymbolicHotkeys; ++uid) {
|
||||
struct SymbolicHotKey* hotkey = &hotkeys[uid];
|
||||
if (!hotkey->enabled) continue;
|
||||
if (hotkey->macOSVersion > macOSVersion.majorVersion) continue;
|
||||
|
||||
char keyCharBuf[64];
|
||||
const char *keyCharStr = keyCharBuf;
|
||||
if (hotkey->character >= 0 && hotkey->character <= 0xFF) {
|
||||
sprintf(keyCharBuf, "%c", hotkey->character);
|
||||
} else {
|
||||
keyCharStr = NULL;
|
||||
}
|
||||
int modifiers = symbolicHotKeysModifiers2java(hotkey->modifiers);
|
||||
visitorBlock(hotkey->key, keyCharStr, modifiers, hotkey->description, uid);
|
||||
|
||||
if (uid == Shortcut_FocusNextApplicationWindow) {
|
||||
// Derive the "Move focus to the previous window in application" shortcut
|
||||
if (!(modifiers & AWT_SHIFT_DOWN_MASK)) {
|
||||
visitorBlock(hotkey->key, keyCharStr, modifiers | AWT_SHIFT_DOWN_MASK, "Move focus to the previous window in application", -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,46 +555,84 @@ static void readPbsHotkeys(Visitor visitorBlock) {
|
||||
}
|
||||
}
|
||||
|
||||
static void readAppleSymbolicHotkeysCached(struct SymbolicHotKey hotkeys[numSymbolicHotkeys]) {
|
||||
@synchronized ([SystemHotkey class]) {
|
||||
if (!subscribedToShortcutUpdates) {
|
||||
[SystemHotkey setUp];
|
||||
}
|
||||
|
||||
memcpy(hotkeys, currentSymbolicHotkeys, numSymbolicHotkeys * sizeof(struct SymbolicHotKey));
|
||||
}
|
||||
}
|
||||
|
||||
static void readSystemHotkeysImpl(Visitor visitorBlock) {
|
||||
readAppleSymbolicHotkeys(visitorBlock);
|
||||
// Normally, SystemHotkey would get initialized in LWCToolkit initialization.
|
||||
// But since we can (theoretically) use this API from headless, let's check again.
|
||||
|
||||
struct SymbolicHotKey hotkeys[numSymbolicHotkeys];
|
||||
readAppleSymbolicHotkeysCached(hotkeys);
|
||||
|
||||
iterateAppleSymbolicHotkeys(hotkeys, visitorBlock);
|
||||
readPbsHotkeys(visitorBlock);
|
||||
}
|
||||
|
||||
bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, int keyCode, NSString *chars) {
|
||||
static NSString * shortcutCharacter = nil;
|
||||
static int shortcutMask = 0;
|
||||
static int shortcutKeyCode = -1;
|
||||
if (shortcutCharacter == nil && shortcutKeyCode == -1) {
|
||||
readSystemHotkeysImpl(
|
||||
^bool(int vkeyCode, const char * keyCharStr, int jmodifiers, const char * descriptionStr, int hotkeyUid) {
|
||||
if (hotkeyUid != Shortcut_FocusNextApplicationWindow)
|
||||
return true;
|
||||
@implementation SystemHotkey
|
||||
+ (void)setUp {
|
||||
// This should be called on LWCToolkit initialization.
|
||||
|
||||
if (keyCharStr != NULL) {
|
||||
shortcutCharacter = [[NSString stringWithFormat:@"%s", keyCharStr] retain];
|
||||
}
|
||||
|
||||
if (vkeyCode != -1) {
|
||||
shortcutKeyCode = vkeyCode;
|
||||
}
|
||||
|
||||
shortcutMask = javaModifiers2NS(jmodifiers);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
if (shortcutCharacter == nil && shortcutKeyCode == -1) {
|
||||
shortcutCharacter = @"`";
|
||||
shortcutMask = NSCommandKeyMask;
|
||||
@synchronized (self) {
|
||||
if (subscribedToShortcutUpdates) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update cached values
|
||||
updateAppleSymbolicHotkeysCache();
|
||||
|
||||
// Subscribe to changes
|
||||
NSUserDefaults *symbolicHotKeys = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.symbolichotkeys"];
|
||||
[symbolicHotKeys addObserver:self forKeyPath:@"AppleSymbolicHotKeys" options:NSKeyValueObservingOptionNew
|
||||
context:nil];
|
||||
|
||||
subscribedToShortcutUpdates = true;
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context {
|
||||
// Called when AppleSymbolicHotKeys change.
|
||||
// This method can be called from any thread.
|
||||
|
||||
if ([keyPath isEqualToString:@"AppleSymbolicHotKeys"]) {
|
||||
updateAppleSymbolicHotkeysCache();
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, int keyCode, NSString *chars) {
|
||||
struct SymbolicHotKey shortcut;
|
||||
@synchronized ([SystemHotkey class]) {
|
||||
if (!subscribedToShortcutUpdates) {
|
||||
[SystemHotkey setUp];
|
||||
}
|
||||
shortcut = currentSymbolicHotkeys[Shortcut_FocusNextApplicationWindow];
|
||||
}
|
||||
|
||||
int ignoredModifiers = NSAlphaShiftKeyMask | NSFunctionKeyMask | NSNumericPadKeyMask | NSHelpKeyMask;
|
||||
// Ignore Shift because of JBR-4899.
|
||||
if (!(shortcutMask & NSShiftKeyMask)) {
|
||||
if (!(shortcut.modifiers & NSShiftKeyMask)) {
|
||||
ignoredModifiers |= NSShiftKeyMask;
|
||||
}
|
||||
if ((modifiersMask & ~ignoredModifiers) == shortcutMask) {
|
||||
return shortcutKeyCode == keyCode || [chars isEqualToString:shortcutCharacter];
|
||||
|
||||
if ((modifiersMask & ~ignoredModifiers) != shortcut.modifiers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shortcut.key == keyCode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shortcut.character > 0 && shortcut.character < 0xFFFF) {
|
||||
unichar ch = shortcut.character;
|
||||
return [chars isEqualToString:[NSString stringWithCharacters:&ch length:1]];
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -584,13 +643,12 @@ JNIEXPORT void JNICALL Java_java_awt_desktop_SystemHotkeyReader_readSystemHotkey
|
||||
jmethodID methodAdd = (*env)->GetMethodID(env, clsReader, "add", "(ILjava/lang/String;ILjava/lang/String;)V");
|
||||
|
||||
readSystemHotkeysImpl(
|
||||
^bool(int vkeyCode, const char * keyCharStr, int jmodifiers, const char * descriptionStr, int hotkeyUid){
|
||||
^(int vkeyCode, const char * keyCharStr, int jmodifiers, const char * descriptionStr, int hotkeyUid){
|
||||
jstring jkeyChar = keyCharStr == NULL ? NULL : (*env)->NewStringUTF(env, keyCharStr);
|
||||
jstring jdesc = descriptionStr == NULL ? NULL : (*env)->NewStringUTF(env, descriptionStr);
|
||||
(*env)->CallVoidMethod(
|
||||
env, reader, methodAdd, (jint)vkeyCode, jkeyChar, (jint)jmodifiers, jdesc
|
||||
);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user