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 **/
35typedef 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
43struct 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
63void
64skin_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
75void
76skin_keyboard_set_rotation( SkinKeyboard*     keyboard,
77                            SkinRotation      rotation )
78{
79    keyboard->rotation = (rotation & 3);
80}
81
82void
83skin_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
89void
90skin_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
96void
97skin_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
105void
106skin_keyboard_flush( SkinKeyboard*  kb )
107{
108    android_keycodes_flush(&kb->keycodes);
109}
110
111
112static void
113skin_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
123static LastKey*
124skin_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
137static void
138skin_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
154static void
155skin_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
173static void
174skin_keyboard_clear_last( SkinKeyboard*  keyboard )
175{
176    keyboard->last_count = 0;
177}
178
179static int
180skin_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
216static AndroidKeyCode
217skin_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 */
318static AndroidKeyCode
319skin_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
393static void
394skin_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
405int
406skin_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
413void
414skin_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
424void
425skin_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
505static SkinKeyboard*
506skin_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
532SkinKeyboard*
533skin_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
545void
546skin_keyboard_free( SkinKeyboard*  keyboard )
547{
548    if (keyboard) {
549        AFREE(keyboard);
550    }
551}
552