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