mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2026-01-09 10:01:41 +01:00
JBR-6456 Sudden keyboard death on Linux using iBus.
Add a workaround for the iBus's bug which leads to the issue. (cherry picked from commitb8e9dbf8c9) (cherry picked from commit7355948065)
This commit is contained in:
committed by
jbrbot
parent
099c53541c
commit
9cb30a006d
@@ -332,6 +332,7 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
|
||||
DISABLED_WARNINGS_gcc_XRBackendNative.c := maybe-uninitialized, \
|
||||
DISABLED_WARNINGS_gcc_XToolkit.c := unused-result, \
|
||||
DISABLED_WARNINGS_gcc_XWindow.c := unused-function, \
|
||||
DISABLED_WARNINGS_gcc_awt_InputMethod.c := unused-label, \
|
||||
DISABLED_WARNINGS_clang_awt_Taskbar.c := parentheses, \
|
||||
DISABLED_WARNINGS_clang_gtk3_interface.c := unused-function parentheses, \
|
||||
DISABLED_WARNINGS_clang_GLXSurfaceData.c := unused-function, \
|
||||
@@ -339,6 +340,7 @@ ifeq ($(call isTargetOs, windows macosx)+$(ENABLE_HEADLESS_ONLY), false+false)
|
||||
DISABLED_WARNINGS_clang_OGLPaints.c := format-nonliteral, \
|
||||
DISABLED_WARNINGS_clang_screencast_pipewire.c := format-nonliteral, \
|
||||
DISABLED_WARNINGS_clang_sun_awt_X11_GtkFileDialogPeer.c := parentheses, \
|
||||
DISABLED_WARNINGS_clang_awt_InputMethod.c := unused-label, \
|
||||
DISABLED_WARNINGS_clang_XWindow.c := unused-function, \
|
||||
DISABLED_WARNINGS_clang_aix := deprecated-non-prototype, \
|
||||
DISABLED_WARNINGS_clang_aix_awt_Taskbar.c := parentheses, \
|
||||
|
||||
@@ -33,12 +33,15 @@ import java.awt.im.spi.InputMethodContext;
|
||||
import java.awt.peer.ComponentPeer;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.X11GraphicsDevice;
|
||||
import sun.awt.X11GraphicsEnvironment;
|
||||
import sun.awt.X11InputMethod;
|
||||
@@ -303,6 +306,155 @@ public class XInputMethod extends X11InputMethod {
|
||||
}
|
||||
|
||||
|
||||
// JBR-6456: Sudden keyboard death on Linux using iBus.
|
||||
// xicDestroyMustBeDelayed, XIC_DELAYED_TO_BE_DESTROYED_CAPACITY, xicDelayedToBeDestroyed can only be accessed
|
||||
// under the AWT lock
|
||||
// See the #disposeXIC method for the purpose of these fields
|
||||
private static boolean xicDestroyMustBeDelayed = false;
|
||||
private static final int XIC_DELAYED_TO_BE_DESTROYED_CAPACITY = 16;
|
||||
private static final Queue<Long> xicDelayedToBeDestroyed = new ArrayDeque<>(XIC_DELAYED_TO_BE_DESTROYED_CAPACITY);
|
||||
|
||||
static void delayAllXICDestroyUntilAFurtherNotice() {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("delayAllXICDestroyUntilAFurtherNotice(): is being called", new Throwable("Stacktrace"));
|
||||
}
|
||||
|
||||
XToolkit.awtLock();
|
||||
try {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("delayAllXICDestroyUntilAFurtherNotice(): xicDestroyMustBeDelayed=={0}", xicDestroyMustBeDelayed);
|
||||
}
|
||||
|
||||
xicDestroyMustBeDelayed = true;
|
||||
} finally {
|
||||
XToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
static void delayedXICDestroyShouldBeDone() {
|
||||
XToolkit.awtLock();
|
||||
try {
|
||||
xicDestroyMustBeDelayed = false;
|
||||
doDelayedXICDestroy(false, -1);
|
||||
} finally {
|
||||
XToolkit.awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void doDelayedXICDestroy(boolean forced, int maxCountToDestroy) {
|
||||
final boolean isFineLoggable = log.isLoggable(PlatformLogger.Level.FINE);
|
||||
|
||||
if (isFineLoggable) {
|
||||
log.fine(
|
||||
"doDelayedXICDestroy(forced==" + forced + ", maxCountToDestroy==" + maxCountToDestroy + "): is being called",
|
||||
new Throwable("Stacktrace")
|
||||
);
|
||||
}
|
||||
|
||||
assert(SunToolkit.isAWTLockHeldByCurrentThread());
|
||||
assert(forced || !xicDestroyMustBeDelayed);
|
||||
|
||||
while ( (maxCountToDestroy != 0) && !xicDelayedToBeDestroyed.isEmpty() ) {
|
||||
final long pX11IMData = xicDelayedToBeDestroyed.remove();
|
||||
--maxCountToDestroy;
|
||||
|
||||
if (isFineLoggable) {
|
||||
log.fine("doDelayedXICDestroy(): destroying pX11IMData={0}", pX11IMData);
|
||||
}
|
||||
|
||||
assert(pX11IMData != 0);
|
||||
delayedDisposeXIC_disposeXICNative(pX11IMData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disposeXIC() {
|
||||
awtLock();
|
||||
try {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("disposeXIC(): xicDestroyMustBeDelayed=={0}", xicDestroyMustBeDelayed);
|
||||
}
|
||||
|
||||
if (!xicDestroyMustBeDelayed) {
|
||||
// JBR-6456: Sudden keyboard death on Linux using iBus.
|
||||
// iBus's X11 frontend being run in the async mode (IBUS_ENABLE_SYNC_MODE=0) has a bug leading to a
|
||||
// violation of the communication protocol between iBus and Xlib (so-called "XIM protocol"),
|
||||
// later causing Xlib to behave unexpectedly from iBus's point of view, breaking iBus's
|
||||
// internal state. After all, iBus starts to "steal" all the keyboard events
|
||||
// (so that each call of XFilterEvent(...) with an instance of XKeyEvent returns True).
|
||||
// The initial iBus's bug only appears when XDestroyIC(...) gets called right after a call of
|
||||
// XFilterEvent(...) with an instance of XKeyEvent returned True,
|
||||
// meaning that iBus has started, but hasn't finished yet processing of the key event.
|
||||
// In case of AWT/Swing apps, XDestroyIC gets called whenever a focused HW window gets closed
|
||||
// (because it leads to disposing of the associated input context,
|
||||
// see java.awt.Window#doDispose and sun.awt.im.InputContext#dispose)
|
||||
// So, to work around iBus's bug, we have to avoid calling XDestroyIC until iBus finishes processing of
|
||||
// all the keyboard events it has already started processing of, i.e. until a call of
|
||||
// XFilterEvent(...) returns False.
|
||||
// To achieve that, the implemented fix delays destroying of input contexts whenever a call of
|
||||
// XFilterEvent(...) with an instance of XKeyEvent returns True until one of the next calls of
|
||||
// XFilterEvent(...) with the same instance of XKeyEvent returns False.
|
||||
// The delaying is implemented via storing the native pointers to the input contexts to
|
||||
// xicDelayedToBeDestroyed instead of applying XDestroyIC(...) immediately.
|
||||
// The xicDelayedToBeDestroyed's size is explicitly limited to
|
||||
// XIC_DELAYED_TO_BE_DESTROYED_CAPACITY. If the limit gets reached, a few input contexts gets
|
||||
// pulled from there and destroyed regardless of the current value of xicDestroyMustBeDelayed.
|
||||
// The xicDestroyMustBeDelayed field is responsible for indication whether it's required to delay
|
||||
// the destroying or not. It gets set in #delayAllXICDestroyUntilAFurtherNotice
|
||||
// and unset in delayedXICDestroyShouldBeDone; both are called by sun.awt.X11.XToolkit depending on
|
||||
// the value returned by the calls of sun.awt.X11.XlibWrapper#XFilterEvent.
|
||||
|
||||
super.disposeXIC();
|
||||
return;
|
||||
}
|
||||
|
||||
final long pX11IMData = pData;
|
||||
|
||||
// To make sure that the delayed to be destroyed input context won't get used by AWT/Swing or Xlib
|
||||
// by a mistake, the following things are done:
|
||||
// 1. The input method focus gets detached from the input context (via a call of XUnsetICFocus)
|
||||
// 2. All the native pointers to this instance of XInputMethod
|
||||
// (now it's just the variable currentX11InputMethodInstance in awt_InputMethod.c) get unset
|
||||
// 3. All the java pointers to the native context (now it's just sun.awt.X11InputMethodBase#pData)
|
||||
// get unset as well
|
||||
delayedDisposeXIC_preparation_unsetFocusAndDetachCurrentXICNative();
|
||||
|
||||
// 4. The state of the native context gets reset (effectively via a call of XmbResetIC)
|
||||
delayedDisposeXIC_preparation_resetSpecifiedCtxNative(pX11IMData);
|
||||
|
||||
if (pX11IMData == 0) {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("disposeXIC(): pX11IMData==NULL, skipped");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the storage is full, a few input context are pulled from there and destroyed regardless of
|
||||
// the value of xicDestroyMustBeDelayed
|
||||
if (xicDelayedToBeDestroyed.size() >= XIC_DELAYED_TO_BE_DESTROYED_CAPACITY) {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine(
|
||||
"disposeXIC(): xicDelayedToBeDestroyed.size()=={0} >= XIC_DELAYED_TO_BE_DESTROYED_CAPACITY",
|
||||
xicDelayedToBeDestroyed.size()
|
||||
);
|
||||
}
|
||||
|
||||
doDelayedXICDestroy(true, xicDelayedToBeDestroyed.size() - XIC_DELAYED_TO_BE_DESTROYED_CAPACITY + 1);
|
||||
}
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine(
|
||||
"disposeXIC(): adding pX11IMData=={0} to xicDelayedToBeDestroyed (which already contains {1} elements)",
|
||||
pX11IMData, xicDelayedToBeDestroyed.size()
|
||||
);
|
||||
}
|
||||
xicDelayedToBeDestroyed.add(pX11IMData);
|
||||
} finally {
|
||||
awtUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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:
|
||||
@@ -616,6 +768,15 @@ public class XInputMethod extends X11InputMethod {
|
||||
private native void setXICFocusNative(long window, boolean value, boolean active);
|
||||
private native void adjustStatusWindow(long window);
|
||||
|
||||
// 1. Applies XUnsetICFocus to the current input context
|
||||
// 2. Unsets currentX11InputMethodInstance if it's set to this instance of XInputMethod
|
||||
// 3. Unsets sun.awt.X11InputMethodBase#pData
|
||||
private native void delayedDisposeXIC_preparation_unsetFocusAndDetachCurrentXICNative();
|
||||
// Applies XmbResetIC to the passed input context
|
||||
private static native void delayedDisposeXIC_preparation_resetSpecifiedCtxNative(long pX11IMData);
|
||||
// Applies XDestroyIC to the passed input context
|
||||
private static native void delayedDisposeXIC_disposeXICNative(long pX11IMData);
|
||||
|
||||
private native boolean doesFocusedXICSupportMovingCandidatesNativeWindow();
|
||||
|
||||
private native void adjustCandidatesNativeWindowPosition(int x, int y);
|
||||
|
||||
@@ -988,11 +988,22 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
||||
final boolean isKeyEvent = ( (ev.get_type() == XConstants.KeyPress) ||
|
||||
(ev.get_type() == XConstants.KeyRelease) );
|
||||
|
||||
final long keyEventSerial = isKeyEvent ? ev.get_xkey().get_serial() : -1;
|
||||
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE) && isKeyEvent) {
|
||||
keyEventLog.fine("before XFilterEvent:" + ev);
|
||||
}
|
||||
if (XlibWrapper.XFilterEvent(ev.getPData(), w)) {
|
||||
if (isKeyEvent) {
|
||||
if (keyEventLog.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
keyEventLog.fine(
|
||||
"Setting lastFilteredKeyEventSerial=={0} to {1}",
|
||||
lastFilteredKeyEventSerial, keyEventSerial
|
||||
);
|
||||
}
|
||||
lastFilteredKeyEventSerial = keyEventSerial;
|
||||
|
||||
XInputMethod.delayAllXICDestroyUntilAFurtherNotice();
|
||||
XInputMethod.onXKeyEventFiltering(true);
|
||||
}
|
||||
continue;
|
||||
@@ -1004,6 +1015,14 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
||||
|
||||
if (isKeyEvent) {
|
||||
XInputMethod.onXKeyEventFiltering(false);
|
||||
if (keyEventSerial == lastFilteredKeyEventSerial) {
|
||||
// JBR-6456: Sudden keyboard death on Linux using iBus.
|
||||
// If more than 1 key events are being processed by iBus
|
||||
// (i.e. more than one in a row calls of XFilterEvent(...) with instances of XKeyEvent have
|
||||
// returned true),
|
||||
// we have to postpone destroying until the very last one is completely processed)
|
||||
XInputMethod.delayedXICDestroyShouldBeDone();
|
||||
}
|
||||
}
|
||||
|
||||
dispatchEvent(ev);
|
||||
@@ -1075,6 +1094,14 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
||||
}
|
||||
|
||||
|
||||
// JBR-6456: Sudden keyboard death on Linux using iBus.
|
||||
// The field holds the value of sun.awt.X11.XKeyEvent#get_serial of the last key event, which
|
||||
// XFilterEvent(...) returned True for.
|
||||
// See the usages of the variable for more info.
|
||||
// See sun.awt.X11.XInputMethod#disposeXIC for the detailed explanation of the whole fix.
|
||||
private long lastFilteredKeyEventSerial = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Listener installed to detect display changes.
|
||||
*/
|
||||
|
||||
@@ -1638,6 +1638,81 @@ Java_sun_awt_X11_XInputMethod_releaseXICNative(JNIEnv *env,
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_X11_XInputMethod_delayedDisposeXIC_1preparation_1unsetFocusAndDetachCurrentXICNative
|
||||
(JNIEnv *env, jobject this)
|
||||
{
|
||||
DASSERT(env != NULL);
|
||||
X11InputMethodData *pX11IMData = NULL;
|
||||
|
||||
AWT_LOCK();
|
||||
|
||||
pX11IMData = getX11InputMethodData(env, this);
|
||||
if (pX11IMData == NULL) {
|
||||
AWT_UNLOCK();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pX11IMData->ic_active.xic != (XIC)0) {
|
||||
setXICFocus(pX11IMData->ic_active.xic, False);
|
||||
}
|
||||
if ( (pX11IMData->ic_passive.xic != (XIC)0) && (pX11IMData->ic_passive.xic != pX11IMData->ic_active.xic) ) {
|
||||
setXICFocus(pX11IMData->ic_passive.xic, False);
|
||||
}
|
||||
pX11IMData->current_ic = (XIC)0;
|
||||
|
||||
setX11InputMethodData(env, this, NULL);
|
||||
if ( (*env)->IsSameObject(env, pX11IMData->x11inputmethod, currentX11InputMethodInstance) == JNI_TRUE ) {
|
||||
// currentX11InputMethodInstance never holds a "unique" java ref - it only holds a "weak" copy of
|
||||
// _X11InputMethodData::x11inputmethod, so we mustn't DeleteGlobalRef here
|
||||
currentX11InputMethodInstance = NULL;
|
||||
currentFocusWindow = 0;
|
||||
}
|
||||
|
||||
AWT_UNLOCK();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_X11_XInputMethod_delayedDisposeXIC_1preparation_1resetSpecifiedCtxNative
|
||||
(JNIEnv *env, jclass clazz, const jlong pData)
|
||||
{
|
||||
X11InputMethodData * const pX11IMData = (X11InputMethodData *)pData;
|
||||
char* preeditText = NULL;
|
||||
|
||||
if (pX11IMData == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
AWT_LOCK();
|
||||
|
||||
if (pX11IMData->ic_active.xic != (XIC)0) {
|
||||
if ( (preeditText = XmbResetIC(pX11IMData->ic_active.xic)) != NULL ) {
|
||||
(void)XFree(preeditText); preeditText = NULL;
|
||||
}
|
||||
}
|
||||
if ( (pX11IMData->ic_passive.xic != (XIC)0) && (pX11IMData->ic_passive.xic != pX11IMData->ic_active.xic) ) {
|
||||
if ( (preeditText = XmbResetIC(pX11IMData->ic_passive.xic)) != NULL ) {
|
||||
(void)XFree(preeditText); preeditText = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
AWT_UNLOCK();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_X11_XInputMethod_delayedDisposeXIC_1disposeXICNative(JNIEnv *env, jclass clazz, jlong pData)
|
||||
{
|
||||
X11InputMethodData *pX11IMData = (X11InputMethodData *)pData;
|
||||
if (pX11IMData == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
AWT_LOCK();
|
||||
destroyX11InputMethodData(env, pX11IMData); pX11IMData = NULL; pData = 0;
|
||||
AWT_UNLOCK();
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_X11_XInputMethod_setXICFocusNative(JNIEnv *env,
|
||||
jobject this,
|
||||
|
||||
Reference in New Issue
Block a user