Skip to content

Commit

Permalink
support multibytes input from Input method on Linux (Xlib)
Browse files Browse the repository at this point in the history
  • Loading branch information
momokan committed Oct 3, 2014
1 parent b44f503 commit a7e3074
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 16 deletions.
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);
}


0 comments on commit a7e3074

Please sign in to comment.