Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support multibytes input from Input method on Linux (Xlib) #62

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/java/org/lwjgl/input/Keyboard.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ public class Keyboard {

private static InputImplementation implementation;

/**
* Input method
* input method is disable at default, for backward compatibility.
**/
private static int imKeyboardBufferSize = 50;
private static String imLocaleModifiers = "@im=none";

/**
* Keyboard cannot be constructed.
*/
Expand Down Expand Up @@ -584,6 +591,47 @@ public static boolean isRepeatEvent() {
}
}

/**
* returns the byte size of keyborad buffer which receives string from Input method.
*/
public static int getImKeyboardBufferSize() {
return imKeyboardBufferSize;
}

/**
* Set the byte size of keyborad buffer which receives string from Input method.
* Can receive a long string at a time, so that this value becomes big.
*
* To support long sentence of Japanease or other multibytes languages by Input method,
* It is recommended to set this value to 1000 or more.
*
* @param imKeyboardBufferSize keyborad buffer size for Input mehotd.
*/
public static void setImKeyboardBufferSize(int imKeyboardBufferSize) {
Keyboard.imKeyboardBufferSize = imKeyboardBufferSize;
}

/**
* returns the Input method locale modifiers.
*/
public static String getImLocaleModifiers() {
return imLocaleModifiers;
}

/**
* Set the Input method locale modifiers which is specified to XSetLocaleModifiers()
* argument on Linux Xlib.
* "@im=none" is set to this value from old days,
* but to support Japanease or other multibytes languages, this value must be empty string.
* when a empty string is specified to the argument of XSetLocaleModifiers(),
* default Input method is used on the environment.
*
* @param imLocaleModifiers the Input method locale modifiers.
*/
public static void setImLocaleModifiers(String imLocaleModifiers) {
Keyboard.imLocaleModifiers = imLocaleModifiers;
}

private static final class KeyEvent {
/** The current keyboard character being examined */
private int character;
Expand All @@ -607,4 +655,5 @@ private void reset() {
repeat = false;
}
}

}
7 changes: 5 additions & 2 deletions src/java/org/lwjgl/opengl/LinuxDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.lwjgl.LWJGLException;
import org.lwjgl.LWJGLUtil;
import org.lwjgl.MemoryUtil;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.XRandR.Screen;
import org.lwjgl.opengles.EGL;

