mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-6588: JBR API for inspecting certain properties of KeyEvents
This commit is contained in:
@@ -59,6 +59,7 @@ import java.awt.peer.KeyboardFocusManagerPeer;
|
||||
import java.awt.peer.WindowPeer;
|
||||
import java.util.List;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
@@ -1043,7 +1044,7 @@ public class LWWindowPeer
|
||||
*/
|
||||
@Override
|
||||
public void notifyKeyEvent(int id, long when, int modifiers,
|
||||
int keyCode, char keyChar, int keyLocation)
|
||||
int keyCode, char keyChar, int keyLocation, Map<String, Object> properties)
|
||||
{
|
||||
LWKeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
|
||||
Component focusOwner = kfmPeer.getCurrentFocusOwner();
|
||||
@@ -1060,6 +1061,7 @@ public class LWWindowPeer
|
||||
AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent,
|
||||
(keyChar == KeyEvent.CHAR_UNDEFINED) ? keyCode
|
||||
: ExtendedKeyCodes.getExtendedKeyCodeForChar(keyChar));
|
||||
AWTAccessor.getKeyEventAccessor().setJBRExtraProperties(keyEvent, properties);
|
||||
postEvent(keyEvent);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package sun.lwawt;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Map;
|
||||
|
||||
public interface PlatformEventNotifier {
|
||||
void notifyIconify(boolean iconify);
|
||||
@@ -61,5 +62,9 @@ public interface PlatformEventNotifier {
|
||||
* Called by the delegate when a key is pressed.
|
||||
*/
|
||||
void notifyKeyEvent(int id, long when, int modifiers,
|
||||
int keyCode, char keyChar, int keyLocation);
|
||||
int keyCode, char keyChar, int keyLocation, Map<String, Object> properties);
|
||||
|
||||
default void notifyKeyEvent(int id, long when, int modifiers, int keyCode, char keyChar, int keyLocation) {
|
||||
notifyKeyEvent(id, when, modifiers, keyCode, keyChar, keyLocation, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.awt.event.MouseEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -196,6 +197,9 @@ final class CPlatformResponder {
|
||||
int jkeyCode = KeyEvent.VK_UNDEFINED;
|
||||
int jkeyLocation = KeyEvent.KEY_LOCATION_UNKNOWN;
|
||||
boolean spaceKeyTyped = false;
|
||||
HashMap<String, Object> properties = new HashMap<>();
|
||||
|
||||
properties.put("RAW_KEYCODE", keyCode);
|
||||
|
||||
char testChar = KeyEvent.CHAR_UNDEFINED;
|
||||
|
||||
@@ -208,6 +212,11 @@ final class CPlatformResponder {
|
||||
jkeyCode = out[0];
|
||||
jkeyLocation = out[1];
|
||||
jeventType = out[2];
|
||||
properties.put("US_KEYCODE", jkeyCode);
|
||||
properties.put("DEAD_KEYCODE", KeyEvent.VK_UNDEFINED);
|
||||
properties.put("DEAD_KEYSTROKE", KeyEvent.VK_UNDEFINED);
|
||||
properties.put("IS_TYPED", false);
|
||||
properties.put("CHARACTERS", "");
|
||||
} else {
|
||||
if (chars != null && chars.length() > 0) {
|
||||
// `chars` might contain more than one character, so why are we using the last one?
|
||||
@@ -224,15 +233,23 @@ final class CPlatformResponder {
|
||||
}
|
||||
}
|
||||
|
||||
int[] in = new int[] {keyCode, KeyEventProcessing.useNationalLayouts ? 1 : 0, KeyEventProcessing.reportDeadKeysAsNormal ? 1 : 0};
|
||||
int[] out = new int[2]; // [jkeyCode, jkeyLocation]
|
||||
|
||||
int[] in = new int[] {keyCode, modifierFlags, KeyEventProcessing.useNationalLayouts ? 1 : 0, KeyEventProcessing.reportDeadKeysAsNormal ? 1 : 0};
|
||||
int[] out = new int[5]; // [jkeyCode, jkeyLocation, usKeyCode, deadKeyCode, deadKeyComboCode]
|
||||
NSEvent.nsToJavaKeyInfo(in, out);
|
||||
|
||||
jkeyCode = out[0];
|
||||
jkeyLocation = out[1];
|
||||
jeventType = isNpapiCallback ? NSEvent.npToJavaEventType(eventType) :
|
||||
NSEvent.nsToJavaEventType(eventType);
|
||||
|
||||
properties.put("US_KEYCODE", out[2]);
|
||||
properties.put("DEAD_KEYCODE", out[3]);
|
||||
if (chars != null && chars.isEmpty()) {
|
||||
properties.put("DEAD_KEYSTROKE", out[4]);
|
||||
} else {
|
||||
properties.put("DEAD_KEYSTROKE", KeyEvent.VK_UNDEFINED);
|
||||
}
|
||||
properties.put("CHARACTERS", actualChars == null ? "" : actualChars);
|
||||
}
|
||||
|
||||
char javaChar = (testChar == KeyEvent.CHAR_UNDEFINED) ? KeyEvent.CHAR_UNDEFINED :
|
||||
@@ -245,7 +262,7 @@ final class CPlatformResponder {
|
||||
lastKeyPressCode = jkeyCode;
|
||||
}
|
||||
eventNotifier.notifyKeyEvent(jeventType, when, jmodifiers,
|
||||
jkeyCode, javaChar, jkeyLocation);
|
||||
jkeyCode, javaChar, jkeyLocation, properties);
|
||||
|
||||
// That's the reaction on the PRESSED (not RELEASED) event as it comes to
|
||||
// appear in MacOSX.
|
||||
@@ -258,7 +275,6 @@ final class CPlatformResponder {
|
||||
// Either macOS didn't send us anything in insertText: to type,
|
||||
// or this event was not generated in AWTView.m. Let's fall back to using javaChar
|
||||
// since we still need to generate KEY_TYPED events, for instance for Ctrl+ combinations.
|
||||
// javaChar is guaranteed to be a valid character, since postsTyped is true.
|
||||
actualChars = String.valueOf(javaChar);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
|
||||
public final class CWarningWindow extends CPlatformWindow
|
||||
implements SecurityWarningWindow, PlatformEventNotifier {
|
||||
@@ -245,7 +246,7 @@ public final class CWarningWindow extends CPlatformWindow
|
||||
|
||||
@Override
|
||||
public void notifyKeyEvent(int id, long when, int modifiers, int keyCode,
|
||||
char keyChar, int keyLocation) {
|
||||
char keyChar, int keyLocation, Map<String, Object> properties) {
|
||||
}
|
||||
|
||||
protected int getInitialStyleBits() {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package sun.lwawt.macosx;
|
||||
|
||||
import com.jetbrains.desktop.JBRKeyboard;
|
||||
import com.jetbrains.exported.JBRApi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class JBRKeyboardMacOS extends JBRKeyboard {
|
||||
@Override
|
||||
public String getCurrentKeyboardLayout() {
|
||||
// ensure LWCToolkit is initialized
|
||||
LWCToolkit.getLWCToolkit();
|
||||
|
||||
return LWCToolkit.getKeyboardLayoutId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getEnabledKeyboardLayouts() {
|
||||
// ensure LWCToolkit is initialized
|
||||
LWCToolkit.getLWCToolkit();
|
||||
|
||||
return LWCToolkit.getKeyboardLayoutList(false);
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,6 @@ struct KeyCodeTranslationResult {
|
||||
};
|
||||
|
||||
TISInputSourceRef GetCurrentUnderlyingLayout(BOOL useNationalLayouts);
|
||||
struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef layout, unsigned short keyCode);
|
||||
struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef layout, unsigned short keyCode, unsigned mods);
|
||||
|
||||
#endif /* __AWTEVENT_H */
|
||||
|
||||
@@ -505,7 +505,7 @@ TISInputSourceRef GetCurrentUnderlyingLayout(BOOL useNationalLayouts) {
|
||||
return underlyingLayout;
|
||||
}
|
||||
|
||||
struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef layout, unsigned short keyCode)
|
||||
struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef layout, unsigned short keyCode, unsigned mods)
|
||||
{
|
||||
struct KeyCodeTranslationResult result = {
|
||||
.character = (unichar)0,
|
||||
@@ -548,7 +548,6 @@ struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef la
|
||||
return result;
|
||||
}
|
||||
|
||||
UInt32 modifierKeyState = 0;
|
||||
UInt32 deadKeyState = 0;
|
||||
const UniCharCount maxStringLength = 255;
|
||||
UniCharCount actualStringLength = 0;
|
||||
@@ -556,7 +555,7 @@ struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef la
|
||||
|
||||
// get the deadKeyState
|
||||
OSStatus status = UCKeyTranslate(keyboardLayout,
|
||||
keyCode, kUCKeyActionDown, modifierKeyState,
|
||||
keyCode, kUCKeyActionDown, mods,
|
||||
LMGetKbdType(), 0,
|
||||
&deadKeyState,
|
||||
maxStringLength,
|
||||
@@ -589,7 +588,7 @@ struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef la
|
||||
|
||||
// Extract the dead key non-combining character
|
||||
status = UCKeyTranslate(keyboardLayout,
|
||||
keyCode, kUCKeyActionDown, modifierKeyState,
|
||||
keyCode, kUCKeyActionDown, mods,
|
||||
LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask,
|
||||
&deadKeyState,
|
||||
maxStringLength,
|
||||
@@ -609,14 +608,50 @@ struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef la
|
||||
return result;
|
||||
}
|
||||
|
||||
static jint CharacterToDeadKeyCode(unichar ch) {
|
||||
for (const struct CharToVKEntry *map = charToDeadVKTable; map->c != 0; ++map) {
|
||||
if (ch == map->c) {
|
||||
return map->javaKey;
|
||||
}
|
||||
}
|
||||
|
||||
// No builtin VK_DEAD_ constant for this dead key,
|
||||
// nothing better to do than to fall back to the extended key code.
|
||||
// This can happen on the following ascii-capable key layouts on the base layer:
|
||||
// - Apache (com.apple.keylayout.Apache)
|
||||
// - Chickasaw (com.apple.keylayout.Chickasaw)
|
||||
// - Choctaw (com.apple.keylayout.Choctaw)
|
||||
// - Navajo (com.apple.keylayout.Navajo)
|
||||
// - Vietnamese (com.apple.keylayout.Vietnamese)
|
||||
// Vietnamese layout is unique among these in that the "dead key" is actually a self-containing symbol,
|
||||
// that can be modified by an accent typed after it. In essence, it's like a dead key in reverse:
|
||||
// the user should first type the letter and only then the necessary accent.
|
||||
// This way the key code would be what the user expects.
|
||||
|
||||
return 0x1000000 + ch;
|
||||
}
|
||||
|
||||
struct JavaKeyTranslationResult {
|
||||
jint keyCode;
|
||||
jint keyLocation;
|
||||
|
||||
jint usKeyCode; // this is always the key position on the QWERTY keyboard
|
||||
|
||||
// if the base key is dead, then it's the corresponding dead key code, otherwise it's VK_UNDEFINED
|
||||
jint deadKeyCode;
|
||||
|
||||
// if the key combo is dead, then it's the corresponding dead key code, otherwise it's VK_UNDEFINED
|
||||
jint deadKeyComboCode;
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the function that uses the table above to take incoming
|
||||
* NSEvent keyCodes and translate to the Java virtual key code.
|
||||
*/
|
||||
static void
|
||||
NsCharToJavaVirtualKeyCode(unsigned short key, const BOOL useNationalLayouts,
|
||||
NsCharToJavaVirtualKeyCode(unsigned short key, unsigned mods, const BOOL useNationalLayouts,
|
||||
const BOOL reportDeadKeysAsNormal,
|
||||
jint *keyCode, jint *keyLocation)
|
||||
struct JavaKeyTranslationResult* result)
|
||||
{
|
||||
// This is going to be a lengthy explanation about what it is that we need to achieve in this function.
|
||||
// It took me quite a while to figure out myself, so hopefully it will be useful to others as well.
|
||||
@@ -699,47 +734,45 @@ NsCharToJavaVirtualKeyCode(unsigned short key, const BOOL useNationalLayouts,
|
||||
// Need to take into account that the same virtual key code may correspond to
|
||||
// different keys depending on the physical layout.
|
||||
|
||||
// memset the result to zero
|
||||
*result = (struct JavaKeyTranslationResult){0};
|
||||
|
||||
const struct KeyTableEntry* usKey = GetKeyTableEntryForKeyCode(key);
|
||||
|
||||
// Determine the underlying layout.
|
||||
// If underlyingLayout is nil then fall back to using the usKey.
|
||||
|
||||
TISInputSourceRef underlyingLayout = GetCurrentUnderlyingLayout(useNationalLayouts);
|
||||
TISInputSourceRef activeLayout = useNationalLayouts ? GetCurrentUnderlyingLayout(NO) : underlyingLayout;
|
||||
|
||||
// Default to returning the US key data.
|
||||
*keyCode = usKey->javaKeyCode;
|
||||
*keyLocation = usKey->javaKeyLocation;
|
||||
result->keyCode = result->usKeyCode = usKey->javaKeyCode;
|
||||
result->keyLocation = usKey->javaKeyLocation;
|
||||
result->deadKeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
|
||||
result->deadKeyComboCode = java_awt_event_KeyEvent_VK_UNDEFINED;
|
||||
|
||||
if (underlyingLayout == nil || !usKey->variesBetweenLayouts) {
|
||||
if (underlyingLayout == nil || activeLayout == nil || !usKey->variesBetweenLayouts) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Translate the key + modifiers using the current key layout
|
||||
struct KeyCodeTranslationResult translatedKeyCombo = TranslateKeyCodeUsingLayout(activeLayout, key, (mods >> 16) & 0xFF);
|
||||
|
||||
// If the key + modifiers produces a dead key, report it
|
||||
if (translatedKeyCombo.isDead) {
|
||||
result->deadKeyComboCode = CharacterToDeadKeyCode(translatedKeyCombo.character);
|
||||
}
|
||||
|
||||
// Translate the key using the underlying key layout.
|
||||
struct KeyCodeTranslationResult translatedKey = TranslateKeyCodeUsingLayout(underlyingLayout, key);
|
||||
struct KeyCodeTranslationResult translatedKey = TranslateKeyCodeUsingLayout(underlyingLayout, key, 0);
|
||||
|
||||
// Test whether this key is dead.
|
||||
if (translatedKey.isDead && !reportDeadKeysAsNormal) {
|
||||
for (const struct CharToVKEntry *map = charToDeadVKTable; map->c != 0; ++map) {
|
||||
if (translatedKey.character == map->c) {
|
||||
*keyCode = map->javaKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (translatedKey.isDead) {
|
||||
result->deadKeyCode = CharacterToDeadKeyCode(translatedKey.character);
|
||||
}
|
||||
|
||||
// No builtin VK_DEAD_ constant for this dead key,
|
||||
// nothing better to do than to fall back to the extended key code.
|
||||
// This can happen on the following ascii-capable key layouts:
|
||||
// - Apache (com.apple.keylayout.Apache)
|
||||
// - Chickasaw (com.apple.keylayout.Chickasaw)
|
||||
// - Choctaw (com.apple.keylayout.Choctaw)
|
||||
// - Navajo (com.apple.keylayout.Navajo)
|
||||
// - Vietnamese (com.apple.keylayout.Vietnamese)
|
||||
// Vietnamese layout is unique among these in that the "dead key" is actually a self-containing symbol,
|
||||
// that can be modified by an accent typed after it. In essence, it's like a dead key in reverse:
|
||||
// the user should first type the letter and only then the necessary accent.
|
||||
// This way the key code would be what the user expects.
|
||||
|
||||
*keyCode = 0x1000000 + translatedKey.character;
|
||||
if (!reportDeadKeysAsNormal && result->deadKeyCode) {
|
||||
result->keyCode = result->deadKeyCode;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -755,21 +788,21 @@ NsCharToJavaVirtualKeyCode(unsigned short key, const BOOL useNationalLayouts,
|
||||
|
||||
for (const struct CharToVKEntry *map = extraCharToVKTable; map->c != 0; ++map) {
|
||||
if (map->c == ch) {
|
||||
*keyCode = map->javaKey;
|
||||
result->keyCode = map->javaKey;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
// key is a basic latin letter
|
||||
*keyCode = java_awt_event_KeyEvent_VK_A + ch - 'a';
|
||||
result->keyCode = java_awt_event_KeyEvent_VK_A + ch - 'a';
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
// key is a digit
|
||||
// numpad digits are already handled, since they don't vary between layouts
|
||||
*keyCode = java_awt_event_KeyEvent_VK_0 + ch - '0';
|
||||
result->keyCode = java_awt_event_KeyEvent_VK_0 + ch - '0';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -782,7 +815,7 @@ NsCharToJavaVirtualKeyCode(unsigned short key, const BOOL useNationalLayouts,
|
||||
// Apart from letters, this is the case for characters like the Section Sign (U+00A7)
|
||||
// on the French keyboard (key ANSI_6) or Pound Sign (U+00A3) on the Italian – QZERTY keyboard (key ANSI_8).
|
||||
|
||||
*keyCode = 0x01000000 + ch;
|
||||
result->keyCode = 0x01000000 + ch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -965,20 +998,24 @@ JNI_COCOA_ENTER(env);
|
||||
|
||||
// in = [keyCode, useNationalLayouts]
|
||||
jshort keyCode = (jshort)data[0];
|
||||
BOOL useNationalLayouts = (data[1] != 0);
|
||||
BOOL reportDeadKeysAsNormal = (data[2] != 0);
|
||||
unsigned mods = ((unsigned*)data)[1];
|
||||
BOOL useNationalLayouts = (data[2] != 0);
|
||||
BOOL reportDeadKeysAsNormal = (data[3] != 0);
|
||||
|
||||
jint jkeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
|
||||
jint jkeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
|
||||
struct JavaKeyTranslationResult result = {0};
|
||||
|
||||
NsCharToJavaVirtualKeyCode((unsigned short)keyCode,
|
||||
mods,
|
||||
useNationalLayouts,
|
||||
reportDeadKeysAsNormal,
|
||||
&jkeyCode, &jkeyLocation);
|
||||
&result);
|
||||
|
||||
// out = [jkeyCode, jkeyLocation];
|
||||
(*env)->SetIntArrayRegion(env, outData, 0, 1, &jkeyCode);
|
||||
(*env)->SetIntArrayRegion(env, outData, 1, 1, &jkeyLocation);
|
||||
// out = [jkeyCode, jkeyLocation, usKeyCode, deadKeyCode, deadKeyComboCode];
|
||||
(*env)->SetIntArrayRegion(env, outData, 0, 1, &result.keyCode);
|
||||
(*env)->SetIntArrayRegion(env, outData, 1, 1, &result.keyLocation);
|
||||
(*env)->SetIntArrayRegion(env, outData, 2, 1, &result.usKeyCode);
|
||||
(*env)->SetIntArrayRegion(env, outData, 3, 1, &result.deadKeyCode);
|
||||
(*env)->SetIntArrayRegion(env, outData, 4, 1, &result.deadKeyComboCode);
|
||||
|
||||
(*env)->ReleaseIntArrayElements(env, inData, data, 0);
|
||||
|
||||
|
||||
@@ -505,7 +505,7 @@ static void debugPrintNSEvent(NSEvent* event, const char* comment) {
|
||||
// for layouts that have the backtick as a dead key. Unfortunately, some (but notably not all) of these layouts
|
||||
// consider Cmd+Dead Grave to also be a dead key, which means that event.characters will be an empty string.
|
||||
// Explicitly translating the key code with a proper underlying key layout fixes this.
|
||||
struct KeyCodeTranslationResult translationResult = TranslateKeyCodeUsingLayout(GetCurrentUnderlyingLayout(YES), [event keyCode]);
|
||||
struct KeyCodeTranslationResult translationResult = TranslateKeyCodeUsingLayout(GetCurrentUnderlyingLayout(YES), [event keyCode], 0);
|
||||
if (translationResult.isSuccess && translationResult.character) {
|
||||
return isSystemShortcut_NextWindowInApplication(deviceIndependentModifierFlagsMask, [event keyCode], [NSString stringWithCharacters:&translationResult.character length:1]) ? YES : NO;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.jetbrains.desktop;
|
||||
|
||||
import com.jetbrains.exported.JBRApi;
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.event.KeyEventProcessing;
|
||||
import sun.lwawt.macosx.LWCToolkit;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implementation for platform-agnostic parts of JBR keyboard API
|
||||
*/
|
||||
@JBRApi.Service
|
||||
@JBRApi.Provides("Keyboard")
|
||||
public class JBRKeyboard {
|
||||
/** (Integer) Java key code as if the keyboard was a US QWERTY one */
|
||||
public static final String US_KEYCODE = "US_KEYCODE";
|
||||
|
||||
/** (Integer) VK_DEAD_* keycode for the base key, or VK_UNDEFINED if the base key is not dead */
|
||||
public static final String DEAD_KEYCODE = "DEAD_KEYCODE";
|
||||
|
||||
/** (Integer) VK_DEAD_* keycode for the key with modifiers, or VK_UNDEFINED if the key with modifiers is not dead */
|
||||
public static final String DEAD_KEYSTROKE = "DEAD_KEYSTROKE";
|
||||
|
||||
/** (String) The characters that this specific event has generated as a sequence of KEY_TYPED events */
|
||||
public static final String CHARACTERS = "CHARACTERS";
|
||||
|
||||
/** (Integer) Raw platform-dependent keycode for the specific key on the keyboard */
|
||||
public static final String RAW_KEYCODE = "RAW_KEYCODE";
|
||||
|
||||
private static JBRKeyboard create() {
|
||||
try {
|
||||
var implMacOS = Class.forName("sun.lwawt.macosx.JBRKeyboardMacOS");
|
||||
var instance = implMacOS.getDeclaredConstructor().newInstance();
|
||||
return (JBRKeyboard)instance;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return new JBRKeyboard();
|
||||
} catch (InvocationTargetException | IllegalAccessException | InstantiationException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Object getKeyEventProperty(KeyEvent event, String name) {
|
||||
Map<String, Object> properties = AWTAccessor.getKeyEventAccessor().getJBRExtraProperties(event);
|
||||
return (properties == null) ? null : properties.get(name);
|
||||
}
|
||||
|
||||
public int getKeyEventUSKeyCode(KeyEvent event) {
|
||||
if (event.getID() == KeyEvent.KEY_PRESSED || event.getID() == KeyEvent.KEY_RELEASED) {
|
||||
var prop = getKeyEventProperty(event, US_KEYCODE);
|
||||
if (!(prop instanceof Integer)) {
|
||||
throw new UnsupportedOperationException("US_KEYCODE property is invalid");
|
||||
}
|
||||
return (Integer)prop;
|
||||
} else {
|
||||
throw new IllegalArgumentException("event is not KEY_PRESSED or KEY_RELEASED");
|
||||
}
|
||||
}
|
||||
|
||||
public int getKeyEventDeadKeyCode(KeyEvent event) {
|
||||
if (event.getID() == KeyEvent.KEY_PRESSED || event.getID() == KeyEvent.KEY_RELEASED) {
|
||||
var prop = getKeyEventProperty(event, DEAD_KEYCODE);
|
||||
if (!(prop instanceof Integer)) {
|
||||
throw new UnsupportedOperationException("DEAD_KEYCODE property is invalid");
|
||||
}
|
||||
return (Integer)prop;
|
||||
} else {
|
||||
throw new IllegalArgumentException("event is not KEY_PRESSED or KEY_RELEASED");
|
||||
}
|
||||
}
|
||||
|
||||
public int getKeyEventDeadKeyStroke(KeyEvent event) {
|
||||
if (event.getID() == KeyEvent.KEY_PRESSED || event.getID() == KeyEvent.KEY_RELEASED) {
|
||||
var prop = getKeyEventProperty(event, DEAD_KEYSTROKE);
|
||||
if (!(prop instanceof Integer)) {
|
||||
throw new UnsupportedOperationException("DEAD_KEYSTROKE property is invalid");
|
||||
}
|
||||
return (Integer)prop;
|
||||
} else {
|
||||
throw new IllegalArgumentException("event is not KEY_PRESSED or KEY_RELEASED");
|
||||
}
|
||||
}
|
||||
|
||||
public String getKeyEventCharacters(KeyEvent event) {
|
||||
if (event.getID() == KeyEvent.KEY_RELEASED) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (event.getID() == KeyEvent.KEY_TYPED) {
|
||||
return String.valueOf(event.getKeyChar());
|
||||
}
|
||||
|
||||
if (event.getID() == KeyEvent.KEY_PRESSED) {
|
||||
var prop = getKeyEventProperty(event, CHARACTERS);
|
||||
if (!(prop instanceof String)) {
|
||||
throw new UnsupportedOperationException("CHARACTERS property is invalid");
|
||||
}
|
||||
return (String)prop;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("event with unknown ID");
|
||||
}
|
||||
|
||||
public String getCurrentKeyboardLayout() {
|
||||
throw new UnsupportedOperationException("getCurrentKeyboardLayout() not implemented for this platform");
|
||||
}
|
||||
|
||||
public List<String> getEnabledKeyboardLayouts() {
|
||||
throw new UnsupportedOperationException("getEnabledKeyboardLayouts() not implemented for this platform");
|
||||
}
|
||||
|
||||
public void setReportNationalKeyCodes(boolean value) {
|
||||
EventQueue.invokeLater(() -> {
|
||||
KeyEventProcessing.useNationalLayouts = value;
|
||||
});
|
||||
}
|
||||
|
||||
void setConvertDeadKeyCodesToNormal(boolean value) {
|
||||
EventQueue.invokeLater(() -> {
|
||||
KeyEventProcessing.reportDeadKeysAsNormal = value;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ import java.awt.Toolkit;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
|
||||
@@ -1182,6 +1184,9 @@ public non-sealed class KeyEvent extends InputEvent {
|
||||
private transient long scancode = 0; // for MS Windows only
|
||||
private transient long extendedKeyCode = 0;
|
||||
|
||||
// JBR-specific additional options
|
||||
private transient Map<String, Object> jbrExtraProperties;
|
||||
|
||||
/**
|
||||
* Use serialVersionUID from JDK 1.1 for interoperability.
|
||||
*/
|
||||
@@ -1219,6 +1224,16 @@ public non-sealed class KeyEvent extends InputEvent {
|
||||
public boolean isProxyActive(KeyEvent ev) {
|
||||
return ev.isProxyActive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getJBRExtraProperties(KeyEvent ev) {
|
||||
return ev.jbrExtraProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setJBRExtraProperties(KeyEvent ev, Map<String, Object> properties) {
|
||||
ev.jbrExtraProperties = properties;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.AccessControlContext;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Vector;
|
||||
import java.util.function.BooleanSupplier;
|
||||
@@ -745,6 +746,16 @@ public final class AWTAccessor {
|
||||
* Gets isProxyActive field for KeyEvent
|
||||
*/
|
||||
boolean isProxyActive(KeyEvent ev);
|
||||
|
||||
/**
|
||||
* Returns the JBR-specific property map
|
||||
*/
|
||||
Map<String, Object> getJBRExtraProperties(KeyEvent ev);
|
||||
|
||||
/**
|
||||
* Overwrites the JBR-specific property map
|
||||
*/
|
||||
void setJBRExtraProperties(KeyEvent ev, Map<String, Object> properties);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,14 +10,14 @@ public class KeyEventProcessing {
|
||||
// "com.jetbrains.use.old.keyevent.processing"
|
||||
public final static String useNationalLayoutsOption = "com.sun.awt.use.national.layouts";
|
||||
@Native
|
||||
public final static boolean useNationalLayouts = "true".equals(
|
||||
public static volatile boolean useNationalLayouts = "true".equals(
|
||||
System.getProperty(useNationalLayoutsOption,
|
||||
FontUtilities.isMacOSX ? "true" : "false"));
|
||||
|
||||
// Report dead keys (such as VK_DEAD_GRAVE) as normal keys (such as VK_BACK_QUOTE)
|
||||
public final static String reportDeadKeysAsNormalOption = "com.sun.awt.reportDeadKeysAsNormal";
|
||||
@Native
|
||||
public final static boolean reportDeadKeysAsNormal = "true".equals(
|
||||
public static boolean reportDeadKeysAsNormal = "true".equals(
|
||||
System.getProperty(reportDeadKeysAsNormalOption,
|
||||
FontUtilities.isMacOSX ? "true" : "false"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user