diff --git a/engine/client/client.h b/engine/client/client.h index 648e9183b..1c3effebb 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1269,6 +1269,7 @@ int Master_FindBestRoute(char *server, char *out, size_t outsize, int *directcos float CL_KeyState (kbutton_t *key, int pnum, qboolean noslowstart); const char *Key_KeynumToString (int keynum, int modifier); +const char *Key_KeynumToLocalString (int keynum, int modifier); int Key_StringToKeynum (const char *str, int *modifier); const char *Key_GetBinding(int keynum, int bindmap, int modifier); void Key_GetBindMap(int *bindmaps); diff --git a/engine/client/in_win.c b/engine/client/in_win.c index 04e38c25f..efb0fe83f 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -2413,6 +2413,28 @@ void INS_TranslateKeyEvent(WPARAM wParam, LPARAM lParam, qboolean down, int qdev IN_KeyEvent(qdeviceid, down, qcode, unicode); } +qboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize) +{ + int i; + *buf = 0; //assume failure + for (i = 0; i < countof(scantokey); i++) + { + if (!scantokey[i]) + continue; //not a vkey that quake understands + if (scantokey[i] == qkey) + { + wchar_t tmpbuf[64]; + if (GetKeyNameTextW(i<<16, tmpbuf, sizeof(tmpbuf))) + { + narrowen(buf, bufsize, tmpbuf); //yay for utf-8 + return true; + } + break; + } + } + return false; +} + #ifndef APPCOMMAND_BROWSER_BACKWARD //added in win2k (but was probably used before that too) #define APPCOMMAND_BROWSER_BACKWARD 1 diff --git a/engine/client/input.h b/engine/client/input.h index e29920ed5..7cdbf0446 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -68,6 +68,7 @@ void INS_Rumble(int joy, quint16_t amp_low, quint16_t amp_high, quint32_t durati void INS_RumbleTriggers(int joy, quint16_t left, quint16_t right, quint32_t duration); void INS_SetLEDColor(int id, vec3_t color); void INS_SetTriggerFX(int id, const void *data, size_t size); +qboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize); //returns a name for the key, according to their keyboard layout AND system language(hopefully), or false on unsupported/error. result may change at any time (eg: tap alt+shift on windows) #define DEVID_UNSET ~0u diff --git a/engine/client/keys.c b/engine/client/keys.c index bd7c83400..8fe45b37f 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -196,6 +196,8 @@ keyname_t keynames[] = {"MOUSE10", K_MOUSE10}, {"MWHEELUP", K_MWHEELUP}, {"MWHEELDOWN", K_MWHEELDOWN}, + {"MWHEELLEFT", K_MWHEELLEFT}, + {"MWHEELRIGHT", K_MWHEELRIGHT}, {"LWIN", K_LWIN}, //windows name {"RWIN", K_RWIN}, //windows name @@ -2439,6 +2441,39 @@ const char *Key_KeynumToString (int keynum, int modifier) } } +const char *Key_KeynumToLocalString (int keynum, int modifier) +{ + const char *r; +#if defined(_WIN32) || (defined(__linux__)&&!defined(NO_X11)) //not defined in all targets yet... + static char tmp[64]; + if (INS_KeyToLocalName(keynum, tmp, sizeof(tmp))) + r = tmp; + else +#endif + r = Key_KeynumToStringRaw(keynum); + if (r[0] == '<' && r[1]) + modifier = 0; //would be too weird. + switch(modifier) + { + case KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT: + return va("Ctrl+Alt+Shift+%s", r); + case KEY_MODIFIER_ALT|KEY_MODIFIER_SHIFT: + return va("Alt+Shift+%s", r); + case KEY_MODIFIER_CTRL|KEY_MODIFIER_SHIFT: + return va("Ctrl+Shift+%s", r); + case KEY_MODIFIER_CTRL|KEY_MODIFIER_ALT: + return va("Ctrl+Alt+%s", r); + case KEY_MODIFIER_CTRL: + return va("Ctrl+%s", r); + case KEY_MODIFIER_ALT: + return va("Alt+%s", r); + case KEY_MODIFIER_SHIFT: + return va("Shift+%s", r); + default: + return r; //no modifier or a bindmap + } +} + /* =================== Key_SetBinding diff --git a/engine/client/keys.h b/engine/client/keys.h index df59aeded..f8e126c20 100644 --- a/engine/client/keys.h +++ b/engine/client/keys.h @@ -104,11 +104,11 @@ typedef enum { K_KP_STAR, K_KP_EQUALS, - K_MOUSE1, - K_MOUSE2, - K_MOUSE3, - K_MOUSE4, - K_MOUSE5, + K_MOUSE1, //aka left + K_MOUSE2, //aka right + K_MOUSE3, //aka middle + K_MOUSE4, //aka back + K_MOUSE5, //aka forward K_MWHEELDOWN, K_MWHEELUP, @@ -210,6 +210,10 @@ typedef enum { K_MOUSE9, K_MOUSE10, + /*FIXME*/ +#define K_MWHEELLEFT K_MOUSE9 +#define K_MWHEELRIGHT K_MOUSE10 + /* spare joystick button presses */ K_JOY_UP, K_JOY_DOWN, diff --git a/engine/client/m_items.c b/engine/client/m_items.c index 75f6284b8..6eefca967 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -813,7 +813,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men Draw_FunString (x + 8, y, "or"); x += 32; } - keyname = Key_KeynumToString (keys[j], keymods[j]); + keyname = Key_KeynumToLocalString (keys[j], keymods[j]); Draw_FunString (x, y, keyname); x += strlen(keyname) * 8; } @@ -2085,7 +2085,8 @@ void M_Complex_Key(emenu_t *currentmenu, int key, int unicode) switch(key) { - case K_MOUSE2: + case K_MOUSE2: //right + case K_MOUSE4: //back case K_ESCAPE: case K_GP_BACK: //remove @@ -2190,7 +2191,6 @@ void M_Complex_Key(emenu_t *currentmenu, int key, int unicode) } case K_MOUSE1: case K_MOUSE3: - case K_MOUSE4: case K_MOUSE5: case K_MOUSE6: case K_MOUSE7: diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 8c62a2c14..338ae4e18 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -774,7 +774,7 @@ static qboolean SL_Key (int key, emenu_t *menu) serverinfo_t *server = selectedserver.inuse?Master_InfoForServer(&selectedserver.adr, selectedserver.brokerid):NULL; qboolean ctrldown = keydown[K_LCTRL] || keydown[K_RCTRL]; - if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2) + if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2 || key == K_MOUSE4) { serverpreview = SVPV_NO; return true; diff --git a/engine/client/menu.c b/engine/client/menu.c index e9f251940..e254b1d14 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -487,7 +487,7 @@ static qboolean Prompt_MenuKeyEvent(struct menu_s *gm, qboolean isdown, unsigned } return true; } - else if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2) + else if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2 || key == K_MOUSE4) action = PROMPT_CANCEL; else if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1 || key == K_GP_A) { @@ -1070,6 +1070,7 @@ qboolean M_Help_Key (int key, emenu_t *m) case K_ESCAPE: case K_GP_BACK: case K_MOUSE2: + case K_MOUSE4: M_RemoveMenu(m); return true; diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 2f3a372f1..7102049aa 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -218,6 +218,9 @@ static struct int (*pXGrabServer)(Display *display); int (*pXUngrabServer)(Display *display); + char *(*pXKeysymToString)(KeySym); + KeySym *(*pXGetKeyboardMapping)(Display *display, KeyCode first_keycode, int keycode_count, int *keysyms_per_keycode_return); + #define XI_RESOURCENAME "FTEQW" #define XI_RESOURCECLASS "FTEQW" char *(*pXSetLocaleModifiers)(char *modifier_list); @@ -315,6 +318,9 @@ static qboolean x11_initlib(void) {(void**)&x11.pXMatchVisualInfo, "XMatchVisualInfo"}, {(void**)&x11.pXGetVisualInfo, "XGetVisualInfo"}, + {(void**)&x11.pXKeysymToString, "XKeysymToString"}, + {(void**)&x11.pXGetKeyboardMapping, "XGetKeyboardMapping"}, + {(void**)&x11.pXGrabServer, "XGrabServer"}, {(void**)&x11.pXUngrabServer, "XUngrabServer"}, @@ -2484,6 +2490,97 @@ static long X_InitUnicode(void) return requiredevents; } +#define XKEY_QUAKE_MAP() \ + XKQM(XK_KP_Page_Up, K_KP_PGUP) \ + XKQM(XK_Page_Up, K_PGUP) \ + XKQM(XK_KP_Page_Down, K_KP_PGDN) \ + XKQM(XK_Page_Down, K_PGDN) \ + XKQM(XK_KP_Home, K_KP_HOME) \ + XKQM(XK_Home, K_HOME) \ + XKQM(XK_KP_End, K_KP_END) \ + XKQM(XK_End, K_END) \ + XKQM(XK_KP_Left, K_KP_LEFTARROW) \ + XKQM(XK_Left, K_LEFTARROW) \ + XKQM(XK_KP_Right, K_KP_RIGHTARROW)\ + XKQM(XK_Right, K_RIGHTARROW) \ + XKQM(XK_KP_Down, K_KP_DOWNARROW) \ + XKQM(XK_Down, K_DOWNARROW) \ + XKQM(XK_KP_Up, K_KP_UPARROW) \ + XKQM(XK_Up, K_UPARROW) \ + XKQM(XK_Escape, K_ESCAPE) \ + XKQM(XK_KP_Enter, K_KP_ENTER) \ + XKQM(XK_Return, K_ENTER) \ + XKQM(XK_Num_Lock, K_KP_NUMLOCK) \ + XKQM(XK_Caps_Lock, K_CAPSLOCK) \ + XKQM(XK_Scroll_Lock, K_SCRLCK) \ + XKQM(XK_Print, K_PRINTSCREEN) \ + XKQM(XK_Super_L, K_LWIN) \ + XKQM(XK_Super_R, K_RWIN) \ + XKQM(XK_Tab, K_TAB) \ + XKQM(XK_F1, K_F1) \ + XKQM(XK_F2, K_F2) \ + XKQM(XK_F3, K_F3) \ + XKQM(XK_F4, K_F4) \ + XKQM(XK_F5, K_F5) \ + XKQM(XK_F6, K_F6) \ + XKQM(XK_F7, K_F7) \ + XKQM(XK_F8, K_F8) \ + XKQM(XK_F9, K_F9) \ + XKQM(XK_F10, K_F10) \ + XKQM(XK_F11, K_F11) \ + XKQM(XK_F12, K_F12) \ + XKQM(XK_F13, K_F13) \ + XKQM(XK_F14, K_F14) \ + XKQM(XK_F15, K_F15) \ + XKQM(XK_BackSpace, K_BACKSPACE) \ + XKQM(XK_KP_Delete, K_KP_DEL) \ + XKQM(XK_Delete, K_DEL) \ + XKQM(XK_Pause, K_PAUSE) \ + XKQM(XK_Shift_L, K_LSHIFT) \ + XKQM(XK_Shift_R, K_RSHIFT) \ + /*XKQM2(XK_Execute, K_LCTRL)*/ \ + XKQM(XK_Control_L, K_LCTRL) \ + XKQM(XK_Control_R, K_RCTRL) \ + XKQM(XK_Alt_L, K_LALT) \ + /*XKQM2(XK_Meta_L, K_LWIN)*/ \ + XKQM(XK_Alt_R, K_RALT) \ + /*XKQM2(XK_Meta_R, K_RWIN)*/ \ + XKQM(XK_Menu, K_APP) \ + XKQM(XK_KP_Begin, K_KP_5) \ + XKQM(XK_KP_Insert, K_KP_INS) \ + XKQM(XK_Insert, K_INS) \ + XKQM(XK_KP_Multiply, K_KP_STAR) \ + XKQM(XK_KP_Add, K_KP_PLUS) \ + XKQM(XK_KP_Subtract, K_KP_MINUS) \ + XKQM(XK_KP_Divide, K_KP_SLASH) + + +qboolean INS_KeyToLocalName(int qkey, char *buf, size_t bufsize) +{ + char *s; + int xk; + *buf = 0; + if(vid_dpy) + { + switch(qkey) + { +#define XKQM(x,q) case q: xk = x; break; +XKEY_QUAKE_MAP() +#undef XKQM + default: + if (qkey >= K_SPACE && qkey < 127) + xk = qkey; //printable ascii is identity-mapped in both + else + return false; //don't know how to map this. might not be a keyboard key. + break; + } + s = x11.pXKeysymToString(xk); + if (s) + Q_strncpyz(buf, s, bufsize); + } + return !!*buf; +} + static void X_KeyEvent(XKeyEvent *ev, qboolean pressed, qboolean filtered) { int i; @@ -2552,112 +2649,9 @@ static void X_KeyEvent(XKeyEvent *ev, qboolean pressed, qboolean filtered) switch(keysym) { - case XK_KP_Page_Up: key = K_KP_PGUP; break; - case XK_Page_Up: key = K_PGUP; break; - - case XK_KP_Page_Down: key = K_KP_PGDN; break; - case XK_Page_Down: key = K_PGDN; break; - - case XK_KP_Home: key = K_KP_HOME; break; - case XK_Home: key = K_HOME; break; - - case XK_KP_End: key = K_KP_END; break; - case XK_End: key = K_END; break; - - case XK_KP_Left: key = K_KP_LEFTARROW; break; - case XK_Left: key = K_LEFTARROW; break; - - case XK_KP_Right: key = K_KP_RIGHTARROW; break; - case XK_Right: key = K_RIGHTARROW; break; - - case XK_KP_Down: key = K_KP_DOWNARROW; break; - case XK_Down: key = K_DOWNARROW; break; - - case XK_KP_Up: key = K_KP_UPARROW; break; - case XK_Up: key = K_UPARROW; break; - - case XK_Escape: key = K_ESCAPE; break; - - case XK_KP_Enter: key = K_KP_ENTER; break; - case XK_Return: key = K_ENTER; break; - - case XK_Num_Lock: key = K_KP_NUMLOCK; break; - case XK_Caps_Lock: key = K_CAPSLOCK; break; - case XK_Scroll_Lock: key = K_SCRLCK; break; - case XK_Print: key = K_PRINTSCREEN; break; - case XK_Super_L: key = K_LWIN; break; - case XK_Super_R: key = K_RWIN; break; - - case XK_Tab: key = K_TAB; break; - - case XK_F1: key = K_F1; break; - case XK_F2: key = K_F2; break; - case XK_F3: key = K_F3; break; - case XK_F4: key = K_F4; break; - case XK_F5: key = K_F5; break; - case XK_F6: key = K_F6; break; - case XK_F7: key = K_F7; break; - case XK_F8: key = K_F8; break; - case XK_F9: key = K_F9; break; - case XK_F10: key = K_F10; break; - case XK_F11: key = K_F11; break; - case XK_F12: key = K_F12; break; - case XK_F13: key = K_F13; break; - case XK_F14: key = K_F14; break; - case XK_F15: key = K_F15; break; - - case XK_BackSpace: key = K_BACKSPACE; break; - - case XK_KP_Delete: key = K_KP_DEL; break; - case XK_Delete: key = K_DEL; break; - - case XK_Pause: key = K_PAUSE; break; - - case XK_Shift_L: key = K_LSHIFT; break; - case XK_Shift_R: key = K_RSHIFT; break; - - case XK_Execute: key = K_LCTRL; break; - case XK_Control_L: key = K_LCTRL; break; - case XK_Control_R: key = K_RCTRL; break; - - case XK_Alt_L: key = K_LALT; break; - case XK_Meta_L: key = K_LALT; break; - case XK_Alt_R: key = K_RALT; break; - case XK_Meta_R: key = K_RALT; break; - case XK_Menu: key = K_APP; break; - - case XK_KP_Begin: key = K_KP_5; break; - - case XK_KP_Insert: key = K_KP_INS; break; - case XK_Insert: key = K_INS; break; - - case XK_KP_Multiply: key = K_KP_STAR; break; - case XK_KP_Add: key = K_KP_PLUS; break; - case XK_KP_Subtract: key = K_KP_MINUS; break; - case XK_KP_Divide: key = K_KP_SLASH; break; - -#if 0 - case 0x021: key = '1';break;/* [!] */ - case 0x040: key = '2';break;/* [@] */ - case 0x023: key = '3';break;/* [#] */ - case 0x024: key = '4';break;/* [$] */ - case 0x025: key = '5';break;/* [%] */ - case 0x05e: key = '6';break;/* [^] */ - case 0x026: key = '7';break;/* [&] */ - case 0x02a: key = '8';break;/* [*] */ - case 0x028: key = '9';;break;/* [(] */ - case 0x029: key = '0';break;/* [)] */ - case 0x05f: key = '-';break;/* [_] */ - case 0x02b: key = '=';break;/* [+] */ - case 0x07c: key = '\'';break;/* [|] */ - case 0x07d: key = '[';break;/* [}] */ - case 0x07b: key = ']';break;/* [{] */ - case 0x022: key = '\'';break;/* ["] */ - case 0x03a: key = ';';break;/* [:] */ - case 0x03f: key = '/';break;/* [?] */ - case 0x03e: key = '.';break;/* [>] */ - case 0x03c: key = ',';break;/* [<] */ -#endif +#define XKQM(x,q) case x: key = q; break; +XKEY_QUAKE_MAP() +#undef XKQM default: key = keysym; @@ -2853,13 +2847,15 @@ static void GetEvent(void) case 3: button = K_MOUSE2; break; case 4: button = K_MWHEELUP; break; //so much for 'raw'. case 5: button = K_MWHEELDOWN; break; - case 6: button = K_MOUSE4; break; - case 7: button = K_MOUSE5; break; - case 8: button = K_MOUSE6; break; - case 9: button = K_MOUSE7; break; - case 10: button = K_MOUSE8; break; - case 11: button = K_MOUSE9; break; - case 12: button = K_MOUSE10; break; + case 6: button = K_MWHEELLEFT; break; //so much for 'raw'. + case 7: button = K_MWHEELRIGHT; break; + case 8: button = K_MOUSE4; break; + case 9: button = K_MOUSE5; break; + case 10: button = K_MOUSE6; break; + case 11: button = K_MOUSE7; break; + case 12: button = K_MOUSE8; break; + case 13: button = K_MOUSE9; break; + case 14: button = K_MOUSE10; break; default:button = 0; break; } if (button) @@ -3006,19 +3002,19 @@ static void GetEvent(void) //note, the x11 protocol does not support more than 5 mouse buttons //which is a bit of a shame, but hey. else if (event.xbutton.button == 6) - b = x11violations?K_MOUSE4:-1; + b = x11violations?K_MWHEELLEFT:-1; else if (event.xbutton.button == 7) - b = x11violations?K_MOUSE5:-1; + b = x11violations?K_MWHEELRIGHT:-1; else if (event.xbutton.button == 8) - b = x11violations?K_MOUSE6:-1; + b = x11violations?K_MOUSE4:-1; else if (event.xbutton.button == 9) - b = x11violations?K_MOUSE7:-1; + b = x11violations?K_MOUSE5:-1; else if (event.xbutton.button == 10) - b = x11violations?K_MOUSE8:-1; + b = x11violations?K_MOUSE6:-1; else if (event.xbutton.button == 11) - b = x11violations?K_MOUSE9:-1; + b = x11violations?K_MOUSE7:-1; else if (event.xbutton.button == 12) - b = x11violations?K_MOUSE10:-1; + b = x11violations?K_MOUSE8:-1; if (b>=0) IN_KeyEvent(x11_mouseqdev, true, b, 0); @@ -3051,19 +3047,19 @@ static void GetEvent(void) //note, the x11 protocol does not support more than 5 mouse buttons //which is a bit of a shame, but hey. else if (event.xbutton.button == 6) - b = x11violations?K_MOUSE4:-1; + b = x11violations?K_MWHEELLEFT:-1; else if (event.xbutton.button == 7) - b = x11violations?K_MOUSE5:-1; + b = x11violations?K_MWHEELRIGHT:-1; else if (event.xbutton.button == 8) - b = x11violations?K_MOUSE6:-1; + b = x11violations?K_MOUSE4:-1; else if (event.xbutton.button == 9) - b = x11violations?K_MOUSE7:-1; + b = x11violations?K_MOUSE5:-1; else if (event.xbutton.button == 10) - b = x11violations?K_MOUSE8:-1; + b = x11violations?K_MOUSE6:-1; else if (event.xbutton.button == 11) - b = x11violations?K_MOUSE9:-1; + b = x11violations?K_MOUSE7:-1; else if (event.xbutton.button == 12) - b = x11violations?K_MOUSE10:-1; + b = x11violations?K_MOUSE8:-1; if (b>=0) IN_KeyEvent(x11_mouseqdev, false, b, 0);