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