1 /* Copyright (C) 2007-2008 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 ** GNU General Public License for more details.
11 */
12 #include "android/skin/keyboard.h"
13 #include "android/utils/debug.h"
14 #include "android/utils/bufprint.h"
15 #include "android/utils/system.h"
16 #include "android/android.h"
17 #include "android/keycode-array.h"
18 #include "android/charmap.h"
19
20 #define DEBUG 1
21
22 #if DEBUG
23 # define D(...) VERBOSE_PRINT(keys,__VA_ARGS__)
24 #else
25 # define D(...) ((void)0)
26 #endif
27
28 #define DEFAULT_ANDROID_CHARMAP "qwerty2"
29
30 /** LAST PRESSED KEYS
31 ** a small buffer of last pressed keys, this is used to properly
32 ** implement the Unicode keyboard mode (SDL key up event always have
33 ** their .unicode field set to 0
34 **/
35 typedef struct {
36 int unicode; /* Unicode of last pressed key */
37 int sym; /* SDL key symbol value (e.g. SDLK_a) */
38 int mod; /* SDL key modifier value */
39 } LastKey;
40
41 #define MAX_LAST_KEYS 16
42
43 struct SkinKeyboard {
44 const AKeyCharmap* charmap;
45 SkinKeyset* kset;
46 char enabled;
47 char raw_keys;
48 char last_count;
49
50 SkinRotation rotation;
51
52 SkinKeyCommandFunc command_func;
53 void* command_opaque;
54 SkinKeyEventFunc press_func;
55 void* press_opaque;
56
57 LastKey last_keys[ MAX_LAST_KEYS ];
58
59 AKeycodeBuffer keycodes;
60 };
61
62
63 void
skin_keyboard_set_keyset(SkinKeyboard * keyboard,SkinKeyset * kset)64 skin_keyboard_set_keyset( SkinKeyboard* keyboard, SkinKeyset* kset )
65 {
66 if (kset == NULL)
67 return;
68 if (keyboard->kset && keyboard->kset != android_keyset) {
69 skin_keyset_free(keyboard->kset);
70 }
71 keyboard->kset = kset;
72 }
73
74
75 void
skin_keyboard_set_rotation(SkinKeyboard * keyboard,SkinRotation rotation)76 skin_keyboard_set_rotation( SkinKeyboard* keyboard,
77 SkinRotation rotation )
78 {
79 keyboard->rotation = (rotation & 3);
80 }
81
82 void
skin_keyboard_on_command(SkinKeyboard * keyboard,SkinKeyCommandFunc cmd_func,void * cmd_opaque)83 skin_keyboard_on_command( SkinKeyboard* keyboard, SkinKeyCommandFunc cmd_func, void* cmd_opaque )
84 {
85 keyboard->command_func = cmd_func;
86 keyboard->command_opaque = cmd_opaque;
87 }
88
89 void
skin_keyboard_on_key_press(SkinKeyboard * keyboard,SkinKeyEventFunc press_func,void * press_opaque)90 skin_keyboard_on_key_press( SkinKeyboard* keyboard, SkinKeyEventFunc press_func, void* press_opaque )
91 {
92 keyboard->press_func = press_func;
93 keyboard->press_opaque = press_opaque;
94 }
95
96 void
skin_keyboard_add_key_event(SkinKeyboard * kb,unsigned code,unsigned down)97 skin_keyboard_add_key_event( SkinKeyboard* kb,
98 unsigned code,
99 unsigned down )
100 {
101 android_keycodes_add_key_event(&kb->keycodes, code, down);
102 }
103
104
105 void
skin_keyboard_flush(SkinKeyboard * kb)106 skin_keyboard_flush( SkinKeyboard* kb )
107 {
108 android_keycodes_flush(&kb->keycodes);
109 }
110
111
112 static void
skin_keyboard_cmd(SkinKeyboard * keyboard,SkinKeyCommand command,int param)113 skin_keyboard_cmd( SkinKeyboard* keyboard,
114 SkinKeyCommand command,
115 int param )
116 {
117 if (keyboard->command_func) {
118 keyboard->command_func( keyboard->command_opaque, command, param );
119 }
120 }
121
122
123 static LastKey*
skin_keyboard_find_last(SkinKeyboard * keyboard,int sym)124 skin_keyboard_find_last( SkinKeyboard* keyboard,
125 int sym )
126 {
127 LastKey* k = keyboard->last_keys;
128 LastKey* end = k + keyboard->last_count;
129
130 for ( ; k < end; k++ ) {
131 if (k->sym == sym)
132 return k;
133 }
134 return NULL;
135 }
136
137 static void
skin_keyboard_add_last(SkinKeyboard * keyboard,int sym,int mod,int unicode)138 skin_keyboard_add_last( SkinKeyboard* keyboard,
139 int sym,
140 int mod,
141 int unicode )
142 {
143 LastKey* k = keyboard->last_keys + keyboard->last_count;
144
145 if (keyboard->last_count < MAX_LAST_KEYS) {
146 k->sym = sym;
147 k->mod = mod;
148 k->unicode = unicode;
149
150 keyboard->last_count += 1;
151 }
152 }
153
154 static void
skin_keyboard_remove_last(SkinKeyboard * keyboard,int sym)155 skin_keyboard_remove_last( SkinKeyboard* keyboard,
156 int sym )
157 {
158 LastKey* k = keyboard->last_keys;
159 LastKey* end = k + keyboard->last_count;
160
161 for ( ; k < end; k++ ) {
162 if (k->sym == sym) {
163 /* we don't need a sorted array, so place the last
164 * element in place at the position of the removed
165 * one... */
166 k[0] = end[-1];
167 keyboard->last_count -= 1;
168 break;
169 }
170 }
171 }
172
173 static void
skin_keyboard_clear_last(SkinKeyboard * keyboard)174 skin_keyboard_clear_last( SkinKeyboard* keyboard )
175 {
176 keyboard->last_count = 0;
177 }
178
179 static int
skin_keyboard_rotate_sym(SkinKeyboard * keyboard,int sym)180 skin_keyboard_rotate_sym( SkinKeyboard* keyboard,
181 int sym )
182 {
183 switch (keyboard->rotation) {
184 case SKIN_ROTATION_90:
185 switch (sym) {
186 case SDLK_LEFT: sym = SDLK_DOWN; break;
187 case SDLK_RIGHT: sym = SDLK_UP; break;
188 case SDLK_UP: sym = SDLK_LEFT; break;
189 case SDLK_DOWN: sym = SDLK_RIGHT; break;
190 }
191 break;
192
193 case SKIN_ROTATION_180:
194 switch (sym) {
195 case SDLK_LEFT: sym = SDLK_RIGHT; break;
196 case SDLK_RIGHT: sym = SDLK_LEFT; break;
197 case SDLK_UP: sym = SDLK_DOWN; break;
198 case SDLK_DOWN: sym = SDLK_UP; break;
199 }
200 break;
201
202 case SKIN_ROTATION_270:
203 switch (sym) {
204 case SDLK_LEFT: sym = SDLK_UP; break;
205 case SDLK_RIGHT: sym = SDLK_DOWN; break;
206 case SDLK_UP: sym = SDLK_RIGHT; break;
207 case SDLK_DOWN: sym = SDLK_LEFT; break;
208 }
209 break;
210
211 default: ;
212 }
213 return sym;
214 }
215
216 static AndroidKeyCode
skin_keyboard_key_to_code(SkinKeyboard * keyboard,unsigned sym,int mod,int down)217 skin_keyboard_key_to_code( SkinKeyboard* keyboard,
218 unsigned sym,
219 int mod,
220 int down )
221 {
222 AndroidKeyCode code = 0;
223 int mod0 = mod;
224 SkinKeyCommand command;
225
226 /* first, handle the arrow keys directly */
227 /* rotate them if necessary */
228 sym = skin_keyboard_rotate_sym(keyboard, sym);
229 mod &= (KMOD_CTRL | KMOD_ALT | KMOD_SHIFT);
230
231 switch (sym) {
232 case SDLK_LEFT: code = kKeyCodeDpadLeft; break;
233 case SDLK_RIGHT: code = kKeyCodeDpadRight; break;
234 case SDLK_UP: code = kKeyCodeDpadUp; break;
235 case SDLK_DOWN: code = kKeyCodeDpadDown; break;
236 default: ;
237 }
238
239 if (code != 0) {
240 D("handling arrow (sym=%d mod=%d)", sym, mod);
241 if (!keyboard->raw_keys) {
242 int doCapL, doCapR, doAltL, doAltR;
243
244 if (!down) {
245 LastKey* k = skin_keyboard_find_last(keyboard, sym);
246 if (k != NULL) {
247 mod = k->mod;
248 skin_keyboard_remove_last( keyboard, sym );
249 }
250 } else {
251 skin_keyboard_add_last( keyboard, sym, mod, 0);
252 }
253
254 doCapL = (mod & 0x7ff) & KMOD_LSHIFT;
255 doCapR = (mod & 0x7ff) & KMOD_RSHIFT;
256 doAltL = (mod & 0x7ff) & KMOD_LALT;
257 doAltR = (mod & 0x7ff) & KMOD_RALT;
258
259 if (down) {
260 if (doAltL) skin_keyboard_add_key_event( keyboard, kKeyCodeAltLeft, 1 );
261 if (doAltR) skin_keyboard_add_key_event( keyboard, kKeyCodeAltRight, 1 );
262 if (doCapL) skin_keyboard_add_key_event( keyboard, kKeyCodeCapLeft, 1 );
263 if (doCapR) skin_keyboard_add_key_event( keyboard, kKeyCodeCapRight, 1 );
264 }
265 skin_keyboard_add_key_event(keyboard, code, down);
266
267 if (!down) {
268 if (doCapR) skin_keyboard_add_key_event( keyboard, kKeyCodeCapRight, 0 );
269 if (doCapL) skin_keyboard_add_key_event( keyboard, kKeyCodeCapLeft, 0 );
270 if (doAltR) skin_keyboard_add_key_event( keyboard, kKeyCodeAltRight, 0 );
271 if (doAltL) skin_keyboard_add_key_event( keyboard, kKeyCodeAltLeft, 0 );
272 }
273 code = 0;
274 }
275 return code;
276 }
277
278 /* special case for keypad keys, ignore them here if numlock is on */
279 if ((mod0 & KMOD_NUM) != 0) {
280 switch (sym) {
281 case SDLK_KP0:
282 case SDLK_KP1:
283 case SDLK_KP2:
284 case SDLK_KP3:
285 case SDLK_KP4:
286 case SDLK_KP5:
287 case SDLK_KP6:
288 case SDLK_KP7:
289 case SDLK_KP8:
290 case SDLK_KP9:
291 case SDLK_KP_PLUS:
292 case SDLK_KP_MINUS:
293 case SDLK_KP_MULTIPLY:
294 case SDLK_KP_DIVIDE:
295 case SDLK_KP_EQUALS:
296 case SDLK_KP_PERIOD:
297 case SDLK_KP_ENTER:
298 return 0;
299 }
300 }
301
302 /* now try all keyset combos */
303 command = skin_keyset_get_command( keyboard->kset, sym, mod );
304 if (command != SKIN_KEY_COMMAND_NONE) {
305 D("handling command %s from (sym=%d, mod=%d, str=%s)",
306 skin_key_command_to_str(command), sym, mod, skin_key_symmod_to_str(sym,mod));
307 skin_keyboard_cmd( keyboard, command, down );
308 return 0;
309 }
310 D("could not handle (sym=%d, mod=%d, str=%s)", sym, mod,
311 skin_key_symmod_to_str(sym,mod));
312 return -1;
313 }
314
315 /* this gets called only if the reverse unicode mapping didn't work
316 * or wasn't used (when in raw keys mode)
317 */
318 static AndroidKeyCode
skin_keyboard_raw_key_to_code(SkinKeyboard * kb,unsigned sym,int down)319 skin_keyboard_raw_key_to_code(SkinKeyboard* kb, unsigned sym, int down)
320 {
321 switch(sym){
322 case SDLK_1: return kKeyCode1;
323 case SDLK_2: return kKeyCode2;
324 case SDLK_3: return kKeyCode3;
325 case SDLK_4: return kKeyCode4;
326 case SDLK_5: return kKeyCode5;
327 case SDLK_6: return kKeyCode6;
328 case SDLK_7: return kKeyCode7;
329 case SDLK_8: return kKeyCode8;
330 case SDLK_9: return kKeyCode9;
331 case SDLK_0: return kKeyCode0;
332
333 case SDLK_q: return kKeyCodeQ;
334 case SDLK_w: return kKeyCodeW;
335 case SDLK_e: return kKeyCodeE;
336 case SDLK_r: return kKeyCodeR;
337 case SDLK_t: return kKeyCodeT;
338 case SDLK_y: return kKeyCodeY;
339 case SDLK_u: return kKeyCodeU;
340 case SDLK_i: return kKeyCodeI;
341 case SDLK_o: return kKeyCodeO;
342 case SDLK_p: return kKeyCodeP;
343 case SDLK_a: return kKeyCodeA;
344 case SDLK_s: return kKeyCodeS;
345 case SDLK_d: return kKeyCodeD;
346 case SDLK_f: return kKeyCodeF;
347 case SDLK_g: return kKeyCodeG;
348 case SDLK_h: return kKeyCodeH;
349 case SDLK_j: return kKeyCodeJ;
350 case SDLK_k: return kKeyCodeK;
351 case SDLK_l: return kKeyCodeL;
352 case SDLK_z: return kKeyCodeZ;
353 case SDLK_x: return kKeyCodeX;
354 case SDLK_c: return kKeyCodeC;
355 case SDLK_v: return kKeyCodeV;
356 case SDLK_b: return kKeyCodeB;
357 case SDLK_n: return kKeyCodeN;
358 case SDLK_m: return kKeyCodeM;
359 case SDLK_COMMA: return kKeyCodeComma;
360 case SDLK_PERIOD: return kKeyCodePeriod;
361 case SDLK_SPACE: return kKeyCodeSpace;
362 case SDLK_SLASH: return kKeyCodeSlash;
363 case SDLK_RETURN: return kKeyCodeNewline;
364 case SDLK_BACKSPACE: return kKeyCodeDel;
365
366 /* these are qwerty keys not on a device keyboard */
367 case SDLK_TAB: return kKeyCodeTab;
368 case SDLK_BACKQUOTE: return kKeyCodeGrave;
369 case SDLK_MINUS: return kKeyCodeMinus;
370 case SDLK_EQUALS: return kKeyCodeEquals;
371 case SDLK_LEFTBRACKET: return kKeyCodeLeftBracket;
372 case SDLK_RIGHTBRACKET: return kKeyCodeRightBracket;
373 case SDLK_BACKSLASH: return kKeyCodeBackslash;
374 case SDLK_SEMICOLON: return kKeyCodeSemicolon;
375 case SDLK_QUOTE: return kKeyCodeApostrophe;
376
377 case SDLK_RSHIFT: return kKeyCodeCapRight;
378 case SDLK_LSHIFT: return kKeyCodeCapLeft;
379 case SDLK_RMETA: return kKeyCodeSym;
380 case SDLK_LMETA: return kKeyCodeSym;
381 case SDLK_RALT: return kKeyCodeAltRight;
382 case SDLK_LALT: return kKeyCodeAltLeft;
383 case SDLK_RCTRL: return kKeyCodeSym;
384 case SDLK_LCTRL: return kKeyCodeSym;
385
386 default:
387 /* fprintf(stderr,"* unknown sdl keysym %d *\n", sym); */
388 return -1;
389 }
390 }
391
392
393 static void
skin_keyboard_do_key_event(SkinKeyboard * kb,AndroidKeyCode code,int down)394 skin_keyboard_do_key_event( SkinKeyboard* kb,
395 AndroidKeyCode code,
396 int down )
397 {
398 if (kb->press_func) {
399 kb->press_func( kb->press_opaque, code, down );
400 }
401 skin_keyboard_add_key_event(kb, code, down);
402 }
403
404
405 int
skin_keyboard_process_unicode_event(SkinKeyboard * kb,unsigned int unicode,int down)406 skin_keyboard_process_unicode_event( SkinKeyboard* kb, unsigned int unicode, int down )
407 {
408 return android_charmap_reverse_map_unicode(kb->charmap, unicode, down,
409 &kb->keycodes);
410 }
411
412
413 void
skin_keyboard_enable(SkinKeyboard * keyboard,int enabled)414 skin_keyboard_enable( SkinKeyboard* keyboard,
415 int enabled )
416 {
417 keyboard->enabled = enabled;
418 if (enabled) {
419 SDL_EnableUNICODE(!keyboard->raw_keys);
420 SDL_EnableKeyRepeat(0,0);
421 }
422 }
423
424 void
skin_keyboard_process_event(SkinKeyboard * kb,SDL_Event * ev,int down)425 skin_keyboard_process_event( SkinKeyboard* kb, SDL_Event* ev, int down )
426 {
427 unsigned code;
428 int unicode = ev->key.keysym.unicode;
429 int sym = ev->key.keysym.sym;
430 int mod = ev->key.keysym.mod;
431
432 /* ignore key events if we're not enabled */
433 if (!kb->enabled) {
434 printf( "ignoring key event sym=%d mod=0x%x unicode=%d\n",
435 sym, mod, unicode );
436 return;
437 }
438
439 /* first, try the keyboard-mode-independent keys */
440 code = skin_keyboard_key_to_code( kb, sym, mod, down );
441 if (code == 0)
442 return;
443
444 if ((int)code > 0) {
445 skin_keyboard_do_key_event(kb, code, down);
446 skin_keyboard_flush(kb);
447 return;
448 }
449
450 /* Ctrl-K is used to switch between 'unicode' and 'raw' modes */
451 if (sym == SDLK_k)
452 {
453 int mod2 = mod & 0x7ff;
454
455 if ( mod2 == KMOD_LCTRL || mod2 == KMOD_RCTRL ) {
456 if (down) {
457 skin_keyboard_clear_last(kb);
458 kb->raw_keys = !kb->raw_keys;
459 SDL_EnableUNICODE(!kb->raw_keys);
460 D( "switching keyboard to %s mode", kb->raw_keys ? "raw" : "unicode" );
461 }
462 return;
463 }
464 }
465
466 if (!kb->raw_keys) {
467 /* ev->key.keysym.unicode is only valid on keydown events, and will be 0
468 * on the corresponding keyup ones, so remember the set of last pressed key
469 * syms to "undo" the job
470 */
471 if ( !down && unicode == 0 ) {
472 LastKey* k = skin_keyboard_find_last(kb, sym);
473 if (k != NULL) {
474 unicode = k->unicode;
475 skin_keyboard_remove_last(kb, sym);
476 }
477 }
478 }
479 if (!kb->raw_keys &&
480 skin_keyboard_process_unicode_event( kb, unicode, down ) > 0)
481 {
482 if (down)
483 skin_keyboard_add_last( kb, sym, mod, unicode );
484
485 skin_keyboard_flush( kb );
486 return;
487 }
488
489 code = skin_keyboard_raw_key_to_code( kb, sym, down );
490
491 if ( !kb->raw_keys &&
492 (code == kKeyCodeAltLeft || code == kKeyCodeAltRight ||
493 code == kKeyCodeCapLeft || code == kKeyCodeCapRight ||
494 code == kKeyCodeSym) )
495 return;
496
497 if (code == -1) {
498 D("ignoring keysym %d", sym );
499 } else if (code > 0) {
500 skin_keyboard_do_key_event(kb, code, down);
501 skin_keyboard_flush(kb);
502 }
503 }
504
505 static SkinKeyboard*
skin_keyboard_create_from_charmap_name(const char * charmap_name,int use_raw_keys)506 skin_keyboard_create_from_charmap_name(const char* charmap_name,
507 int use_raw_keys)
508 {
509 SkinKeyboard* kb;
510
511 ANEW0(kb);
512
513 kb->charmap = android_get_charmap_by_name(charmap_name);
514 if (!kb->charmap) {
515 // Charmap name was not found. Default to "qwerty2" */
516 kb->charmap = android_get_charmap_by_name(DEFAULT_ANDROID_CHARMAP);
517 fprintf(stderr, "### warning, skin requires unknown '%s' charmap, reverting to '%s'\n",
518 charmap_name, kb->charmap->name );
519 }
520 kb->raw_keys = use_raw_keys;
521 kb->enabled = 0;
522
523 /* add default keyset */
524 if (android_keyset)
525 kb->kset = android_keyset;
526 else
527 kb->kset = skin_keyset_new_from_text( skin_keyset_get_default() );
528
529 return kb;
530 }
531
532 SkinKeyboard*
skin_keyboard_create(const char * kcm_file_path,int use_raw_keys)533 skin_keyboard_create( const char* kcm_file_path, int use_raw_keys )
534 {
535 const char* charmap_name = DEFAULT_ANDROID_CHARMAP;
536 char cmap_buff[AKEYCHARMAP_NAME_SIZE];
537
538 if (kcm_file_path != NULL) {
539 kcm_extract_charmap_name(kcm_file_path, cmap_buff, sizeof cmap_buff);
540 charmap_name = cmap_buff;
541 }
542 return skin_keyboard_create_from_charmap_name(charmap_name, use_raw_keys);
543 }
544
545 void
skin_keyboard_free(SkinKeyboard * keyboard)546 skin_keyboard_free( SkinKeyboard* keyboard )
547 {
548 if (keyboard) {
549 AFREE(keyboard);
550 }
551 }
552