mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-4394, IDEA-246833: refactoring and blocking the fix of JBR-1573 to recreate IMs and ICs during preediting.
(cherry picked from commits 7a36587db8d3d5a63c5691243cd5bf56733c06e6, 321a4a3f83)
This commit is contained in:
committed by
jbrbot
parent
a7347a95d5
commit
13aefa8a4e
@@ -156,6 +156,95 @@ public class XInputMethod extends X11InputMethod {
|
||||
return peer.getContentWindow();
|
||||
}
|
||||
|
||||
|
||||
static void onXKeyEventFiltering(final boolean isXKeyEventFiltered) {
|
||||
// Fix of JBR-1573, JBR-2444, JBR-4394 (a.k.a. IDEA-246833).
|
||||
// Input method is considered broken if and only if all the following statements are true:
|
||||
// * XFilterEvent have filtered more than filteredEventsThreshold last events of types KeyPress, KeyRelease;
|
||||
// * Input method hasn't been changed (e.g. recreated);
|
||||
// * The input context is not in preedit state (XNPreeditStartCallback has been called but then XNPreeditDoneCallback - hasn't)
|
||||
|
||||
// The functionality is disabled
|
||||
if (BrokenImDetectionContext.EATEN_EVENTS_THRESHOLD < 1) {
|
||||
return;
|
||||
}
|
||||
// Must be called within AWT_LOCK
|
||||
if (!XToolkit.isAWTLockHeldByCurrentThread()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isXKeyEventFiltered) {
|
||||
final long nativeDataPtr = BrokenImDetectionContext.obtainCurrentXimNativeDataPtr();
|
||||
if (nativeDataPtr == 0) {
|
||||
++BrokenImDetectionContext.eatenKeyEventsCount;
|
||||
} else {
|
||||
final int isDuringPreediting = BrokenImDetectionContext.isDuringPreediting(nativeDataPtr);
|
||||
if (isDuringPreediting > 0) {
|
||||
BrokenImDetectionContext.eatenKeyEventsCount = 0;
|
||||
} else if (isDuringPreediting == 0) {
|
||||
++BrokenImDetectionContext.eatenKeyEventsCount;
|
||||
} else if (BrokenImDetectionContext.isCurrentXicPassive(nativeDataPtr)) {
|
||||
// Unfortunately for passive XIC (XIMPreeditNothing | XIMStatusNothing) we have no way to get know
|
||||
// whether the XIC is in preediting state or not, so we won't handle this case.
|
||||
BrokenImDetectionContext.eatenKeyEventsCount = 0;
|
||||
} else {
|
||||
++BrokenImDetectionContext.eatenKeyEventsCount;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BrokenImDetectionContext.eatenKeyEventsCount = 0;
|
||||
}
|
||||
|
||||
if (BrokenImDetectionContext.eatenKeyEventsCount > BrokenImDetectionContext.EATEN_EVENTS_THRESHOLD) {
|
||||
BrokenImDetectionContext.eatenKeyEventsCount = 0;
|
||||
recreateAllXIC();
|
||||
}
|
||||
}
|
||||
|
||||
private static class BrokenImDetectionContext {
|
||||
static final int EATEN_EVENTS_THRESHOLD;
|
||||
|
||||
static int eatenKeyEventsCount = 0;
|
||||
|
||||
/**
|
||||
* @return pointer to X11InputMethodData
|
||||
*/
|
||||
static native long obtainCurrentXimNativeDataPtr();
|
||||
|
||||
/**
|
||||
* <0 - unknown
|
||||
* >0 - true
|
||||
* 0 - false
|
||||
*/
|
||||
static native int isDuringPreediting(long ximNativeDataPtr);
|
||||
|
||||
static native boolean isCurrentXicPassive(long ximNativeDataPtr);
|
||||
|
||||
|
||||
static {
|
||||
int eatenEventsThresholdInitializer = 7;
|
||||
final String eventsThresholdMode = System.getProperty("recreate.x11.input.method", "true");
|
||||
|
||||
if ("false".equals(eventsThresholdMode)) {
|
||||
eatenEventsThresholdInitializer = 0;
|
||||
} else if (!"true".equals(eventsThresholdMode)) {
|
||||
try {
|
||||
eatenEventsThresholdInitializer = Integer.parseInt(eventsThresholdMode);
|
||||
} catch (NumberFormatException err) {
|
||||
log.warning(
|
||||
"Invalid value of \"recreate.x11.input.method\" system property \"" +
|
||||
eventsThresholdMode +
|
||||
"\". Only \"true\", \"false\" and integer values are supported",
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EATEN_EVENTS_THRESHOLD = eatenEventsThresholdInitializer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Native methods
|
||||
*/
|
||||
|
||||
@@ -1022,21 +1022,28 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && (
|
||||
ev.get_type() == XConstants.KeyPress
|
||||
|| ev.get_type() == XConstants.KeyRelease)) {
|
||||
|
||||
final boolean isKeyEvent = ( (ev.get_type() == XConstants.KeyPress) ||
|
||||
(ev.get_type() == XConstants.KeyRelease) );
|
||||
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && isKeyEvent) {
|
||||
keyEventLog.fine("before XFilterEvent:" + ev);
|
||||
}
|
||||
if (XlibWrapper.XFilterEvent(ev.getPData(), w)) {
|
||||
if (isKeyEvent) {
|
||||
XInputMethod.onXKeyEventFiltering(true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && (
|
||||
ev.get_type() == XConstants.KeyPress
|
||||
|| ev.get_type() == XConstants.KeyRelease)) {
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && isKeyEvent) {
|
||||
keyEventLog.fine(
|
||||
"after XFilterEvent:" + ev); // IS THIS CORRECT?
|
||||
}
|
||||
|
||||
if (isKeyEvent) {
|
||||
XInputMethod.onXKeyEventFiltering(false);
|
||||
}
|
||||
|
||||
dispatchEvent(ev);
|
||||
} catch (Throwable thr) {
|
||||
XBaseWindow.ungrabInput();
|
||||
|
||||
@@ -364,8 +364,8 @@ public abstract class X11InputMethod extends X11InputMethodBase {
|
||||
}
|
||||
}
|
||||
|
||||
static void recreateAllXIC() {
|
||||
// NOTE: called from native within AWT_LOCK
|
||||
protected static void recreateAllXIC() {
|
||||
// NOTE: called within AWT_LOCK
|
||||
Map<X11InputMethod, Integer> im2ctxid = new HashMap<>(activeInputMethods.size());
|
||||
for (X11InputMethod im : activeInputMethods) {
|
||||
im2ctxid.put(im, im.releaseXIC());
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <sun_awt_X11InputMethodBase.h>
|
||||
#include <sun_awt_X11_XInputMethod.h>
|
||||
#include <sun_awt_X11_XInputMethod_BrokenImDetectionContext.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -129,6 +130,10 @@ typedef struct _X11InputMethodData {
|
||||
#endif
|
||||
char *lookup_buf; /* buffer used for XmbLookupString */
|
||||
int lookup_buf_len; /* lookup buffer size in bytes */
|
||||
|
||||
struct {
|
||||
Boolean isBetweenPreeditStartAndPreeditDone;
|
||||
} brokenImDetectionContext;
|
||||
} X11InputMethodData;
|
||||
|
||||
/*
|
||||
@@ -404,6 +409,8 @@ freeX11InputMethodData(JNIEnv *env, X11InputMethodData *pX11IMData)
|
||||
free((void *)pX11IMData->lookup_buf);
|
||||
}
|
||||
|
||||
pX11IMData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone = False;
|
||||
|
||||
free((void *)pX11IMData);
|
||||
}
|
||||
|
||||
@@ -1041,6 +1048,8 @@ createXIC(JNIEnv * env, X11InputMethodData *pX11IMData, Window w)
|
||||
XNResetState, XIMInitialState,
|
||||
NULL);
|
||||
|
||||
pX11IMData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone = False;
|
||||
|
||||
/* Add the global reference object to X11InputMethod to the list. */
|
||||
addToX11InputMethodGRefList(pX11IMData->x11inputmethod);
|
||||
|
||||
@@ -1061,16 +1070,53 @@ createXIC(JNIEnv * env, X11InputMethodData *pX11IMData, Window w)
|
||||
static int
|
||||
PreeditStartCallback(XIC ic, XPointer client_data, XPointer call_data)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/* printf("Native: PreeditStartCallback\n"); */
|
||||
/* printf("Native: PreeditStartCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
|
||||
JNIEnv * const env = GetJNIEnv();
|
||||
|
||||
AWT_LOCK();
|
||||
|
||||
jobject javaInputMethodGRef = (jobject)client_data;
|
||||
if (!isX11InputMethodGRefInList(javaInputMethodGRef)) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
X11InputMethodData * const pX11IMData = getX11InputMethodData(env, javaInputMethodGRef);
|
||||
if (pX11IMData == NULL) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
pX11IMData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone = True;
|
||||
|
||||
finally:
|
||||
AWT_UNLOCK();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
PreeditDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/* printf("Native: PreeditDoneCallback\n"); */
|
||||
/* printf("Native: PreeditDoneCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
|
||||
JNIEnv * const env = GetJNIEnv();
|
||||
|
||||
AWT_LOCK();
|
||||
|
||||
jobject javaInputMethodGRef = (jobject)client_data;
|
||||
if (!isX11InputMethodGRefInList(javaInputMethodGRef)) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
X11InputMethodData * const pX11IMData = getX11InputMethodData(env, javaInputMethodGRef);
|
||||
if (pX11IMData == NULL) {
|
||||
goto finally;
|
||||
}
|
||||
|
||||
pX11IMData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone = False;
|
||||
|
||||
finally:
|
||||
AWT_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1083,6 +1129,8 @@ static void
|
||||
PreeditDrawCallback(XIC ic, XPointer client_data,
|
||||
XIMPreeditDrawCallbackStruct *pre_draw)
|
||||
{
|
||||
/* printf("Native: PreeditDrawCallback(%p, %p, %p)\n", ic, client_data, pre_draw); */
|
||||
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
jmethodID x11imMethodID;
|
||||
@@ -1175,7 +1223,7 @@ PreeditCaretCallback(XIC ic, XPointer client_data,
|
||||
XIMPreeditCaretCallbackStruct *pre_caret)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/* printf("Native: PreeditCaretCallback\n"); */
|
||||
/* printf("Native: PreeditCaretCallback(%p, %p, %p)\n", ic, client_data, pre_caret); */
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
@@ -1183,14 +1231,14 @@ static void
|
||||
StatusStartCallback(XIC ic, XPointer client_data, XPointer call_data)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/*printf("StatusStartCallback:\n"); */
|
||||
/*printf("Native: StatusStartCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
}
|
||||
|
||||
static void
|
||||
StatusDoneCallback(XIC ic, XPointer client_data, XPointer call_data)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/*printf("StatusDoneCallback:\n"); */
|
||||
/*printf("Native: StatusDoneCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
StatusWindow *statusWindow;
|
||||
@@ -1221,7 +1269,7 @@ StatusDrawCallback(XIC ic, XPointer client_data,
|
||||
XIMStatusDrawCallbackStruct *status_draw)
|
||||
{
|
||||
/*ARGSUSED*/
|
||||
/*printf("StatusDrawCallback:\n"); */
|
||||
/*printf("Native: StatusDrawCallback(%p, %p, %p)\n", ic, client_data, status_draw); */
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
StatusWindow *statusWindow;
|
||||
@@ -1274,6 +1322,8 @@ StatusDrawCallback(XIC ic, XPointer client_data,
|
||||
#endif /* __linux__ */
|
||||
|
||||
static void CommitStringCallback(XIC ic, XPointer client_data, XPointer call_data) {
|
||||
/* printf("Native: CommitStringCallback(%p, %p, %p)\n", ic, client_data, call_data); */
|
||||
|
||||
JNIEnv *env = GetJNIEnv();
|
||||
XIMText * text = (XIMText *)call_data;
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
@@ -1514,6 +1564,110 @@ Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env,
|
||||
AWT_UNLOCK();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11_XInputMethod_BrokenImDetectionContext
|
||||
* Method: obtainCurrentXimNativeDataPtr
|
||||
* Signature: ()J
|
||||
*
|
||||
* NOTE: MUST BE CALLED WITHIN AWT_LOCK
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_sun_awt_X11_XInputMethod_00024BrokenImDetectionContext_obtainCurrentXimNativeDataPtr
|
||||
(JNIEnv *env, jclass cls)
|
||||
{
|
||||
jlong result = 0;
|
||||
|
||||
if (isX11InputMethodGRefInList(currentX11InputMethodInstance)) {
|
||||
X11InputMethodData * const pX11IMData = getX11InputMethodData(env, currentX11InputMethodInstance);
|
||||
result = ptr_to_jlong(pX11IMData);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11_XInputMethod_BrokenImDetectionContext
|
||||
* Method: isCurrentXicPassive
|
||||
* Signature: (J)Z
|
||||
*
|
||||
* NOTE: MUST BE CALLED WITHIN AWT_LOCK
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XInputMethod_00024BrokenImDetectionContext_isCurrentXicPassive
|
||||
(JNIEnv *env, jclass cls, jlong ximNativeDataPtr)
|
||||
{
|
||||
X11InputMethodData * const pX11ImData = (X11InputMethodData *)jlong_to_ptr(ximNativeDataPtr);
|
||||
if (pX11ImData == NULL) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
const jboolean result = (pX11ImData->current_ic == NULL) ? JNI_FALSE
|
||||
: (pX11ImData->current_ic == pX11ImData->ic_passive) ? JNI_TRUE
|
||||
: JNI_FALSE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static XIMPreeditState getPreeditStateOf(XIC xic) {
|
||||
#if defined(__linux__) && defined(_LP64) && !defined(_LITTLE_ENDIAN)
|
||||
// XIMPreeditState value which is used for XGetICValues must be 32bit on BigEndian XOrg's xlib
|
||||
unsigned int state = XIMPreeditUnKnown;
|
||||
#else
|
||||
XIMPreeditState state = XIMPreeditUnKnown;
|
||||
#endif
|
||||
|
||||
XVaNestedList preeditStateAttr = XVaCreateNestedList(0, XNPreeditState, &state, NULL);
|
||||
if (preeditStateAttr == NULL) {
|
||||
return XIMPreeditUnKnown;
|
||||
}
|
||||
const char * const unsupportedAttrs = XGetICValues(xic, XNPreeditAttributes, preeditStateAttr, NULL);
|
||||
XFree((void *)preeditStateAttr);
|
||||
|
||||
if (unsupportedAttrs != NULL) {
|
||||
return XIMPreeditUnKnown;
|
||||
}
|
||||
|
||||
return (state == XIMPreeditEnable) ? XIMPreeditEnable
|
||||
: (state == XIMPreeditDisable) ? XIMPreeditDisable
|
||||
: XIMPreeditUnKnown;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11_XInputMethod_BrokenImDetectionContext
|
||||
* Method: isDuringPreediting
|
||||
* Signature: ()I
|
||||
*
|
||||
* Returns the following values:
|
||||
* * >0 in case the IM is in preediting state;
|
||||
* * 0 in case the IM is not in preediting state;
|
||||
* * <0 in case it's unknown whether the IM is in preediting state or not.
|
||||
*
|
||||
* NOTE: MUST BE CALLED WITHIN AWT_LOCK
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_X11_XInputMethod_00024BrokenImDetectionContext_isDuringPreediting
|
||||
(JNIEnv *env, jclass cls, jlong ximNativeDataPtr)
|
||||
{
|
||||
X11InputMethodData * const pX11ImData = (X11InputMethodData *)jlong_to_ptr(ximNativeDataPtr);
|
||||
if (pX11ImData == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
jint result = -1;
|
||||
|
||||
if (pX11ImData->brokenImDetectionContext.isBetweenPreeditStartAndPreeditDone) {
|
||||
result = 1;
|
||||
} else if (pX11ImData->current_ic != NULL) {
|
||||
const XIMPreeditState preeditState = getPreeditStateOf(pX11ImData->current_ic);
|
||||
if (preeditState == XIMPreeditEnable) {
|
||||
result = 1;
|
||||
} else if (preeditState == XIMPreeditDisable) {
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11InputMethodBase
|
||||
* Method: initIDs
|
||||
|
||||
@@ -653,63 +653,6 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XWindowEvent
|
||||
XWindowEvent( (Display *) jlong_to_ptr(display), (Window)window, event_mask, (XEvent *) jlong_to_ptr(event_return));
|
||||
}
|
||||
|
||||
static int filteredEventsCount = 0;
|
||||
static int filteredEventsThreshold = -1;
|
||||
static const int DEFAULT_THRESHOLD = 5;
|
||||
#define KeyPressEventType 2
|
||||
#define KeyReleaseEventType 3
|
||||
|
||||
static void checkBrokenInputMethod(XEvent * event, jboolean isEventFiltered) {
|
||||
// Fix for JBR-2444
|
||||
// By default filteredEventsThreshold == 5, you can turn it off with
|
||||
// recreate.x11.input.method=false
|
||||
if (filteredEventsThreshold < 0) {
|
||||
filteredEventsThreshold = 0;
|
||||
|
||||
// read from VM-property
|
||||
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
|
||||
jclass systemCls = (*env)->FindClass(env, "java/lang/System");
|
||||
CHECK_NULL(systemCls);
|
||||
jmethodID mid = (*env)->GetStaticMethodID(env, systemCls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
CHECK_NULL(mid);
|
||||
jstring name = (*env)->NewStringUTF(env, "recreate.x11.input.method");
|
||||
CHECK_NULL(name);
|
||||
jstring jvalue = (*env)->CallStaticObjectMethod(env, systemCls, mid, name);
|
||||
|
||||
if (jvalue == NULL) {
|
||||
filteredEventsThreshold = DEFAULT_THRESHOLD;
|
||||
} else {
|
||||
const char * utf8string = (*env)->GetStringUTFChars(env, jvalue, NULL);
|
||||
if (utf8string != NULL) {
|
||||
const int parsedVal = atoi(utf8string);
|
||||
if (parsedVal > 0)
|
||||
filteredEventsThreshold = parsedVal;
|
||||
else if (strncmp(utf8string, "true", 4) == 0)
|
||||
filteredEventsThreshold = DEFAULT_THRESHOLD;
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, jvalue, utf8string);
|
||||
}
|
||||
|
||||
(*env)->DeleteLocalRef(env, name);
|
||||
}
|
||||
if (filteredEventsThreshold <= 0)
|
||||
return;
|
||||
|
||||
if (event->type == KeyPressEventType || event->type == KeyReleaseEventType) {
|
||||
if (isEventFiltered) {
|
||||
filteredEventsCount++;
|
||||
} else {
|
||||
filteredEventsCount = 0;
|
||||
}
|
||||
|
||||
if (filteredEventsCount > filteredEventsThreshold) {
|
||||
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
|
||||
JNU_CallStaticMethodByName(env, NULL, "sun/awt/X11InputMethod", "recreateAllXIC", "()V");
|
||||
filteredEventsCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_X11_XlibWrapper
|
||||
* Method: XFilterEvent
|
||||
@@ -724,8 +667,10 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_X11_XlibWrapper_XFilterEvent
|
||||
return (jboolean)True;
|
||||
}
|
||||
#endif
|
||||
jboolean isEventFiltered = (jboolean) XFilterEvent((XEvent *) jlong_to_ptr(ptr), (Window) window);
|
||||
checkBrokenInputMethod((XEvent *)jlong_to_ptr(ptr), isEventFiltered);
|
||||
XEvent* const xEvent = (XEvent *)jlong_to_ptr(ptr);
|
||||
|
||||
const jboolean isEventFiltered = (jboolean) XFilterEvent(xEvent, (Window) window);
|
||||
|
||||
return isEventFiltered;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user