keyboard.c revision 71bb14f826775867d16e7d382cfdc48e6ca46ccb
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 **/
34typedef 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
42struct 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
62void
63skin_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
74const char*
75skin_keyboard_charmap_name( SkinKeyboard*  keyboard )
76{
77    if (keyboard && keyboard->charmap)
78        return keyboard->charmap->name;
79
80    return "qwerty";
81}
82
83void
84skin_keyboard_set_rotation( SkinKeyboard*     keyboard,
85                            SkinRotation      rotation )
86{
87    keyboard->rotation = (rotation & 3);
88}
89
90void
91skin_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
97void
98skin_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
104void
105skin_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
113void
114skin_keyboard_flush( SkinKeyboard*  kb )
115{
116    android_keycodes_flush(&kb->keycodes);
117}
118
119
120static void
121skin_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
131static LastKey*
132skin_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
145static void
146skin_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
162static void
163skin_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
181static void
182skin_keyboard_clear_last( SkinKeyboard*  keyboard )
183{
184    keyboard->last_count = 0;
185}
186
187static int
188skin_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
224static AndroidKeyCode
225skin_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 */
326static AndroidKeyCode
327skin_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
401static void
402skin_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
413int
414skin_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
421void
422skin_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
432void
433skin_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
513static SkinKeyboard*
514skin_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
540SkinKeyboard*
541skin_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
551SkinKeyboard*
552skin_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
560void
561skin_keyboard_free( SkinKeyboard*  keyboard )
562{
563    if (keyboard) {
564        AFREE(keyboard);
565    }
566}
567