Skip to content

Commit

Permalink
8305418: [Linux] Replace obsolete XIM as Input Method Editor
Browse files Browse the repository at this point in the history
Reviewed-by: kcr, mfox, lkostyra
  • Loading branch information
Thiago Milczarek Sayao committed Jan 16, 2025
1 parent a95151e commit 069db87
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 386 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,21 @@

import com.sun.glass.ui.Pixels;
import com.sun.glass.ui.View;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Map;

final class GtkView extends View {

private boolean imEnabled = false;
private boolean isInPreeditMode = false;
private final StringBuilder preedit = new StringBuilder();
private ByteBuffer attributes;
private int lastCaret;

private native void enableInputMethodEventsImpl(long ptr, boolean enable);

@Override
protected void _enableInputMethodEvents(long ptr, boolean enable) {
enableInputMethodEventsImpl(ptr, enable);
if (imEnabled) {
preedit.setLength(0);
}

imEnabled = enable;
}

Expand Down Expand Up @@ -113,106 +106,34 @@ protected void _uploadPixels(long ptr, Pixels pixels) {

@Override
protected void _finishInputMethodComposition(long ptr) {
if (imEnabled && isInPreeditMode) {
// Discard any pre-edited text
preedit.setLength(0);
notifyInputMethod(preedit.toString(), null, null, null, 0, 0, 0);
if (imEnabled) {
enableInputMethodEventsImpl(ptr, true);
}
}

private void notifyPreeditMode(boolean enabled){
isInPreeditMode = enabled;
protected double[] notifyInputMethodCandidateRelativePosRequest(int offset) {
return convertPosToRelative(super.notifyInputMethodCandidatePosRequest(offset));
}

private double[] convertPosToRelative(double[] pos) {
var w = getWindow();

protected void notifyInputMethodDraw(String text, int first, int length, int caret, byte[] attr) {
int[] boundary = null;
byte[] values = null;

if (attributes == null ) {
attributes = ByteBuffer.allocate(32);
if (w != null) {
pos[0] -= (w.getX() + getX());
pos[1] -= (w.getY() + getY());
}

if (length > 0) {
preedit.replace(first, first + length, "");
}
return pos;
}

if (text != null) {
preedit.insert(first, text);
protected void notifyInputMethodLinux(String str, int commitLength, int cursor, byte attr) {
if (commitLength > 0) {
notifyInputMethod(str, null, null, null, commitLength, cursor, 0);
} else {
if (attr == null) {
preedit.setLength(0);
}
}

if (attributes.capacity() < preedit.length()) {
ByteBuffer tmp = ByteBuffer.allocate((int) (preedit.length() * 1.5));
tmp.put(attributes);
attributes = tmp;
}

attributes.limit(preedit.length());

if (attr != null && attributes.limit() >= (first + attr.length)) {
attributes.position(first);
attributes.put(attr);
}

if (attributes.limit() > 0) {
ArrayList<Integer> boundaryList = new ArrayList<>();
ArrayList<Byte> valuesList = new ArrayList<>();
attributes.rewind();
byte lastAttribute = attributes.get();

boundaryList.add(0);
valuesList.add(lastAttribute);

int i = 1;
while (attributes.hasRemaining()) {
byte a = attributes.get();
if (lastAttribute != a) {
boundaryList.add(i);
valuesList.add(a);
}
lastAttribute = a;
i++;
}

boundaryList.add(attributes.limit());

boundary = new int[boundaryList.size()];
i = 0;
for (Integer e : boundaryList) {
boundary[i++] = e;
}

values = new byte[valuesList.size()];
i = 0;
for (Byte e: valuesList) {
values[i++] = e;
}
}

notifyInputMethod(preedit.toString(), boundary, boundary, values, 0, caret, 0);
lastCaret = caret;
}
int[] attBounds = new int[] { 0, str.length() };
byte[] attValues = new byte[] { attr };

protected void notifyInputMethodCaret(int pos, int direction, int style) {
switch (direction) {
case 0: //XIMForwardChar
lastCaret += pos;
break;
case 1: //XIMBackwardChar
lastCaret -= pos;
break;
case 10: //XIMAbsolute
lastCaret = pos;
break;
default:
//TODO: as we don't know the text structure, we cannot compute the position
// for other directions (like forward words, lines, etc...).
// Luckily, vast majority of IM uses XIMAbsolute (10)
notifyInputMethod(str, attBounds, attBounds, attValues, 0, cursor, 0);
}
notifyInputMethod(preedit.toString(), null, null, null, 0, lastCaret, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ JNIEXPORT void JNICALL Java_com_sun_glass_ui_gtk_GtkApplication__1init
gdk_window_set_events(root, static_cast<GdkEventMask>(gdk_window_get_events(root) | GDK_PROPERTY_CHANGE_MASK));

platformSupport = new PlatformSupport(env, obj);

// Set ibus to sync mode
setenv("IBUS_ENABLE_SYNC_MODE", "1", 1);
}

/*
Expand Down Expand Up @@ -469,7 +472,7 @@ static void process_events(GdkEvent* event, gpointer data)

EventsCounterHelper helper(ctx);

if (ctx != NULL && ctx->hasIME() && ctx->filterIME(event)) {
if ((event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) && ctx != NULL && ctx->filterIME(event)) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,8 @@ jmethodID jViewNotifyDragOver;
jmethodID jViewNotifyDragDrop;
jmethodID jViewNotifyDragLeave;
jmethodID jViewNotifyScroll;
jmethodID jViewNotifyInputMethod;
jmethodID jViewNotifyInputMethodDraw;
jmethodID jViewNotifyInputMethodCaret;
jmethodID jViewNotifyPreeditMode;
jmethodID jViewNotifyInputMethodLinux;
jmethodID jViewNotifyInputMethodCandidateRelativePosRequest;
jmethodID jViewNotifyMenu;
jfieldID jViewPtr;

Expand Down Expand Up @@ -229,20 +227,17 @@ JNI_OnLoad(JavaVM *jvm, void *reserved)
if (env->ExceptionCheck()) return JNI_ERR;
jViewNotifyScroll = env->GetMethodID(clazz, "notifyScroll", "(IIIIDDIIIIIDD)V");
if (env->ExceptionCheck()) return JNI_ERR;
jViewNotifyInputMethod = env->GetMethodID(clazz, "notifyInputMethod", "(Ljava/lang/String;[I[I[BIII)V");
if (env->ExceptionCheck()) return JNI_ERR;
jViewNotifyMenu = env->GetMethodID(clazz, "notifyMenu", "(IIIIZ)V");
if (env->ExceptionCheck()) return JNI_ERR;
jViewPtr = env->GetFieldID(clazz, "ptr", "J");
if (env->ExceptionCheck()) return JNI_ERR;

clazz = env->FindClass("com/sun/glass/ui/gtk/GtkView");
if (env->ExceptionCheck()) return JNI_ERR;
jViewNotifyInputMethodDraw = env->GetMethodID(clazz, "notifyInputMethodDraw", "(Ljava/lang/String;III[B)V");
if (env->ExceptionCheck()) return JNI_ERR;
jViewNotifyInputMethodCaret = env->GetMethodID(clazz, "notifyInputMethodCaret", "(III)V");
jViewNotifyInputMethodLinux = env->GetMethodID(clazz, "notifyInputMethodLinux", "(Ljava/lang/String;IIB)V");
if (env->ExceptionCheck()) return JNI_ERR;
jViewNotifyPreeditMode = env->GetMethodID(clazz, "notifyPreeditMode", "(Z)V");
jViewNotifyInputMethodCandidateRelativePosRequest
= env->GetMethodID(clazz, "notifyInputMethodCandidateRelativePosRequest", "(I)[D");
if (env->ExceptionCheck()) return JNI_ERR;

clazz = env->FindClass("com/sun/glass/ui/Window");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,9 @@ struct jni_exception: public std::exception {
extern jmethodID jViewNotifyDragDrop; //com.sun.glass.ui.View#notifyDragDrop (IIIII)I
extern jmethodID jViewNotifyDragLeave; //com.sun.glass.ui.View#notifyDragLeave ()V
extern jmethodID jViewNotifyScroll; //com.sun.glass.ui.View#notifyScroll (IIIIDDIIIIIDD)V
extern jmethodID jViewNotifyInputMethod; //com.sun.glass.ui.View#notifyInputMethod (Ljava/lang/String;[I[I[BIII)V
extern jmethodID jViewNotifyInputMethodDraw; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodDraw (Ljava/lang/String;III[B)V
extern jmethodID jViewNotifyInputMethodCaret; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodCaret (III)V
extern jmethodID jViewNotifyPreeditMode; //com.sun.glass.ui.gtk.GtkView#notifyPreeditMode (Z)V
extern jmethodID jViewNotifyInputMethodLinux; //com.sun.glass.ui.View#notifyInputMethodLinux (Ljava/lang/String;IIB)V
extern jmethodID jViewNotifyInputMethodCandidateRelativePosRequest; //com.sun.glass.ui.gtk.GtkView#notifyInputMethodCandidateRelativePosRequest (I)[D

extern jmethodID jViewNotifyMenu; //com.sun.glass.ui.View#notifyMenu (IIIIZ)V
extern jfieldID jViewPtr; //com.sun.glass.ui.View.ptr

Expand Down
76 changes: 24 additions & 52 deletions modules/javafx.graphics/src/main/native-glass/gtk/glass_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,11 @@ void WindowContextBase::process_focus(GdkEventFocus* event) {
ungrab_focus();
}

if (xim.enabled && xim.ic) {
if (im_ctx.enabled && im_ctx.ctx) {
if (event->in) {
XSetICFocus(xim.ic);
gtk_im_context_focus_in(im_ctx.ctx);
} else {
XUnsetICFocus(xim.ic);
gtk_im_context_focus_out(im_ctx.ctx);
}
}

Expand Down Expand Up @@ -459,22 +459,6 @@ void WindowContextBase::process_key(GdkEventKey* event) {
jchar key = gdk_keyval_to_unicode(event->keyval);
if (key >= 'a' && key <= 'z' && (event->state & GDK_CONTROL_MASK)) {
key = key - 'a' + 1; // map 'a' to ctrl-a, and so on.
} else {
#ifdef GLASS_GTK2
if (key == 0) {
// Work around "bug" fixed in gtk-3.0:
// http://mail.gnome.org/archives/commits-list/2011-March/msg06832.html
switch (event->keyval) {
case 0xFF08 /* Backspace */: key = '\b';
case 0xFF09 /* Tab */: key = '\t';
case 0xFF0A /* Linefeed */: key = '\n';
case 0xFF0B /* Vert. Tab */: key = '\v';
case 0xFF0D /* Return */: key = '\r';
case 0xFF1B /* Escape */: key = '\033';
case 0xFFFF /* Delete */: key = '\177';
}
}
#endif
}

if (key > 0) {
Expand All @@ -486,31 +470,27 @@ void WindowContextBase::process_key(GdkEventKey* event) {
} else {
jChars = mainEnv->NewCharArray(0);
}
if (jview) {
if (press) {
mainEnv->CallVoidMethod(jview, jViewNotifyKey,
com_sun_glass_events_KeyEvent_PRESS,
glassKey,
jChars,
glassModifier);
CHECK_JNI_EXCEPTION(mainEnv)

if (jview && key > 0) { // TYPED events should only be sent for printable characters.
mainEnv->CallVoidMethod(jview, jViewNotifyKey,
com_sun_glass_events_KeyEvent_TYPED,
com_sun_glass_events_KeyEvent_VK_UNDEFINED,
jChars,
glassModifier);
CHECK_JNI_EXCEPTION(mainEnv)
}
} else {
mainEnv->CallVoidMethod(jview, jViewNotifyKey,
com_sun_glass_events_KeyEvent_RELEASE,
glassKey,
jChars,
glassModifier);
CHECK_JNI_EXCEPTION(mainEnv)
}
if (!jview) {
return;
}

mainEnv->CallVoidMethod(jview, jViewNotifyKey,
(press) ? com_sun_glass_events_KeyEvent_PRESS
: com_sun_glass_events_KeyEvent_RELEASE,
glassKey,
jChars,
glassModifier);
CHECK_JNI_EXCEPTION(mainEnv)

// jview is checked again because previous call might be an exit key
if (press && key > 0 && jview) { // TYPED events should only be sent for printable characters.
mainEnv->CallVoidMethod(jview, jViewNotifyKey,
com_sun_glass_events_KeyEvent_TYPED,
com_sun_glass_events_KeyEvent_VK_UNDEFINED,
jChars,
glassModifier);
CHECK_JNI_EXCEPTION(mainEnv)
}
}

Expand Down Expand Up @@ -657,15 +637,7 @@ void WindowContextBase::set_background(float r, float g, float b) {
}

WindowContextBase::~WindowContextBase() {
if (xim.ic) {
XDestroyIC(xim.ic);
xim.ic = NULL;
}
if (xim.im) {
XCloseIM(xim.im);
xim.im = NULL;
}

disableIME();
gtk_widget_destroy(gtk_widget);
}

Expand Down
19 changes: 13 additions & 6 deletions modules/javafx.graphics/src/main/native-glass/gtk/glass_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ class WindowContext : public DeletedMemDebug<0xCC> {
virtual bool hasIME() = 0;
virtual bool filterIME(GdkEvent *) = 0;
virtual void enableOrResetIME() = 0;
virtual void updateCaretPos() = 0;
virtual void disableIME() = 0;
virtual void setOnPreEdit(bool) = 0;
virtual void commitIME(gchar *) = 0;

virtual void paint(void* data, jint width, jint height) = 0;
virtual WindowFrameExtents get_frame_extents() = 0;

Expand Down Expand Up @@ -168,11 +172,13 @@ class WindowContext : public DeletedMemDebug<0xCC> {

class WindowContextBase: public WindowContext {

struct _XIM {
XIM im;
XIC ic;
struct ImContext {
GtkIMContext *ctx;
bool enabled;
} xim;
bool on_preedit;
bool send_keypress;
bool on_key_event;
} im_ctx;

size_t events_processing_cnt;
bool can_be_deleted;
Expand Down Expand Up @@ -212,6 +218,9 @@ class WindowContextBase: public WindowContext {
bool hasIME();
bool filterIME(GdkEvent *);
void enableOrResetIME();
void setOnPreEdit(bool);
void commitIME(gchar *);
void updateCaretPos();
void disableIME();
void paint(void*, jint, jint);
GdkWindow *get_gdk_window();
Expand Down Expand Up @@ -252,8 +261,6 @@ class WindowContextBase: public WindowContext {
~WindowContextBase();
protected:
virtual void applyShapeMask(void*, uint width, uint height) = 0;
private:
bool im_filter_keypress(GdkEventKey*);
};

class WindowContextTop: public WindowContextBase {
Expand Down
Loading

11 comments on commit 069db87

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinrushforth
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/skara tag 25+0

@kevinrushforth
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/skara branch jfx24

@openjdk
Copy link

@openjdk openjdk bot commented on 069db87 Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinrushforth The tag 25+0 was successfully created.

@openjdk
Copy link

@openjdk openjdk bot commented on 069db87 Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinrushforth The branch jfx24 was successfully created.

@kevinrushforth
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/skara tag 24+25

@openjdk
Copy link

@openjdk openjdk bot commented on 069db87 Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kevinrushforth The tag 24+25 was successfully created.

@johanvos
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jfx17u

@openjdk
Copy link

@openjdk openjdk bot commented on 069db87 Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johanvos Could not automatically backport 069db877 to openjdk/jfx17u due to conflicts in the following files:

  • modules/javafx.graphics/src/main/native-glass/gtk/GlassApplication.cpp
  • modules/javafx.graphics/src/main/native-glass/gtk/glass_window_ime.cpp

Please fetch the appropriate branch/commit and manually resolve these conflicts by using the following commands in your personal fork of openjdk/jfx17u. Note: these commands are just some suggestions and you can use other equivalent commands you know.

# Fetch the up-to-date version of the target branch
$ git fetch --no-tags https://git.openjdk.org/jfx17u.git master:master

# Check out the target branch and create your own branch to backport
$ git checkout master
$ git checkout -b backport-johanvos-069db877-master

# Fetch the commit you want to backport
$ git fetch --no-tags https://git.openjdk.org/jfx.git 069db877e1c6471a108316701c3136b01cc8f18e

# Backport the commit
$ git cherry-pick --no-commit 069db877e1c6471a108316701c3136b01cc8f18e
# Resolve conflicts now

# Commit the files you have modified
$ git add files/with/resolved/conflicts
$ git commit -m 'Backport 069db877e1c6471a108316701c3136b01cc8f18e'

Once you have resolved the conflicts as explained above continue with creating a pull request towards the openjdk/jfx17u with the title Backport 069db877e1c6471a108316701c3136b01cc8f18e.

Below you can find a suggestion for the pull request body:

Hi all,

This pull request contains a backport of commit 069db877 from the openjdk/jfx repository.

The commit being backported was authored by Thiago Milczarek Sayao on 16 Jan 2025 and was reviewed by Kevin Rushforth, Martin Fox and Lukasz Kostyra.

Thanks!

@johanvos
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jfx21u

@openjdk
Copy link

@openjdk openjdk bot commented on 069db87 Jan 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johanvos Could not automatically backport 069db877 to openjdk/jfx21u due to conflicts in the following files:

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

Please fetch the appropriate branch/commit and manually resolve these conflicts by using the following commands in your personal fork of openjdk/jfx21u. Note: these commands are just some suggestions and you can use other equivalent commands you know.

# Fetch the up-to-date version of the target branch
$ git fetch --no-tags https://git.openjdk.org/jfx21u.git master:master

# Check out the target branch and create your own branch to backport
$ git checkout master
$ git checkout -b backport-johanvos-069db877-master

# Fetch the commit you want to backport
$ git fetch --no-tags https://git.openjdk.org/jfx.git 069db877e1c6471a108316701c3136b01cc8f18e

# Backport the commit
$ git cherry-pick --no-commit 069db877e1c6471a108316701c3136b01cc8f18e
# Resolve conflicts now

# Commit the files you have modified
$ git add files/with/resolved/conflicts
$ git commit -m 'Backport 069db877e1c6471a108316701c3136b01cc8f18e'

Once you have resolved the conflicts as explained above continue with creating a pull request towards the openjdk/jfx21u with the title Backport 069db877e1c6471a108316701c3136b01cc8f18e.

Below you can find a suggestion for the pull request body:

Hi all,

This pull request contains a backport of commit 069db877 from the openjdk/jfx repository.

The commit being backported was authored by Thiago Milczarek Sayao on 16 Jan 2025 and was reviewed by Kevin Rushforth, Martin Fox and Lukasz Kostyra.

Thanks!

Please sign in to comment.