Expand Down Expand Up @@ -864,7 +865,9 @@ private void processEvents() {
event_buffer.nextEvent(getDisplay());
long event_window = event_buffer.getWindow();
relayEventToParent(event_buffer);
if (event_window != getWindow() || event_buffer.filterEvent(event_window) ||
// Regardless of a window where the event occurred, XFilterEvent must send all events to IM.
// So first of all, call org.lwjgl.opengl.LinuxEvent.filterEvent(long).
if (event_buffer.filterEvent(event_window, keyboard) || event_window != getWindow() ||
(mouse != null && mouse.filterEvent(grab, shouldWarpPointer(), event_buffer)) ||
(keyboard != null && keyboard.filterEvent(event_buffer)))
continue;
Expand Down Expand Up @@ -1252,7 +1255,7 @@ public int getMaxCursorSize() {
public void createKeyboard() throws LWJGLException {
lockAWT();
try {
keyboard = new LinuxKeyboard(getDisplay(), getWindow());
keyboard = new LinuxKeyboard(getDisplay(), getWindow(), Keyboard.getImKeyboardBufferSize(), Keyboard.getImLocaleModifiers());
} finally {
unlockAWT();
}
Expand Down
24 changes: 22 additions & 2 deletions src/java/org/lwjgl/opengl/LinuxEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,27 @@ public void sendEvent(long display, long window, boolean propagate, long event_m
}
private static native void nSendEvent(ByteBuffer event_buffer, long display, long window, boolean propagate, long event_mask);

public boolean filterEvent(long window) {
return nFilterEvent(event_buffer, window);
public boolean filterEvent(long window, LinuxKeyboard keyboard) {
// If nFilterEvent() returns True, then some input method has filtered the event,
// and the client should discard the event.
// If nFilterEvent() returns False, then the client should continue processing the event.
if (nFilterEvent(event_buffer, window)) {
return true;
}

// setICFocus() and unsetICFocus() must be called
// whenever FocusIn/FocusOut events are caused.
// Because Input method must know that the receiver of inputed string changed.
switch (getType()) {
case LinuxEvent.FocusIn:
keyboard.setICFocus();
break;
case LinuxEvent.FocusOut:
keyboard.unsetICFocus();
break;
}

return false;
}
private static native boolean nFilterEvent(ByteBuffer event_buffer, long window);

Expand Down Expand Up @@ -205,4 +224,5 @@ public int getKeyState() {
return nGetKeyState(event_buffer);
}
private static native int nGetKeyState(ByteBuffer event_buffer);

}
47 changes: 37 additions & 10 deletions src/java/org/lwjgl/opengl/LinuxKeyboard.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLUtil;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;

final class LinuxKeyboard {
Expand All @@ -68,10 +69,10 @@ final class LinuxKeyboard {
private final EventQueue event_queue = new EventQueue(Keyboard.EVENT_SIZE);

private final ByteBuffer tmp_event = ByteBuffer.allocate(Keyboard.EVENT_SIZE);
private final int[] temp_translation_buffer = new int[KEYBOARD_BUFFER_SIZE];
private final ByteBuffer native_translation_buffer = BufferUtils.createByteBuffer(KEYBOARD_BUFFER_SIZE);
private final CharsetDecoder utf8_decoder = Charset.forName("UTF-8").newDecoder();
private final CharBuffer char_buffer = CharBuffer.allocate(KEYBOARD_BUFFER_SIZE);
private final int[] temp_translation_buffer;
private final ByteBuffer native_translation_buffer;
private final CharBuffer char_buffer;

// Deferred key released event, to detect key repeat
private boolean has_deferred_event;
Expand All @@ -80,7 +81,7 @@ final class LinuxKeyboard {
private long deferred_nanos;
private byte deferred_key_state;

LinuxKeyboard(long display, long window) {
LinuxKeyboard(long display, long window, int keyboardBufferSize, String imLocaleModifiers) {
long modifier_map = getModifierMapping(display);
int tmp_numlock_mask = 0;
int tmp_modeswitch_mask = 0;
Expand Down Expand Up @@ -124,7 +125,7 @@ final class LinuxKeyboard {
caps_lock_mask = tmp_caps_lock_mask;
shift_lock_mask = tmp_shift_lock_mask;
setDetectableKeyRepeat(display, true);
xim = openIM(display);
xim = openIM(display, imLocaleModifiers);
if (xim != 0) {
xic = createIC(xim, window);
if (xic != 0) {
Expand All @@ -136,14 +137,23 @@ final class LinuxKeyboard {
xic = 0;
}
compose_status = allocateComposeStatus();

// Allocate keyborad buffersize.
// When input method send long string to application, it needs a lot of memories.
if (keyboardBufferSize <= 0) {
keyboardBufferSize = KEYBOARD_BUFFER_SIZE;
}
temp_translation_buffer = new int[keyboardBufferSize];
native_translation_buffer = BufferUtils.createByteBuffer(keyboardBufferSize);
char_buffer = CharBuffer.allocate(keyboardBufferSize);
}
private static native long getModifierMapping(long display);
private static native void freeModifierMapping(long modifier_map);
private static native int getMaxKeyPerMod(long modifier_map);
private static native int lookupModifierMap(long modifier_map, int index);
private static native long keycodeToKeySym(long display, int key_code);

private static native long openIM(long display);
private static native long openIM(long display, String locale_modifiers);
private static native long createIC(long xim, long window);
private static native void setupIMEventMask(long display, long window, long xic);
private static native ByteBuffer allocateComposeStatus();
Expand Down Expand Up @@ -204,10 +214,13 @@ private int lookupStringUnicode(long event_ptr, int[] translation_buffer) {
native_translation_buffer.compact();
char_buffer.flip();
int i = 0;
while (char_buffer.hasRemaining() && i < translation_buffer.length) {
translation_buffer[i++] = char_buffer.get();
// For lookuped multibyte string, convert all chars in the CharBuffer into a string,
// and return each characters.
for (char c: char_buffer.toString().toCharArray()) {
translation_buffer[i++] = (int)c;
}
char_buffer.compact();
// Because all bytes is converted to lookuped multibyte string, clear the CharBuffer.
char_buffer.clear();
return i;
}
private static native int utf8LookupString(long xic, long event_ptr, ByteBuffer buffer, int pos, int size);
Expand Down Expand Up @@ -320,7 +333,11 @@ void releaseAll() {
private void handleKeyEvent(long event_ptr, long millis, int event_type, int event_keycode, int event_state) {
int keycode = getKeycode(event_ptr, event_state);
byte key_state = getKeyState(event_type);
boolean repeat = key_state == key_down_buffer[keycode];
// To know whether key events are repeating or not,
// compare current keycode and keystate with previous keycode and keystate.
// But, the keycode is Keyborad.KEY_NONE whenever input method sends string,
// So Keyborad.KEY_NONE has nothing to do with events repetition.
boolean repeat = ((keycode != 0) && (key_state == key_down_buffer[keycode]));
if ( repeat && event_type == LinuxEvent.KeyRelease ) // This can happen for modifier keys after losing and regaining focus.
return;
key_down_buffer[keycode] = key_state;
Expand Down Expand Up @@ -362,4 +379,14 @@ public boolean filterEvent(LinuxEvent event) {
}
return false;
}

public void setICFocus() {
nSetICFocus(xic);
}
private static native void nSetICFocus(long xic);

public void unsetICFocus() {
nUnsetICFocus(xic);
}
private static native void nUnsetICFocus(long xic);
}
20 changes: 18 additions & 2 deletions src/native/linux/org_lwjgl_opengl_LinuxKeyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,11 @@ JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_LinuxKeyboard_keycodeToKeySym(JNIE
return key_sym;
}

JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_LinuxKeyboard_openIM(JNIEnv *env, jclass unused, jlong display_ptr) {
JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_LinuxKeyboard_openIM(JNIEnv *env, jclass unused, jlong display_ptr, jstring locale_modifiers_ptr) {
Display *disp = (Display *)(intptr_t)display_ptr;
XSetLocaleModifiers ("@im=none");
const char *locale_modifiers = GetStringNativeChars(env, locale_modifiers_ptr);

XSetLocaleModifiers(locale_modifiers);
XIM xim = XOpenIM(disp, NULL, NULL, NULL);
return (intptr_t)xim;
}
Expand Down Expand Up @@ -151,3 +153,17 @@ JNIEXPORT jint JNICALL Java_org_lwjgl_opengl_LinuxKeyboard_utf8LookupString(JNIE
positionBuffer(env, buffer_obj, num_bytes);
return status;
}

JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxKeyboard_nSetICFocus(JNIEnv *env, jclass unused, jlong xic_ptr) {
XIC xic = (XIC)(intptr_t)xic_ptr;

XSetICFocus(xic);
}

JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxKeyboard_nUnsetICFocus(JNIEnv *env, jclass unused, jlong xic_ptr) {
XIC xic = (XIC)(intptr_t)xic_ptr;

XUnsetICFocus(xic);
}