Hi On my keyboard Alt Gr is mapped to ISO_Level3_Shift. Using sdl it worked fine without any modification, but using vnc I was unable to use Alt Gr. I added a line on /usr/share/keymaps/modifiers refering to my key:
ISO_Level3_Shift 0xb8 But, unfortunately it didn't work.... Surfing on the qemu code, I realized that the ISO_Level3_Shitf wasn't declared on the vnc_keysym.h file. So I added this line to that file: {"ISO_Level3_Shift", 0xfe03}, /* ISO_Level3 And finally Alt Gr worked! I think that it should be a good idea to allow the user to add keysym manually by number and name. Instead of just name. This will allow to add keysyms with no qemu recompiling. this can be done very easilly. Just modfying get_keysym on keymaps.c I attach a pacth with this two improves. Regards and congratulations for a great program! PS: Please CC me -- Ricardo Ribalda http://www.ii.uam.es/~rribalda/
--- qemu-old/keymaps/es Sun Dec 12 18:56:30 2004 +++ qemu-new/keymaps/es Tue Aug 08 23:41:55 2006 @@ -103,3 +103,58 @@ underscore 0x35 shift dead_belowdot 0x35 altgr dead_abovedot 0x35 shift altgr + + +# Dead keys +aacute 0x1e dead_acute +Aacute 0x1e dead_acute shift +eacute 0x12 dead_acute +Eacute 0x12 dead_acute shift +iacute 0x17 dead_acute +Iacute 0x17 dead_acute shift +oacute 0x18 dead_acute +Oacute 0x18 dead_acute shift +uacute 0x16 dead_acute +Uacute 0x16 dead_acute shift +yacute 0x15 dead_acute +Yacute 0x15 dead_acute shift + +agrave 0x1e dead_grave +Agrave 0x1e dead_grave shift +egrave 0x12 dead_grave +Egrave 0x12 dead_grave shift +igrave 0x17 dead_grave +Igrave 0x17 dead_grave shift +ograve 0x18 dead_grave +Ograve 0x18 dead_grave shift +ugrave 0x16 dead_grave +Ugrave 0x16 dead_grave shift + +adiaeresis 0x1e dead_diaeresis +Adiaeresis 0x1e dead_diaeresis shift +ediaeresis 0x12 dead_diaeresis +Ediaeresis 0x12 dead_diaeresis shift +idiaeresis 0x17 dead_diaeresis +Idiaeresis 0x17 dead_diaeresis shift +odiaeresis 0x18 dead_diaeresis +Odiaeresis 0x18 dead_diaeresis shift +udiaeresis 0x16 dead_diaeresis +Udiaeresis 0x16 dead_diaeresis shift +ydiaeresis 0x15 dead_diaeresis + +acircumflex 0x1e dead_circumflex +Acircumflex 0x1e dead_circumflex shift +ecircumflex 0x12 dead_circumflex +Ecircumflex 0x12 dead_circumflex shift +icircumflex 0x17 dead_circumflex +Icircumflex 0x17 dead_circumflex shift +ocircumflex 0x18 dead_circumflex +Ocircumflex 0x18 dead_circumflex shift +ucircumflex 0x16 dead_circumflex +Ucircumflex 0x16 dead_circumflex shift + +acute 0x39 dead_acute +grave 0x39 dead_grave +diaeresis 0x39 dead_diaeresis +asciicircum 0x39 dead_circumflex + --- qemu-old/keymaps.c Sun Apr 30 23:28:35 2006 +++ qemu-new/keymaps.c Tue Aug 08 23:39:58 2006 @@ -22,6 +22,7 @@ * THE SOFTWARE. */ + static int get_keysym(const char *name) { name2keysym_t *p; @@ -32,13 +33,26 @@ return 0; } + +#define KEYMOD_SHIFT 0x01 +#define KEYMOD_CTRL 0x02 +#define KEYMOD_ALT 0x04 +#define KEYMOD_DEAD 0x08 + #define MAX_NORMAL_KEYCODE 512 #define MAX_EXTRA_COUNT 256 + typedef struct { - uint16_t keysym2keycode[MAX_NORMAL_KEYCODE]; + uint16_t keycode; + uint8_t keymod; + int deadsym; +} keydata_t; + +typedef struct { + keydata_t keysym2keycode[MAX_NORMAL_KEYCODE]; struct { int keysym; - uint16_t keycode; + keydata_t kdata; } keysym2keycode_extra[MAX_EXTRA_COUNT]; int extra_count; } kbd_layout_t; @@ -50,6 +64,7 @@ char file_name[1024]; char line[1024]; int len; + int upper; snprintf(file_name, sizeof(file_name), "%s/keymaps/%s", bios_dir, language); @@ -82,17 +97,61 @@ if (*end_of_keysym) { int keysym; *end_of_keysym = 0; + uint8_t keymod; + int deadsym; + + keymod = 0; + deadsym = 0; + upper = 0; +redo: + if (upper==1){ + char *c; + for(c=line;*c;c++) + *c=toupper(*c); + keymod |= KEYMOD_SHIFT; + upper++; + } keysym = get_keysym(line); if (keysym == 0) { // fprintf(stderr, "Warning: unknown keysym %s\n", line); } else { const char *rest = end_of_keysym + 1; int keycode = strtol(rest, NULL, 0); + char *modifier; + modifier = strtok (rest," "); + modifier = strtok (NULL," "); + while ( modifier != NULL) { + if (!strcmp(modifier, "shift")) { + keymod |= KEYMOD_SHIFT; + } else + if (!strcmp(modifier, "addupper")) { + upper++; + } else + if (!strcmp(modifier, "ctrl")) { + keymod |= KEYMOD_CTRL; + } else + if (!strcmp(modifier, "alt")) { + keymod |= KEYMOD_ALT; + } else + if (!strcmp(modifier, "altgr")) { + keymod |= KEYMOD_CTRL | KEYMOD_ALT; + } else + if (!strncmp(modifier, "dead_",5)) { + keymod |= KEYMOD_DEAD; + deadsym = get_keysym(modifier); + } + modifier = strtok (NULL," "); + } /* if(keycode&0x80) keycode=(keycode<<8)^0x80e0; */ if (keysym < MAX_NORMAL_KEYCODE) { //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode); - k->keysym2keycode[keysym] = keycode; + k->keysym2keycode[keysym]. + keycode = keycode; + k->keysym2keycode[keysym]. + keymod = keymod; + k->keysym2keycode[keysym]. + deadsym = deadsym; } else { if (k->extra_count >= MAX_EXTRA_COUNT) { fprintf(stderr, @@ -105,11 +164,18 @@ #endif k->keysym2keycode_extra[k->extra_count]. keysym = keysym; - k->keysym2keycode_extra[k->extra_count]. + k->keysym2keycode_extra[k->extra_count].kdata. keycode = keycode; + k->keysym2keycode_extra[k->extra_count].kdata. + keymod = keymod; + k->keysym2keycode_extra[k->extra_count].kdata. + deadsym = deadsym; + k->extra_count++; } } + if (upper==1) + goto redo; } } } @@ -123,14 +189,11 @@ return parse_keyboard_layout(language, 0); } -static int keysym2scancode(void *kbd_layout, int keysym) +static keydata_t *find_keysym(void *kbd_layout, int keysym) { kbd_layout_t *k = kbd_layout; if (keysym < MAX_NORMAL_KEYCODE) { - if (k->keysym2keycode[keysym] == 0) - fprintf(stderr, "Warning: no scancode found for keysym %d\n", - keysym); - return k->keysym2keycode[keysym]; + return &k->keysym2keycode[keysym]; } else { int i; #ifdef XK_ISO_Left_Tab @@ -138,8 +201,9 @@ keysym = XK_Tab; #endif for (i = 0; i < k->extra_count; i++) - if (k->keysym2keycode_extra[i].keysym == keysym) - return k->keysym2keycode_extra[i].keycode; + if (k->keysym2keycode_extra[i].keysym == keysym) { + return &k->keysym2keycode_extra[i].kdata; } - return 0; + } + return NULL; } --- qemu-old/sdl.c Tue Jun 13 14:03:53 2006 +++ qemu-new/sdl.c Tue Aug 08 23:40:08 2006 @@ -104,7 +104,11 @@ keysym = ev->keysym.sym; if (keysym == 0 && ev->keysym.scancode == 113) keysym = SDLK_MODE; - return keysym2scancode(kbd_layout, keysym); + keydata_t *eventdata = find_keysym(kbd_layout, keysym); + if (eventdata==NULL) + return 0; + else + return eventdata->keycode; } /* specific keyboard conversions from scan codes */ --- qemu-old/vnc.c Tue Jun 13 18:35:24 2006 +++ qemu-new/vnc.c Tue Aug 08 23:41:37 2006 @@ -31,6 +31,23 @@ #include "vnc_keysym.h" #include "keymaps.c" + +static uint8_t modifiers_state[2][256]; + +typedef struct { + int left; + int right; + int bit; +} modifier_t; + +static modifier_t test_modifier[]={ + {0x2a, 0x36, KEYMOD_SHIFT}, + {0x1d, 0x9d, KEYMOD_CTRL}, + {0x38, 0xb8, KEYMOD_ALT}, + {0,0,0}, +}; + + typedef struct Buffer { size_t capacity; @@ -699,12 +716,9 @@ } } -static void do_key_event(VncState *vs, int down, uint32_t sym) -{ - int keycode; - - keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF); +static void do_keycode(int keycode, int down) +{ if (keycode & 0x80) kbd_put_keycode(0xe0); if (down) @@ -713,11 +727,88 @@ kbd_put_keycode(keycode | 0x80); } +static void do_modifier(int keycode, int down, int level) +{ + do_keycode(keycode,down); + modifiers_state[level][keycode] = down; + if (level==0) { + modifiers_state[1][keycode] = down; + } +} + +static void set_modifiers(uint8_t reqstate,int down,int full) +{ + modifier_t *m; + for(m=test_modifier; m->bit; m++) { + int requested = reqstate & m->bit; + /* Release unwanted modifiers */ + if (!down || full) { + if (modifiers_state[1][m->left] && !requested) + do_modifier(m->left,0,1); + if (modifiers_state[1][m->right] && !requested) + do_modifier(m->right,0,1); + } + /* Press desired modifiers */ + if (down || full) { + int already_set = modifiers_state[1][m->left] | modifiers_state[1][m->right]; + if (!already_set && requested) + do_modifier(m->left,1,1); + } + } +} + +static void restore_modifiers() +{ + /* Restore modifiers from reference */ + modifier_t *m; + for(m=test_modifier; m->bit; m++) { + if (modifiers_state[0][m->left] != modifiers_state[1][m->left]) + do_modifier(m->left,modifiers_state[0][m->left],0); + if (modifiers_state[0][m->right] != modifiers_state[1][m->right]) + do_modifier(m->right,modifiers_state[0][m->right],0); + } +} + static void key_event(VncState *vs, int down, uint32_t sym) { - if (sym >= 'A' && sym <= 'Z') - sym = sym - 'A' + 'a'; - do_key_event(vs, down, sym); + keydata_t *eventdata; + eventdata = find_keysym(vs->kbd_layout, sym & 0xFFFF); + if (eventdata==NULL) + return; + switch(eventdata->keycode) { + case 0x2a: /* Left Shift */ + case 0x36: /* Right Shift */ + case 0x1d: /* Left CTRL */ + case 0x9d: /* Right CTRL */ + case 0x38: /* Left ALT */ + case 0xb8: /* Right ALT */ + do_modifier(eventdata->keycode, down, 0); + return; + case 0x3a: /* caps lock */ + /* TODO: handle properly caps lock + shift combinations */ + return; + break; + } + + if (down) { + /* Send deadkey */ + if (eventdata->keymod & KEYMOD_DEAD) { + keydata_t *deaddata; + deaddata = find_keysym(vs->kbd_layout, eventdata->deadsym); + if (deaddata!=NULL) { + set_modifiers(deaddata->keymod,0,1); + do_keycode(deaddata->keycode,1); + do_keycode(deaddata->keycode,0); + restore_modifiers(); + } + } + set_modifiers(eventdata->keymod,1,0); + } + + do_keycode(eventdata->keycode,down); + + if (!down) + restore_modifiers(); } static void framebuffer_update_request(VncState *vs, int incremental, --- qemu-old/vnc_keysym.h Sun Apr 30 23:28:36 2006 +++ qemu-new/vnc_keysym.h Tue Aug 08 23:39:35 2006 @@ -247,6 +247,33 @@ {"F14", 0xffcb}, /* XK_F14 */ {"F15", 0xffcc}, /* XK_F15 */ {"Sys_Req", 0xff15}, /* XK_Sys_Req */ +{"KP_Space", 0xff80}, /* XK_KP_Space */ +{"KP_Tab", 0xff89}, /* XK_KP_Tab */ +{"KP_Enter", 0xff8d}, /* XK_KP_Enter */ +{"KP_F1", 0xff91}, /* XK_KP_F1 */ +{"KP_F2", 0xff92}, /* XK_KP_F2 */ +{"KP_F3", 0xff93}, /* XK_KP_F3 */ +{"KP_F4", 0xff94}, /* XK_KP_F4 */ +{"KP_Home", 0xff95}, /* XK_KP_Home */ +{"KP_Left", 0xff96}, /* XK_KP_Left */ +{"KP_Up", 0xff97}, /* XK_KP_Up */ +{"KP_Right", 0xff98}, /* XK_KP_Right */ +{"KP_Down", 0xff99}, /* XK_KP_Down */ +{"KP_Prior", 0xff9a}, /* XK_KP_Prior */ +{"KP_Page_Up", 0xff9a}, /* XK_KP_Page_Up */ +{"KP_Next", 0xff9b}, /* XK_KP_Next */ +{"KP_Page_Down", 0xff9b}, /* XK_KP_Page_Down */ +{"KP_End", 0xff9c}, /* XK_KP_End */ +{"KP_Begin", 0xff9d}, /* XK_KP_Begin */ +{"KP_Insert", 0xff9e}, /* XK_KP_Insert */ +{"KP_Delete", 0xff9f}, /* XK_KP_Delete */ +{"KP_Equal", 0xffbd}, /* XK_KP_Equal */ +{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */ +{"KP_Add", 0xffab}, /* XK_KP_Add */ +{"KP_Separator", 0xffac}, /* XK_KP_Separator */ +{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */ +{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ +{"KP_Divide", 0xffaf}, /* XK_KP_Divide */ {"KP_0", 0xffb0}, /* XK_KP_0 */ {"KP_1", 0xffb1}, /* XK_KP_1 */ {"KP_2", 0xffb2}, /* XK_KP_2 */ @@ -257,13 +284,6 @@ {"KP_7", 0xffb7}, /* XK_KP_7 */ {"KP_8", 0xffb8}, /* XK_KP_8 */ {"KP_9", 0xffb9}, /* XK_KP_9 */ -{"KP_Add", 0xffab}, /* XK_KP_Add */ -{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ -{"KP_Divide", 0xffaf}, /* XK_KP_Divide */ -{"KP_Enter", 0xff8d}, /* XK_KP_Enter */ -{"KP_Equal", 0xffbd}, /* XK_KP_Equal */ -{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */ -{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */ {"help", 0xff6a}, /* XK_Help */ {"Menu", 0xff67}, /* XK_Menu */ {"Print", 0xff61}, /* XK_Print */ @@ -271,5 +291,27 @@ {"Num_Lock", 0xff7f}, /* XK_Num_Lock */ {"Pause", 0xff13}, /* XK_Pause */ {"Escape", 0xff1b}, /* XK_Escape */ + +/* dead keys */ +{"dead_grave", 0xfe50}, /* XK_dead_grave */ +{"dead_acute", 0xfe51}, /* XK_dead_acute */ +{"dead_circumflex", 0xfe52}, /* XK_dead_circumflex */ +{"dead_tilde", 0xfe53}, /* XK_dead_tilde */ +{"dead_macron", 0xfe54}, /* XK_dead_macron */ +{"dead_breve", 0xfe55}, /* XK_dead_breve */ +{"dead_abovedot", 0xfe56}, /* XK_dead_abovedot */ +{"dead_diaeresis", 0xfe57}, /* XK_dead_diaeresis */ +{"dead_abovering", 0xfe58}, /* XK_dead_abovering */ +{"dead_doubleacute", 0xfe59}, /* XK_dead_doubleacute */ +{"dead_caron", 0xfe5a}, /* XK_dead_caron */ +{"dead_cedilla", 0xfe5b}, /* XK_dead_cedilla */ +{"dead_ogonek", 0xfe5c}, /* XK_dead_ogonek */ +{"dead_iota", 0xfe5d}, /* XK_dead_iota */ +{"dead_voiced_sound", 0xfe5e}, /* XK_dead_voiced_sound */ +{"dead_semivoiced_sound", 0xfe5f}, /* XK_dead_semivoiced_sound */ +{"dead_belowdot", 0xfe60}, /* XK_dead_belowdot */ +{"dead_hook", 0xfe61}, /* XK_dead_hook */ +{"dead_horn", 0xfe62}, /* XK_dead_horn */ + {0,0}, };