JBR-6588: JBR API for inspecting certain properties of KeyEvents

This commit is contained in:
Nikita Tsarev
2024-06-05 17:07:30 +02:00
parent 0d5fd3a262
commit c038d78960
12 changed files with 343 additions and 55 deletions

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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);
}
}

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
});
}
}

View File

@@ -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;
}
});
}

View File

@@ -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);
}
/**

View File

@@ -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"));