mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-5233 Setup/teardown necessary keyboard layouts in macOS keyboard tests
This commit is contained in:
@@ -137,21 +137,59 @@ public final class LWCToolkit extends LWToolkit {
|
||||
private static native void initAppkit(ThreadGroup appKitThreadGroup, boolean headless);
|
||||
private static CInputMethodDescriptor sInputMethodDescriptor;
|
||||
|
||||
private static native void switchKeyboardLayoutNative(String layoutName);
|
||||
private static native boolean switchKeyboardLayoutNative(String layoutName);
|
||||
|
||||
static private native String getKeyboardLayoutNativeId();
|
||||
private static native String getKeyboardLayoutNativeId();
|
||||
|
||||
private static native String[] getKeyboardLayoutListNative(boolean includeAll);
|
||||
|
||||
private static native boolean enableKeyboardLayoutNative(String layoutName);
|
||||
|
||||
private static native boolean disableKeyboardLayoutNative(String layoutName);
|
||||
|
||||
public static void switchKeyboardLayout (String layoutName) {
|
||||
if (layoutName == null || layoutName.isEmpty()) {
|
||||
throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
|
||||
}
|
||||
switchKeyboardLayoutNative(layoutName);
|
||||
if (!switchKeyboardLayoutNative(layoutName)) {
|
||||
throw new RuntimeException("Couldn't switch layout to " + layoutName);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getKeyboardLayoutId () {
|
||||
return getKeyboardLayoutNativeId();
|
||||
}
|
||||
|
||||
public static List<String> getKeyboardLayoutList(boolean includeAll) {
|
||||
String[] result = getKeyboardLayoutListNative(includeAll);
|
||||
if (result == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return Arrays.asList(result);
|
||||
}
|
||||
|
||||
public static void enableKeyboardLayout(String layoutName) {
|
||||
if (layoutName == null || layoutName.isEmpty()) {
|
||||
throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
|
||||
}
|
||||
if (!enableKeyboardLayoutNative(layoutName)) {
|
||||
throw new RuntimeException("Couldn't enable layout " + layoutName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void disableKeyboardLayout(String layoutName) {
|
||||
if (layoutName == null || layoutName.isEmpty()) {
|
||||
throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
|
||||
}
|
||||
if (!disableKeyboardLayoutNative(layoutName)) {
|
||||
throw new RuntimeException("Couldn't disable layout " + layoutName);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isKeyboardLayoutEnabled(String layoutName) {
|
||||
return getKeyboardLayoutList(false).contains(layoutName);
|
||||
}
|
||||
|
||||
// Listens to EDT state in invokeAndWait() and disposes the invocation event
|
||||
// when EDT becomes free but the invocation event is not yet dispatched (considered lost).
|
||||
// This prevents a deadlock and makes the invocation return some default result.
|
||||
|
||||
@@ -934,8 +934,7 @@ Java_sun_lwawt_macosx_LWCToolkit_getMultiClickTime(JNIEnv *env, jclass klass) {
|
||||
* Signature: ()Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL
|
||||
JNICALL Java_sun_lwawt_macosx_LWCToolkit_getKeyboardLayoutNativeId(JNIEnv *env, jclass cls)
|
||||
{
|
||||
JNICALL Java_sun_lwawt_macosx_LWCToolkit_getKeyboardLayoutNativeId(JNIEnv *env, jclass cls) {
|
||||
__block NSString * layoutId = NULL;
|
||||
JNI_COCOA_ENTER(env);
|
||||
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
|
||||
@@ -951,19 +950,88 @@ JNICALL Java_sun_lwawt_macosx_LWCToolkit_getKeyboardLayoutNativeId(JNIEnv *env,
|
||||
* Method: switchKeyboardLayoutNative
|
||||
* Signature: (Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
JNICALL Java_sun_lwawt_macosx_LWCToolkit_switchKeyboardLayoutNative(JNIEnv *env, jclass cls, jstring jLayoutId)
|
||||
{
|
||||
JNIEXPORT jboolean JNICALL
|
||||
JNICALL Java_sun_lwawt_macosx_LWCToolkit_switchKeyboardLayoutNative(JNIEnv *env, jclass cls, jstring jLayoutId) {
|
||||
__block OSStatus status = noErr;
|
||||
JNI_COCOA_ENTER(env);
|
||||
__block NSString* layoutId = [JavaStringToNSString(env, jLayoutId) retain];
|
||||
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
|
||||
__block NSString* layoutId = JavaStringToNSString(env, jLayoutId);
|
||||
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
|
||||
NSArray* sources = CFBridgingRelease(TISCreateInputSourceList((__bridge CFDictionaryRef)@{ (__bridge NSString*)kTISPropertyInputSourceID : layoutId }, FALSE));
|
||||
TISInputSourceRef source = (__bridge TISInputSourceRef)sources[0];
|
||||
OSStatus status = TISSelectInputSource(source);
|
||||
status = TISSelectInputSource(source);
|
||||
if (status != noErr) {
|
||||
NSLog(@"error during keyboard layout switch");
|
||||
NSLog(@"failed to switch to keyboard layout %@, error code %d", layoutId, status);
|
||||
}
|
||||
[layoutId release];
|
||||
}];
|
||||
JNI_COCOA_EXIT(env);
|
||||
return status == noErr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_LWCToolkit
|
||||
* Method: getKeyboardLayoutListNative
|
||||
* Signature: (Z)[Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jarray JNICALL
|
||||
JNICALL Java_sun_lwawt_macosx_LWCToolkit_getKeyboardLayoutListNative(JNIEnv *env, jclass cls, jboolean includeAll) {
|
||||
__block jarray out = NULL;
|
||||
jclass stringClazz = (*env)->FindClass(env, "java/lang/String");
|
||||
JNI_COCOA_ENTER(env);
|
||||
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
|
||||
NSArray* sources = CFBridgingRelease(TISCreateInputSourceList(nil, includeAll));
|
||||
int numOfSources = (int)[sources count];
|
||||
out = (*env)->NewObjectArray(env, numOfSources, stringClazz, NULL);
|
||||
for (int i = 0; i < numOfSources; ++i) {
|
||||
id layout = [sources objectAtIndex:i];
|
||||
NSString* layoutId = TISGetInputSourceProperty((TISInputSourceRef)layout, kTISPropertyInputSourceID);
|
||||
jstring layoutIdJava = NSStringToJavaString(env, layoutId);
|
||||
(*env)->SetObjectArrayElement(env, out, i, layoutIdJava);
|
||||
}
|
||||
}];
|
||||
JNI_COCOA_EXIT(env);
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_LWCToolkit
|
||||
* Method: enableKeyboardLayoutNative
|
||||
* Signature: (Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
JNICALL Java_sun_lwawt_macosx_LWCToolkit_enableKeyboardLayoutNative(JNIEnv *env, jclass cls, jstring jLayoutId) {
|
||||
__block OSStatus status = noErr;
|
||||
JNI_COCOA_ENTER(env);
|
||||
__block NSString* layoutId = JavaStringToNSString(env, jLayoutId);
|
||||
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
|
||||
NSArray* sources = CFBridgingRelease(TISCreateInputSourceList((__bridge CFDictionaryRef)@{ (__bridge NSString*)kTISPropertyInputSourceID : layoutId }, YES));
|
||||
TISInputSourceRef source = (__bridge TISInputSourceRef)sources[0];
|
||||
status = TISEnableInputSource(source);
|
||||
if (status != noErr) {
|
||||
NSLog(@"failed to enable keyboard layout %@, error code %d", layoutId, status);
|
||||
}
|
||||
}];
|
||||
JNI_COCOA_EXIT(env);
|
||||
return status == noErr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_lwawt_macosx_LWCToolkit
|
||||
* Method: disableKeyboardLayoutNative
|
||||
* Signature: (Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
JNICALL Java_sun_lwawt_macosx_LWCToolkit_disableKeyboardLayoutNative(JNIEnv *env, jclass cls, jstring jLayoutId) {
|
||||
__block OSStatus status = noErr;
|
||||
JNI_COCOA_ENTER(env);
|
||||
__block NSString* layoutId = JavaStringToNSString(env, jLayoutId);
|
||||
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
|
||||
NSArray* sources = CFBridgingRelease(TISCreateInputSourceList((__bridge CFDictionaryRef)@{ (__bridge NSString*)kTISPropertyInputSourceID : layoutId }, YES));
|
||||
TISInputSourceRef source = (__bridge TISInputSourceRef)sources[0];
|
||||
status = TISDisableInputSource(source);
|
||||
if (status != noErr) {
|
||||
NSLog(@"failed to disable keyboard layout %@, error code %d", layoutId, status);
|
||||
}
|
||||
}];
|
||||
JNI_COCOA_EXIT(env);
|
||||
return status == noErr;
|
||||
}
|
||||
@@ -29,7 +29,9 @@ import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class InputMethodTest {
|
||||
private static JFrame frame;
|
||||
@@ -38,6 +40,7 @@ public class InputMethodTest {
|
||||
private static String currentTest = "";
|
||||
private static String currentSection = "";
|
||||
private static String initialLayout;
|
||||
private static final Set<String> addedLayouts = new HashSet<>();
|
||||
private static boolean success = true;
|
||||
private static int lastKeyCode = -1;
|
||||
|
||||
@@ -62,10 +65,18 @@ public class InputMethodTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
init();
|
||||
for (String arg : args) {
|
||||
runTest(arg);
|
||||
try {
|
||||
for (String arg : args) {
|
||||
runTest(arg);
|
||||
}
|
||||
} finally {
|
||||
LWCToolkit.switchKeyboardLayout(initialLayout);
|
||||
for (String layoutId : addedLayouts) {
|
||||
try {
|
||||
LWCToolkit.disableKeyboardLayout(layoutId);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
LWCToolkit.switchKeyboardLayout(initialLayout);
|
||||
System.exit(success ? 0 : 1);
|
||||
}
|
||||
|
||||
@@ -110,7 +121,12 @@ public class InputMethodTest {
|
||||
|
||||
private static void runTest(String name) {
|
||||
currentTest = name;
|
||||
TestCases.valueOf(name).run();
|
||||
try {
|
||||
TestCases.valueOf(name).run();
|
||||
} catch (Exception e) {
|
||||
System.out.printf("Test %s (%s) failed: %s\n", currentTest, currentSection, e);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void section(String description) {
|
||||
@@ -120,6 +136,20 @@ public class InputMethodTest {
|
||||
}
|
||||
|
||||
public static void layout(String name) {
|
||||
List<String> layouts = new ArrayList<>();
|
||||
if (name.matches("com\\.apple\\.inputmethod\\.(SCIM|TCIM|TYIM)\\.\\w+")) {
|
||||
layouts.add(name.replaceFirst("\\.\\w+$", ""));
|
||||
}
|
||||
|
||||
layouts.add(name);
|
||||
|
||||
for (String layout : layouts) {
|
||||
if (!LWCToolkit.isKeyboardLayoutEnabled(layout)) {
|
||||
LWCToolkit.enableKeyboardLayout(layout);
|
||||
addedLayouts.add(layout);
|
||||
}
|
||||
}
|
||||
|
||||
LWCToolkit.switchKeyboardLayout(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,7 @@ import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.text.CharacterIterator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -124,6 +122,8 @@ public class NationalLayoutTest {
|
||||
|
||||
private static Robot robot;
|
||||
|
||||
private static final Set<String> addedLayouts = new HashSet<>();
|
||||
|
||||
// Synchronized lists for storing results of different key events
|
||||
private static CopyOnWriteArrayList<Integer> keysPressed = new CopyOnWriteArrayList();
|
||||
private static CopyOnWriteArrayList<Character> charsTyped = new CopyOnWriteArrayList();
|
||||
@@ -202,10 +202,17 @@ public class NationalLayoutTest {
|
||||
}
|
||||
|
||||
} finally {
|
||||
for (String layoutId : addedLayouts) {
|
||||
try {
|
||||
LWCToolkit.disableKeyboardLayout(layoutId);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
// Restore initial keyboard layout
|
||||
if(initialLayoutName != null && !initialLayoutName.isEmpty()) {
|
||||
LWCToolkit.switchKeyboardLayout(initialLayoutName);
|
||||
}
|
||||
|
||||
// Destroy test GUI
|
||||
destroyGUI();
|
||||
// Wait for EDT auto-shutdown
|
||||
@@ -365,7 +372,12 @@ public class NationalLayoutTest {
|
||||
|
||||
// Switch current keyboard layout to the test one
|
||||
if(jbr) {
|
||||
LWCToolkit.switchKeyboardLayout(layout.toString());
|
||||
String layoutId = layout.toString();
|
||||
if (!LWCToolkit.isKeyboardLayoutEnabled(layoutId)) {
|
||||
LWCToolkit.enableKeyboardLayout(layoutId);
|
||||
addedLayouts.add(layoutId);
|
||||
}
|
||||
LWCToolkit.switchKeyboardLayout(layoutId);
|
||||
}
|
||||
|
||||
// Support for manual mode: wait while user switches the keyboard layout (if needed)
|
||||
|
||||
Reference in New Issue
Block a user