Skip to content

Commit 217a8cb

Browse files
8259680: Need API to query states of CAPS LOCK and NUM LOCK keys
Reviewed-by: arapte, pbansal
1 parent db6941d commit 217a8cb

File tree

15 files changed

+446
-1
lines changed

15 files changed

+446
-1
lines changed

modules/javafx.graphics/src/main/java/com/sun/glass/events/KeyEvent.java

+7
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ public class KeyEvent {
6868
@Native public final static int MODIFIER_BUTTON_BACK = 1 << 8;
6969
@Native public final static int MODIFIER_BUTTON_FORWARD = 1 << 9;
7070

71+
/*
72+
* Key lock state
73+
*/
74+
@Native public static final int KEY_LOCK_OFF = 0;
75+
@Native public static final int KEY_LOCK_ON = 1;
76+
@Native public static final int KEY_LOCK_UNKNOWN = -1;
77+
7178
/*
7279
* Key event key codes.
7380
*/

modules/javafx.graphics/src/main/java/com/sun/glass/ui/Application.java

+19
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.List;
3737
import java.util.Map;
3838
import java.util.LinkedList;
39+
import java.util.Optional;
3940

4041
public abstract class Application {
4142

@@ -744,4 +745,22 @@ public final boolean supportsSystemMenu() {
744745
public static int getKeyCodeForChar(char c) {
745746
return application._getKeyCodeForChar(c);
746747
}
748+
749+
protected int _isKeyLocked(int keyCode) {
750+
// Overridden in subclasses
751+
return KeyEvent.KEY_LOCK_UNKNOWN;
752+
}
753+
754+
public final Optional<Boolean> isKeyLocked(int keyCode) {
755+
checkEventThread();
756+
int lockState = _isKeyLocked(keyCode);
757+
switch (lockState) {
758+
case KeyEvent.KEY_LOCK_OFF:
759+
return Optional.of(false);
760+
case KeyEvent.KEY_LOCK_ON:
761+
return Optional.of(true);
762+
default:
763+
return Optional.empty();
764+
}
765+
}
747766
}

modules/javafx.graphics/src/main/java/com/sun/glass/ui/gtk/GtkApplication.java

+3
Original file line numberDiff line numberDiff line change
@@ -478,4 +478,7 @@ protected boolean _supportsInputMethods() {
478478
@Override
479479
protected native int _getKeyCodeForChar(char c);
480480

481+
@Override
482+
protected native int _isKeyLocked(int keyCode);
483+
481484
}

modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacApplication.java

+3
Original file line numberDiff line numberDiff line change
@@ -391,4 +391,7 @@ public String getDataDirectory() {
391391

392392
@Override
393393
protected native int _getKeyCodeForChar(char c);
394+
395+
@Override
396+
protected native int _isKeyLocked(int keyCode);
394397
}

modules/javafx.graphics/src/main/java/com/sun/glass/ui/win/WinApplication.java

+3
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,7 @@ public String getDataDirectory() {
358358

359359
@Override
360360
protected native int _getKeyCodeForChar(char c);
361+
362+
@Override
363+
protected native int _isKeyLocked(int keyCode);
361364
}

modules/javafx.graphics/src/main/java/com/sun/javafx/tk/DummyToolkit.java

+6
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import com.sun.scenario.animation.AbstractPrimaryTimer;
6868
import com.sun.scenario.effect.FilterContext;
6969
import com.sun.scenario.effect.Filterable;
70+
import java.util.Optional;
7071

7172
/**
7273
* A stubbed out Toolkit that provides no useful implementation. This is used
@@ -371,6 +372,11 @@ public KeyCode getPlatformShortcutKey() {
371372
throw new UnsupportedOperationException("Not supported yet.");
372373
}
373374

375+
@Override
376+
public Optional<Boolean> isKeyLocked(KeyCode keyCode) {
377+
throw new UnsupportedOperationException("Not supported yet.");
378+
}
379+
374380
@Override
375381
public FileChooserResult showFileChooser(TKStage ownerWindow,
376382
String title,

modules/javafx.graphics/src/main/java/com/sun/javafx/tk/Toolkit.java

+8
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
import com.sun.scenario.effect.Color4f;
9595
import com.sun.scenario.effect.FilterContext;
9696
import com.sun.scenario.effect.Filterable;
97+
import java.util.Optional;
9798

9899

99100
public abstract class Toolkit {
@@ -873,6 +874,13 @@ public KeyCode getPlatformShortcutKey() {
873874
return PlatformUtil.isMac() ? KeyCode.META : KeyCode.CONTROL;
874875
}
875876

877+
/**
878+
* Returns the lock state for the given keyCode.
879+
* @param keyCode the keyCode to check
880+
* @return the lock state for the given keyCode.
881+
*/
882+
public abstract Optional<Boolean> isKeyLocked(KeyCode keyCode);
883+
876884
public abstract FileChooserResult showFileChooser(
877885
TKStage ownerWindow,
878886
String title,

modules/javafx.graphics/src/main/java/com/sun/javafx/tk/quantum/QuantumToolkit.java

+19
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
import com.sun.javafx.logging.PulseLogger;
134134
import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED;
135135
import com.sun.javafx.scene.input.DragboardHelper;
136+
import java.util.Optional;
136137

137138
public final class QuantumToolkit extends Toolkit {
138139

@@ -1236,6 +1237,24 @@ public boolean isMSAASupported() {
12361237
return GraphicsPipeline.getPipeline().isMSAASupported();
12371238
}
12381239

1240+
// Returns the glass keycode for the given JavaFX KeyCode.
1241+
// This method only converts lock state KeyCode values
1242+
private int toGlassKeyCode(KeyCode keyCode) {
1243+
switch (keyCode) {
1244+
case CAPS:
1245+
return com.sun.glass.events.KeyEvent.VK_CAPS_LOCK;
1246+
case NUM_LOCK:
1247+
return com.sun.glass.events.KeyEvent.VK_NUM_LOCK;
1248+
default:
1249+
return com.sun.glass.events.KeyEvent.VK_UNDEFINED;
1250+
}
1251+
}
1252+
1253+
@Override
1254+
public Optional<Boolean> isKeyLocked(KeyCode keyCode) {
1255+
return Application.GetApplication().isKeyLocked(toGlassKeyCode(keyCode));
1256+
}
1257+
12391258
static TransferMode clipboardActionToTransferMode(final int action) {
12401259
switch (action) {
12411260
case Clipboard.ACTION_NONE:

modules/javafx.graphics/src/main/java/javafx/application/Platform.java

+40-1
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525

2626
package javafx.application;
2727

28+
import com.sun.javafx.application.PlatformImpl;
2829
import com.sun.javafx.tk.Toolkit;
30+
import java.util.Optional;
2931
import javafx.beans.property.ReadOnlyBooleanProperty;
3032
import javafx.beans.property.ReadOnlyBooleanWrapper;
31-
import com.sun.javafx.application.PlatformImpl;
33+
import javafx.scene.input.KeyCode;
3234

3335
/**
3436
* Application platform support class.
@@ -329,6 +331,43 @@ public static void exitNestedEventLoop(Object key, Object rval) {
329331
Toolkit.getToolkit().exitNestedEventLoop(key, rval);
330332
}
331333

334+
/**
335+
* Returns a flag indicating whether the key corresponding to {@code keyCode}
336+
* is in the locked (or "on") state.
337+
* {@code keyCode} must be one of: {@link KeyCode#CAPS} or
338+
* {@link KeyCode#NUM_LOCK}.
339+
* If the underlying system is not able to determine the state of the
340+
* specified {@code keyCode}, an empty {@code Optional} is returned.
341+
* If the keyboard attached to the system doesn't have the specified key,
342+
* an {@code Optional} containing {@code false} is returned.
343+
* This method must be called on the JavaFX Application thread.
344+
*
345+
* @param keyCode the {@code KeyCode} of the lock state to query
346+
*
347+
* @return the lock state of the key corresponding to {@code keyCode},
348+
* or an empty {@code Optional} if the system cannot determine its state
349+
*
350+
* @throws IllegalArgumentException if {@code keyCode} is not one of the
351+
* valid {@code KeyCode} values
352+
*
353+
* @throws IllegalStateException if this method is called on a thread
354+
* other than the JavaFX Application Thread
355+
*
356+
* @since 17
357+
*/
358+
public static Optional<Boolean> isKeyLocked(KeyCode keyCode) {
359+
Toolkit.getToolkit().checkFxUserThread();
360+
361+
switch (keyCode) {
362+
case CAPS:
363+
case NUM_LOCK:
364+
break;
365+
default:
366+
throw new IllegalArgumentException("Invalid KeyCode");
367+
}
368+
return Toolkit.getToolkit().isKeyLocked(keyCode);
369+
}
370+
332371
/**
333372
* Checks whether a nested event loop is running, returning true to indicate
334373
* that one is, and false if there are no nested event loops currently

modules/javafx.graphics/src/main/native-glass/gtk/glass_key.cpp

+60
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <glib.h>
3030
#include "glass_general.h"
3131
#include <gdk/gdkkeysyms.h>
32+
#include <X11/XKBlib.h>
3233

3334
static gboolean key_initialized = FALSE;
3435
static GHashTable *keymap;
@@ -345,4 +346,63 @@ JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1getKeyCodeForC
345346
return gdk_keyval_to_glass(keyval);
346347
}
347348

349+
/*
350+
* Function to determine whether the Xkb extention is available. This is a
351+
* precaution against X protocol errors, although it should be available on all
352+
* Linux systems.
353+
*/
354+
355+
static Bool xkbInitialized = False;
356+
static Bool xkbAvailable = False;
357+
358+
static Bool isXkbAvailable(Display *display) {
359+
if (!xkbInitialized) {
360+
int xkbMajor = XkbMajorVersion;
361+
int xkbMinor = XkbMinorVersion;
362+
xkbAvailable = XkbQueryExtension(display, NULL, NULL, NULL, &xkbMajor, &xkbMinor);
363+
xkbInitialized = True;
364+
}
365+
return xkbAvailable;
366+
}
367+
368+
/*
369+
* Class: com_sun_glass_ui_gtk_GtkApplication
370+
* Method: _isKeyLocked
371+
* Signature: (I)I
372+
*/
373+
JNIEXPORT jint JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1isKeyLocked
374+
(JNIEnv * env, jobject obj, jint keyCode)
375+
{
376+
Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default());
377+
if (!isXkbAvailable(display)) {
378+
return com_sun_glass_events_KeyEvent_KEY_LOCK_UNKNOWN;
379+
}
380+
381+
Atom keyCodeAtom = None;
382+
switch (keyCode) {
383+
case com_sun_glass_events_KeyEvent_VK_CAPS_LOCK:
384+
keyCodeAtom = XInternAtom(display, "Caps Lock", True);
385+
break;
386+
387+
case com_sun_glass_events_KeyEvent_VK_NUM_LOCK:
388+
keyCodeAtom = XInternAtom(display, "Num Lock", True);
389+
break;
390+
}
391+
392+
if (keyCodeAtom == None) {
393+
return com_sun_glass_events_KeyEvent_KEY_LOCK_UNKNOWN;
394+
}
395+
396+
Bool isLocked = False;
397+
if (XkbGetNamedIndicator(display, keyCodeAtom, NULL, &isLocked, NULL, NULL)) {
398+
if (isLocked) {
399+
return com_sun_glass_events_KeyEvent_KEY_LOCK_ON;
400+
} else {
401+
return com_sun_glass_events_KeyEvent_KEY_LOCK_OFF;
402+
}
403+
}
404+
405+
return com_sun_glass_events_KeyEvent_KEY_LOCK_UNKNOWN;
406+
}
407+
348408
} // extern "C"

modules/javafx.graphics/src/main/native-glass/mac/GlassKey.m

+22
Original file line numberDiff line numberDiff line change
@@ -396,3 +396,25 @@ BOOL GetMacKey(jint javaKeyCode, unsigned short *outMacKeyCode)
396396
return [GlassApplication getKeyCodeForChar:c];
397397
}
398398

399+
/*
400+
* Class: com_sun_glass_ui_mac_MacApplication
401+
* Method: _isKeyLocked
402+
* Signature: (I)I
403+
*/
404+
JNIEXPORT jint JNICALL Java_com_sun_glass_ui_mac_MacApplication__1isKeyLocked
405+
(JNIEnv * env, jobject obj, jint keyCode)
406+
{
407+
NSUInteger mask = 0;
408+
switch (keyCode) {
409+
case com_sun_glass_events_KeyEvent_VK_CAPS_LOCK:
410+
mask = NSEventModifierFlagCapsLock;
411+
break;
412+
413+
// Caps lock is the only locking key supported on macOS
414+
default:
415+
return com_sun_glass_events_KeyEvent_KEY_LOCK_UNKNOWN;
416+
}
417+
NSUInteger modifierFlags = [NSEvent modifierFlags];
418+
return (modifierFlags & mask) ? com_sun_glass_events_KeyEvent_KEY_LOCK_ON
419+
: com_sun_glass_events_KeyEvent_KEY_LOCK_OFF;
420+
}

modules/javafx.graphics/src/main/native-glass/win/KeyTable.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -247,5 +247,27 @@ JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinApplication__1getKeyCodeForC
247247
return WindowsKeyToJavaKey(vkey);
248248
}
249249

250+
/*
251+
* Class: com_sun_glass_ui_win_WinApplication
252+
* Method: _isKeyLocked
253+
* Signature: (I)I
254+
*/
255+
JNIEXPORT jint JNICALL Java_com_sun_glass_ui_win_WinApplication__1isKeyLocked
256+
(JNIEnv * env, jobject obj, jint keyCode)
257+
{
258+
SHORT keyState = 0;
259+
switch (keyCode) {
260+
case com_sun_glass_events_KeyEvent_VK_CAPS_LOCK:
261+
keyState = ::GetKeyState(VK_CAPITAL);
262+
break;
250263

264+
case com_sun_glass_events_KeyEvent_VK_NUM_LOCK:
265+
keyState = ::GetKeyState(VK_NUMLOCK);
266+
break;
251267

268+
default:
269+
return com_sun_glass_events_KeyEvent_KEY_LOCK_UNKNOWN;
270+
}
271+
return (keyState & 0x1) ? com_sun_glass_events_KeyEvent_KEY_LOCK_ON
272+
: com_sun_glass_events_KeyEvent_KEY_LOCK_OFF;
273+
}

modules/javafx.graphics/src/test/java/test/com/sun/javafx/pgstub/StubToolkit.java

+5
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,11 @@ public KeyCode getPlatformShortcutKey() {
750750
return platformShortcutKey;
751751
}
752752

753+
@Override
754+
public Optional<Boolean> isKeyLocked(KeyCode keyCode) {
755+
return Optional.empty();
756+
}
757+
753758
private DndDelegate dndDelegate;
754759
public void setDndDelegate(DndDelegate dndDelegate) {
755760
this.dndDelegate = dndDelegate;

0 commit comments

Comments
 (0)