1 /* 2 * Copyright (c) 2002-2008 LWJGL Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'LWJGL' nor the names of 17 * its contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.lwjgl.opengl; 33 34 /** 35 * @author elias_naur 36 */ 37 38 import java.nio.ByteBuffer; 39 import java.nio.CharBuffer; 40 import java.nio.charset.Charset; 41 import java.nio.charset.CharsetDecoder; 42 43 import org.lwjgl.BufferUtils; 44 import org.lwjgl.LWJGLUtil; 45 import org.lwjgl.input.Keyboard; 46 47 final class LinuxKeyboard { 48 private static final int LockMapIndex = 1; 49 private static final long NoSymbol = 0; 50 private static final long ShiftMask = 1 << 0; 51 private static final long LockMask = 1 << 1; 52 private static final int XLookupChars = 2; 53 private static final int XLookupBoth = 4; 54 55 private static final int KEYBOARD_BUFFER_SIZE = 50; 56 57 private final long xim; 58 private final long xic; 59 60 private final int numlock_mask; 61 private final int modeswitch_mask; 62 private final int caps_lock_mask; 63 private final int shift_lock_mask; 64 65 private final ByteBuffer compose_status; 66 67 private final byte[] key_down_buffer = new byte[Keyboard.KEYBOARD_SIZE]; 68 private final EventQueue event_queue = new EventQueue(Keyboard.EVENT_SIZE); 69 70 private final ByteBuffer tmp_event = ByteBuffer.allocate(Keyboard.EVENT_SIZE); 71 private final int[] temp_translation_buffer = new int[KEYBOARD_BUFFER_SIZE]; 72 private final ByteBuffer native_translation_buffer = BufferUtils.createByteBuffer(KEYBOARD_BUFFER_SIZE); 73 private final CharsetDecoder utf8_decoder = Charset.forName("UTF-8").newDecoder(); 74 private final CharBuffer char_buffer = CharBuffer.allocate(KEYBOARD_BUFFER_SIZE); 75 76 // Deferred key released event, to detect key repeat 77 private boolean has_deferred_event; 78 private int deferred_keycode; 79 private int deferred_event_keycode; 80 private long deferred_nanos; 81 private byte deferred_key_state; 82 LinuxKeyboard(long display, long window)83 LinuxKeyboard(long display, long window) { 84 long modifier_map = getModifierMapping(display); 85 int tmp_numlock_mask = 0; 86 int tmp_modeswitch_mask = 0; 87 int tmp_caps_lock_mask = 0; 88 int tmp_shift_lock_mask = 0; 89 if (modifier_map != 0) { 90 int max_keypermod = getMaxKeyPerMod(modifier_map); 91 // Find modifier masks 92 int i, j; 93 for (i = 0; i < 8; i++) { 94 for (j = 0; j < max_keypermod; j++) { 95 int key_code = lookupModifierMap(modifier_map, i*max_keypermod + j); 96 int key_sym = (int)keycodeToKeySym(display, key_code); 97 int mask = 1 << i; 98 switch (key_sym) { 99 case LinuxKeycodes.XK_Num_Lock: 100 tmp_numlock_mask |= mask; 101 break; 102 case LinuxKeycodes.XK_Mode_switch: 103 tmp_modeswitch_mask |= mask; 104 break; 105 case LinuxKeycodes.XK_Caps_Lock: 106 if (i == LockMapIndex) { 107 tmp_caps_lock_mask = mask; 108 tmp_shift_lock_mask = 0; 109 } 110 break; 111 case LinuxKeycodes.XK_Shift_Lock: 112 if (i == LockMapIndex && tmp_caps_lock_mask == 0) 113 tmp_shift_lock_mask = mask; 114 break; 115 default: 116 break; 117 } 118 } 119 } 120 freeModifierMapping(modifier_map); 121 } 122 numlock_mask = tmp_numlock_mask; 123 modeswitch_mask = tmp_modeswitch_mask; 124 caps_lock_mask = tmp_caps_lock_mask; 125 shift_lock_mask = tmp_shift_lock_mask; 126 setDetectableKeyRepeat(display, true); 127 xim = openIM(display); 128 if (xim != 0) { 129 xic = createIC(xim, window); 130 if (xic != 0) { 131 setupIMEventMask(display, window, xic); 132 } else { 133 destroy(display); 134 } 135 } else { 136 xic = 0; 137 } 138 compose_status = allocateComposeStatus(); 139 } getModifierMapping(long display)140 private static native long getModifierMapping(long display); freeModifierMapping(long modifier_map)141 private static native void freeModifierMapping(long modifier_map); getMaxKeyPerMod(long modifier_map)142 private static native int getMaxKeyPerMod(long modifier_map); lookupModifierMap(long modifier_map, int index)143 private static native int lookupModifierMap(long modifier_map, int index); keycodeToKeySym(long display, int key_code)144 private static native long keycodeToKeySym(long display, int key_code); 145 openIM(long display)146 private static native long openIM(long display); createIC(long xim, long window)147 private static native long createIC(long xim, long window); setupIMEventMask(long display, long window, long xic)148 private static native void setupIMEventMask(long display, long window, long xic); allocateComposeStatus()149 private static native ByteBuffer allocateComposeStatus(); 150 setDetectableKeyRepeat(long display, boolean enabled)151 private static void setDetectableKeyRepeat(long display, boolean enabled) { 152 boolean success = nSetDetectableKeyRepeat(display, enabled); 153 if (!success) 154 LWJGLUtil.log("Failed to set detectable key repeat to " + enabled); 155 } nSetDetectableKeyRepeat(long display, boolean enabled)156 private static native boolean nSetDetectableKeyRepeat(long display, boolean enabled); 157 destroy(long display)158 public void destroy(long display) { 159 if (xic != 0) 160 destroyIC(xic); 161 if (xim != 0) 162 closeIM(xim); 163 setDetectableKeyRepeat(display, false); 164 } destroyIC(long xic)165 private static native void destroyIC(long xic); closeIM(long xim)166 private static native void closeIM(long xim); 167 read(ByteBuffer buffer)168 public void read(ByteBuffer buffer) { 169 flushDeferredEvent(); 170 event_queue.copyEvents(buffer); 171 } 172 poll(ByteBuffer keyDownBuffer)173 public void poll(ByteBuffer keyDownBuffer) { 174 flushDeferredEvent(); 175 int old_position = keyDownBuffer.position(); 176 keyDownBuffer.put(key_down_buffer); 177 keyDownBuffer.position(old_position); 178 } 179 putKeyboardEvent(int keycode, byte state, int ch, long nanos, boolean repeat)180 private void putKeyboardEvent(int keycode, byte state, int ch, long nanos, boolean repeat) { 181 tmp_event.clear(); 182 tmp_event.putInt(keycode).put(state).putInt(ch).putLong(nanos).put(repeat ? (byte)1 : (byte)0); 183 tmp_event.flip(); 184 event_queue.putEvent(tmp_event); 185 } 186 lookupStringISO88591(long event_ptr, int[] translation_buffer)187 private int lookupStringISO88591(long event_ptr, int[] translation_buffer) { 188 int i; 189 190 int num_chars = lookupString(event_ptr, native_translation_buffer, compose_status); 191 for (i = 0; i < num_chars; i++) { 192 translation_buffer[i] = ((int)native_translation_buffer.get(i)) & 0xff; 193 } 194 return num_chars; 195 } lookupString(long event_ptr, ByteBuffer buffer, ByteBuffer compose_status)196 private static native int lookupString(long event_ptr, ByteBuffer buffer, ByteBuffer compose_status); 197 lookupStringUnicode(long event_ptr, int[] translation_buffer)198 private int lookupStringUnicode(long event_ptr, int[] translation_buffer) { 199 int status = utf8LookupString(xic, event_ptr, native_translation_buffer, native_translation_buffer.position(), native_translation_buffer.remaining()); 200 if (status != XLookupChars && status != XLookupBoth) 201 return 0; 202 native_translation_buffer.flip(); 203 utf8_decoder.decode(native_translation_buffer, char_buffer, true); 204 native_translation_buffer.compact(); 205 char_buffer.flip(); 206 int i = 0; 207 while (char_buffer.hasRemaining() && i < translation_buffer.length) { 208 translation_buffer[i++] = char_buffer.get(); 209 } 210 char_buffer.compact(); 211 return i; 212 } utf8LookupString(long xic, long event_ptr, ByteBuffer buffer, int pos, int size)213 private static native int utf8LookupString(long xic, long event_ptr, ByteBuffer buffer, int pos, int size); 214 lookupString(long event_ptr, int[] translation_buffer)215 private int lookupString(long event_ptr, int[] translation_buffer) { 216 if (xic != 0) { 217 return lookupStringUnicode(event_ptr, translation_buffer); 218 } else 219 return lookupStringISO88591(event_ptr, translation_buffer); 220 } 221 translateEvent(long event_ptr, int keycode, byte key_state, long nanos, boolean repeat)222 private void translateEvent(long event_ptr, int keycode, byte key_state, long nanos, boolean repeat) { 223 int num_chars, i; 224 int ch; 225 226 num_chars = lookupString(event_ptr, temp_translation_buffer); 227 if (num_chars > 0) { 228 ch = temp_translation_buffer[0]; 229 putKeyboardEvent(keycode, key_state, ch, nanos, repeat); 230 for (i = 1; i < num_chars; i++) { 231 ch = temp_translation_buffer[i]; 232 putKeyboardEvent(0, (byte)0, ch, nanos, repeat); 233 } 234 } else { 235 putKeyboardEvent(keycode, key_state, 0, nanos, repeat); 236 } 237 } 238 isKeypadKeysym(long keysym)239 private static boolean isKeypadKeysym(long keysym) { 240 return (0xFF80 <= keysym && keysym <= 0xFFBD) || 241 (0x11000000 <= keysym && keysym <= 0x1100FFFF); 242 } 243 isNoSymbolOrVendorSpecific(long keysym)244 private static boolean isNoSymbolOrVendorSpecific(long keysym) { 245 return keysym == NoSymbol || (keysym & (1 << 28)) != 0; 246 } 247 getKeySym(long event_ptr, int group, int index)248 private static long getKeySym(long event_ptr, int group, int index) { 249 long keysym = lookupKeysym(event_ptr, group*2 + index); 250 if (isNoSymbolOrVendorSpecific(keysym) && index == 1) { 251 keysym = lookupKeysym(event_ptr, group*2 + 0); 252 } 253 if (isNoSymbolOrVendorSpecific(keysym) && group == 1) 254 keysym = getKeySym(event_ptr, 0, index); 255 return keysym; 256 } lookupKeysym(long event_ptr, int index)257 private static native long lookupKeysym(long event_ptr, int index); toUpper(long keysym)258 private static native long toUpper(long keysym); 259 getKeycode(long event_ptr, int event_state)260 private int getKeycode(long event_ptr, int event_state) { 261 boolean shift = (event_state & (ShiftMask | shift_lock_mask)) != 0; 262 int group = (event_state & modeswitch_mask) != 0 ? 1 : 0; 263 long keysym; 264 if ((event_state & numlock_mask) != 0 && isKeypadKeysym(keysym = getKeySym(event_ptr, group, 1))) { 265 if ( shift ) 266 keysym = getKeySym(event_ptr, group, 0); 267 } else { 268 keysym = getKeySym(event_ptr, group, 0); 269 if ( shift ^ ((event_state & caps_lock_mask) != 0) ) 270 keysym = toUpper(keysym); 271 } 272 return LinuxKeycodes.mapKeySymToLWJGLKeyCode(keysym); 273 } 274 getKeyState(int event_type)275 private static byte getKeyState(int event_type) { 276 switch (event_type) { 277 case LinuxEvent.KeyPress: 278 return 1; 279 case LinuxEvent.KeyRelease: 280 return 0; 281 default: 282 throw new IllegalArgumentException("Unknown event_type: " + event_type); 283 } 284 } 285 286 /** This is called when the window loses focus: we release all currently pressed keys. */ releaseAll()287 void releaseAll() { 288 for ( int i = 0; i < key_down_buffer.length; i++ ) { 289 if ( key_down_buffer[i] != 0 ) { 290 key_down_buffer[i] = 0; 291 putKeyboardEvent(i, (byte)0, 0, 0L, false); 292 } 293 } 294 } 295 handleKeyEvent(long event_ptr, long millis, int event_type, int event_keycode, int event_state)296 private void handleKeyEvent(long event_ptr, long millis, int event_type, int event_keycode, int event_state) { 297 int keycode = getKeycode(event_ptr, event_state); 298 byte key_state = getKeyState(event_type); 299 boolean repeat = key_state == key_down_buffer[keycode]; 300 if ( repeat && event_type == LinuxEvent.KeyRelease ) // This can happen for modifier keys after losing and regaining focus. 301 return; 302 key_down_buffer[keycode] = key_state; 303 long nanos = millis*1000000; 304 if (event_type == LinuxEvent.KeyPress) { 305 if (has_deferred_event) { 306 if (nanos == deferred_nanos && event_keycode == deferred_event_keycode) { 307 has_deferred_event = false; 308 repeat = true; // Repeated event 309 } else 310 flushDeferredEvent(); 311 } 312 translateEvent(event_ptr, keycode, key_state, nanos, repeat); 313 } else { 314 flushDeferredEvent(); 315 has_deferred_event = true; 316 deferred_keycode = keycode; 317 deferred_event_keycode = event_keycode; 318 deferred_nanos = nanos; 319 deferred_key_state = key_state; 320 } 321 } 322 flushDeferredEvent()323 private void flushDeferredEvent() { 324 if (has_deferred_event) { 325 putKeyboardEvent(deferred_keycode, deferred_key_state, 0, deferred_nanos, false); 326 has_deferred_event = false; 327 } 328 } 329 filterEvent(LinuxEvent event)330 public boolean filterEvent(LinuxEvent event) { 331 switch (event.getType()) { 332 case LinuxEvent.KeyPress: /* Fall through */ 333 case LinuxEvent.KeyRelease: 334 handleKeyEvent(event.getKeyAddress(), event.getKeyTime(), event.getKeyType(), event.getKeyKeyCode(), event.getKeyState()); 335 return true; 336 default: 337 break; 338 } 339 return false; 340 } 341 } 342