1/* 2 * Copyright © 2008 Ian Osgood <iano@quirkster.com> 3 * Copyright © 2008 Jamey Sharp <jamey@minilop.net> 4 * Copyright © 2008 Josh Triplett <josh@freedesktop.org> 5 * Copyright © 2008 Ulrich Eckhardt <doomster@knuut.de> 6 * 7 * Permission is hereby granted, free of charge, to any person 8 * obtaining a copy of this software and associated documentation 9 * files (the "Software"), to deal in the Software without 10 * restriction, including without limitation the rights to use, copy, 11 * modify, merge, publish, distribute, sublicense, and/or sell copies 12 * of the Software, and to permit persons to whom the Software is 13 * furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be 16 * included in all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 22 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 23 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 * Except as contained in this notice, the names of the authors or 27 * their institutions shall not be used in advertising or otherwise to 28 * promote the sale, use or other dealings in this Software without 29 * prior written authorization from the authors. 30 */ 31 32#include <stdlib.h> 33 34#include <xcb/xcb.h> 35#define XK_MISCELLANY 36#define XK_XKB_KEYS 37#define XK_LATIN1 38#define XK_LATIN2 39#define XK_LATIN3 40#define XK_LATIN4 41#define XK_CYRILLIC 42#define XK_GREEK 43#define XK_ARMENIAN 44#include <X11/keysymdef.h> 45 46#include "xcb_keysyms.h" 47 48/* Private declaration */ 49enum tag_t { 50 TAG_COOKIE, 51 TAG_VALUE 52}; 53 54struct _XCBKeySymbols 55{ 56 xcb_connection_t *c; 57 enum tag_t tag; 58 union { 59 xcb_get_keyboard_mapping_cookie_t cookie; 60 xcb_get_keyboard_mapping_reply_t *reply; 61 } u; 62}; 63 64static void xcb_convert_case(xcb_keysym_t sym, 65 xcb_keysym_t *lower, 66 xcb_keysym_t *upper); 67 68static void xcb_key_symbols_get_reply (xcb_key_symbols_t *syms, 69 xcb_generic_error_t **e); 70 71/* public implementation */ 72 73xcb_key_symbols_t * 74xcb_key_symbols_alloc (xcb_connection_t *c) 75{ 76 xcb_key_symbols_t *syms; 77 xcb_keycode_t min_keycode; 78 xcb_keycode_t max_keycode; 79 80 if (!c) 81 return NULL; 82 83 syms = malloc (sizeof (xcb_key_symbols_t)); 84 85 syms->c = c; 86 syms->tag = TAG_COOKIE; 87 88 min_keycode = xcb_get_setup (c)->min_keycode; 89 max_keycode = xcb_get_setup (c)->max_keycode; 90 91 syms->u.cookie = xcb_get_keyboard_mapping(c, 92 min_keycode, 93 max_keycode - min_keycode + 1); 94 95 return syms; 96} 97 98void 99xcb_key_symbols_free (xcb_key_symbols_t *syms) 100{ 101 if (syms) 102 { 103 if (syms->tag == TAG_VALUE) 104 free (syms->u.reply); 105 free (syms); 106 syms = NULL; 107 } 108} 109 110/* Use of the 'col' parameter: 111 112A list of KeySyms is associated with each KeyCode. The list is intended 113to convey the set of symbols on the corresponding key. If the list 114(ignoring trailing NoSymbol entries) is a single KeySym ``K'', then the 115list is treated as if it were the list ``K NoSymbol K NoSymbol''. If the 116list (ignoring trailing NoSymbol entries) is a pair of KeySyms ``K1 117K2'', then the list is treated as if it were the list ``K1 K2 K1 K2''. 118If the list (ignoring trailing NoSymbol entries) is a triple of KeySyms 119``K1 K2 K3'', then the list is treated as if it were the list ``K1 K2 K3 120NoSymbol''. When an explicit ``void'' element is desired in the list, 121the value VoidSymbol can be used. 122 123The first four elements of the list are split into two groups of 124KeySyms. Group 1 contains the first and second KeySyms; Group 2 contains 125the third and fourth KeySyms. Within each group, if the second element 126of the group is NoSymbol , then the group should be treated as if the 127second element were the same as the first element, except when the first 128element is an alphabetic KeySym ``K'' for which both lowercase and 129uppercase forms are defined. In that case, the group should be treated 130as if the first element were the lowercase form of ``K'' and the second 131element were the uppercase form of ``K.'' 132 133The standard rules for obtaining a KeySym from a KeyPress event make use 134of only the Group 1 and Group 2 KeySyms; no interpretation of other 135KeySyms in the list is given. Which group to use is determined by the 136modifier state. Switching between groups is controlled by the KeySym 137named MODE SWITCH, by attaching that KeySym to some KeyCode and 138attaching that KeyCode to any one of the modifiers Mod1 through Mod5. 139This modifier is called the group modifier. For any KeyCode, Group 1 is 140used when the group modifier is off, and Group 2 is used when the group 141modifier is on. 142 143The Lock modifier is interpreted as CapsLock when the KeySym named 144XK_Caps_Lock is attached to some KeyCode and that KeyCode is attached to 145the Lock modifier. The Lock modifier is interpreted as ShiftLock when 146the KeySym named XK_Shift_Lock is attached to some KeyCode and that 147KeyCode is attached to the Lock modifier. If the Lock modifier could be 148interpreted as both CapsLock and ShiftLock, the CapsLock interpretation 149is used. 150 151The operation of keypad keys is controlled by the KeySym named 152XK_Num_Lock, by attaching that KeySym to some KeyCode and attaching that 153KeyCode to any one of the modifiers Mod1 through Mod5 . This modifier is 154called the numlock modifier. The standard KeySyms with the prefix 155``XK_KP_'' in their name are called keypad KeySyms; these are KeySyms 156with numeric value in the hexadecimal range 0xFF80 to 0xFFBD inclusive. 157In addition, vendor-specific KeySyms in the hexadecimal range 0x11000000 158to 0x1100FFFF are also keypad KeySyms. 159 160Within a group, the choice of KeySym is determined by applying the first 161rule that is satisfied from the following list: 162 163* The numlock modifier is on and the second KeySym is a keypad KeySym. In 164 this case, if the Shift modifier is on, or if the Lock modifier is on 165 and is interpreted as ShiftLock, then the first KeySym is used, 166 otherwise the second KeySym is used. 167 168* The Shift and Lock modifiers are both off. In this case, the first 169 KeySym is used. 170 171* The Shift modifier is off, and the Lock modifier is on and is 172 interpreted as CapsLock. In this case, the first KeySym is used, but 173 if that KeySym is lowercase alphabetic, then the corresponding 174 uppercase KeySym is used instead. 175 176* The Shift modifier is on, and the Lock modifier is on and is 177 interpreted as CapsLock. In this case, the second KeySym is used, but 178 if that KeySym is lowercase alphabetic, then the corresponding 179 uppercase KeySym is used instead. 180 181* The Shift modifier is on, or the Lock modifier is on and is 182 interpreted as ShiftLock, or both. In this case, the second KeySym is 183 used. 184 185*/ 186 187xcb_keysym_t xcb_key_symbols_get_keysym (xcb_key_symbols_t *syms, 188 xcb_keycode_t keycode, 189 int col) 190{ 191 xcb_keysym_t *keysyms; 192 xcb_keysym_t keysym_null = { XCB_NO_SYMBOL }; 193 xcb_keysym_t lsym; 194 xcb_keysym_t usym; 195 xcb_keycode_t min_keycode; 196 xcb_keycode_t max_keycode; 197 int per; 198 199 if (!syms) 200 return keysym_null; 201 202 xcb_key_symbols_get_reply (syms, NULL); 203 204 keysyms = xcb_get_keyboard_mapping_keysyms (syms->u.reply); 205 min_keycode = xcb_get_setup (syms->c)->min_keycode; 206 max_keycode = xcb_get_setup (syms->c)->max_keycode; 207 208 per = syms->u.reply->keysyms_per_keycode; 209 if ((col < 0) || ((col >= per) && (col > 3)) || 210 (keycode < min_keycode) || 211 (keycode > max_keycode)) 212 return keysym_null; 213 214 keysyms = &keysyms[(keycode - min_keycode) * per]; 215 if (col < 4) 216 { 217 if (col > 1) 218 { 219 while ((per > 2) && (keysyms[per - 1] == XCB_NO_SYMBOL)) 220 per--; 221 if (per < 3) 222 col -= 2; 223 } 224 if ((per <= (col|1)) || (keysyms[col|1] == XCB_NO_SYMBOL)) 225 { 226 xcb_convert_case(keysyms[col&~1], &lsym, &usym); 227 if (!(col & 1)) 228 return lsym; 229 else if (usym == lsym) 230 return keysym_null; 231 else 232 return usym; 233 } 234 } 235 return keysyms[col]; 236} 237 238xcb_keycode_t * 239xcb_key_symbols_get_keycode(xcb_key_symbols_t *syms, 240 xcb_keysym_t keysym) 241{ 242 xcb_keysym_t ks; 243 int j, nresult = 0; 244 xcb_keycode_t i, min, max, *result = NULL; 245 246 if(syms) 247 { 248 xcb_key_symbols_get_reply (syms, NULL); 249 min = xcb_get_setup(syms->c)->min_keycode; 250 max = xcb_get_setup(syms->c)->max_keycode; 251 252 for(j = 0; j < syms->u.reply->keysyms_per_keycode; j++) 253 for(i = min; i && i <= max; i++) 254 { 255 ks = xcb_key_symbols_get_keysym(syms, i, j); 256 if(ks == keysym) 257 { 258 nresult++; 259 result = realloc(result, sizeof(xcb_keycode_t) * (nresult + 1)); 260 result[nresult - 1] = i; 261 result[nresult] = XCB_NO_SYMBOL; 262 } 263 } 264 } 265 266 return result; 267} 268 269xcb_keysym_t 270xcb_key_press_lookup_keysym (xcb_key_symbols_t *syms, 271 xcb_key_press_event_t *event, 272 int col) 273{ 274 return xcb_key_symbols_get_keysym (syms, event->detail, col); 275} 276 277xcb_keysym_t 278xcb_key_release_lookup_keysym (xcb_key_symbols_t *syms, 279 xcb_key_release_event_t *event, 280 int col) 281{ 282 return xcb_key_symbols_get_keysym (syms, event->detail, col); 283} 284 285int 286xcb_refresh_keyboard_mapping (xcb_key_symbols_t *syms, 287 xcb_mapping_notify_event_t *event) 288{ 289 if (event->request == XCB_MAPPING_KEYBOARD && syms) { 290 if (syms->tag == TAG_VALUE) { 291 xcb_keycode_t min_keycode; 292 xcb_keycode_t max_keycode; 293 294 if (syms->u.reply) { 295 free (syms->u.reply); 296 syms->u.reply = NULL; 297 } 298 syms->tag = TAG_COOKIE; 299 min_keycode = xcb_get_setup (syms->c)->min_keycode; 300 max_keycode = xcb_get_setup (syms->c)->max_keycode; 301 302 syms->u.cookie = xcb_get_keyboard_mapping(syms->c, 303 min_keycode, 304 max_keycode - min_keycode + 1); 305 306 } 307 return 1; 308 } 309 return 0; 310} 311 312 313/* Tests for classes of symbols */ 314 315int 316xcb_is_keypad_key (xcb_keysym_t keysym) 317{ 318 return ((keysym >= XK_KP_Space) && (keysym <= XK_KP_Equal)); 319} 320 321int 322xcb_is_private_keypad_key (xcb_keysym_t keysym) 323{ 324 return ((keysym >= 0x11000000) && (keysym <= 0x1100FFFF)); 325} 326 327int 328xcb_is_cursor_key (xcb_keysym_t keysym) 329{ 330 return ((keysym >= XK_Home) && (keysym <= XK_Select)); 331} 332 333int 334xcb_is_pf_key (xcb_keysym_t keysym) 335{ 336 return ((keysym >= XK_KP_F1) && (keysym <= XK_KP_F4)); 337} 338 339int 340xcb_is_function_key (xcb_keysym_t keysym) 341{ 342 return ((keysym >= XK_F1) && (keysym <= XK_F35)); 343} 344 345int 346xcb_is_misc_function_key (xcb_keysym_t keysym) 347{ 348 return ((keysym >= XK_Select) && (keysym <= XK_Break)); 349} 350 351int 352xcb_is_modifier_key (xcb_keysym_t keysym) 353{ 354 return (((keysym >= XK_Shift_L) && (keysym <= XK_Hyper_R)) || 355 ((keysym >= XK_ISO_Lock) && (keysym <= XK_ISO_Last_Group_Lock)) || 356 (keysym == XK_Mode_switch) || 357 (keysym == XK_Num_Lock)); 358} 359 360/* private functions */ 361 362void 363xcb_convert_case(xcb_keysym_t sym, 364 xcb_keysym_t *lower, 365 xcb_keysym_t *upper) 366{ 367 *lower = sym; 368 *upper = sym; 369 370 switch(sym >> 8) 371 { 372 case 0: /* Latin 1 */ 373 if ((sym >= XK_A) && (sym <= XK_Z)) 374 *lower += (XK_a - XK_A); 375 else if ((sym >= XK_a) && (sym <= XK_z)) 376 *upper -= (XK_a - XK_A); 377 else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) 378 *lower += (XK_agrave - XK_Agrave); 379 else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis)) 380 *upper -= (XK_agrave - XK_Agrave); 381 else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) 382 *lower += (XK_oslash - XK_Ooblique); 383 else if ((sym >= XK_oslash) && (sym <= XK_thorn)) 384 *upper -= (XK_oslash - XK_Ooblique); 385 break; 386 case 1: /* Latin 2 */ 387 /* Assume the KeySym is a legal value (ignore discontinuities) */ 388 if (sym == XK_Aogonek) 389 *lower = XK_aogonek; 390 else if (sym >= XK_Lstroke && sym <= XK_Sacute) 391 *lower += (XK_lstroke - XK_Lstroke); 392 else if (sym >= XK_Scaron && sym <= XK_Zacute) 393 *lower += (XK_scaron - XK_Scaron); 394 else if (sym >= XK_Zcaron && sym <= XK_Zabovedot) 395 *lower += (XK_zcaron - XK_Zcaron); 396 else if (sym == XK_aogonek) 397 *upper = XK_Aogonek; 398 else if (sym >= XK_lstroke && sym <= XK_sacute) 399 *upper -= (XK_lstroke - XK_Lstroke); 400 else if (sym >= XK_scaron && sym <= XK_zacute) 401 *upper -= (XK_scaron - XK_Scaron); 402 else if (sym >= XK_zcaron && sym <= XK_zabovedot) 403 *upper -= (XK_zcaron - XK_Zcaron); 404 else if (sym >= XK_Racute && sym <= XK_Tcedilla) 405 *lower += (XK_racute - XK_Racute); 406 else if (sym >= XK_racute && sym <= XK_tcedilla) 407 *upper -= (XK_racute - XK_Racute); 408 break; 409 case 2: /* Latin 3 */ 410 /* Assume the KeySym is a legal value (ignore discontinuities) */ 411 if (sym >= XK_Hstroke && sym <= XK_Hcircumflex) 412 *lower += (XK_hstroke - XK_Hstroke); 413 else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex) 414 *lower += (XK_gbreve - XK_Gbreve); 415 else if (sym >= XK_hstroke && sym <= XK_hcircumflex) 416 *upper -= (XK_hstroke - XK_Hstroke); 417 else if (sym >= XK_gbreve && sym <= XK_jcircumflex) 418 *upper -= (XK_gbreve - XK_Gbreve); 419 else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex) 420 *lower += (XK_cabovedot - XK_Cabovedot); 421 else if (sym >= XK_cabovedot && sym <= XK_scircumflex) 422 *upper -= (XK_cabovedot - XK_Cabovedot); 423 break; 424 case 3: /* Latin 4 */ 425 /* Assume the KeySym is a legal value (ignore discontinuities) */ 426 if (sym >= XK_Rcedilla && sym <= XK_Tslash) 427 *lower += (XK_rcedilla - XK_Rcedilla); 428 else if (sym >= XK_rcedilla && sym <= XK_tslash) 429 *upper -= (XK_rcedilla - XK_Rcedilla); 430 else if (sym == XK_ENG) 431 *lower = XK_eng; 432 else if (sym == XK_eng) 433 *upper = XK_ENG; 434 else if (sym >= XK_Amacron && sym <= XK_Umacron) 435 *lower += (XK_amacron - XK_Amacron); 436 else if (sym >= XK_amacron && sym <= XK_umacron) 437 *upper -= (XK_amacron - XK_Amacron); 438 break; 439 case 6: /* Cyrillic */ 440 /* Assume the KeySym is a legal value (ignore discontinuities) */ 441 if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE) 442 *lower -= (XK_Serbian_DJE - XK_Serbian_dje); 443 else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze) 444 *upper += (XK_Serbian_DJE - XK_Serbian_dje); 445 else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN) 446 *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu); 447 else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign) 448 *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu); 449 break; 450 case 7: /* Greek */ 451 /* Assume the KeySym is a legal value (ignore discontinuities) */ 452 if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent) 453 *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent); 454 else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent && 455 sym != XK_Greek_iotaaccentdieresis && 456 sym != XK_Greek_upsilonaccentdieresis) 457 *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent); 458 else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA) 459 *lower += (XK_Greek_alpha - XK_Greek_ALPHA); 460 else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega && 461 sym != XK_Greek_finalsmallsigma) 462 *upper -= (XK_Greek_alpha - XK_Greek_ALPHA); 463 break; 464 case 0x14: /* Armenian */ 465 if (sym >= XK_Armenian_AYB && sym <= XK_Armenian_fe) { 466 *lower = sym | 1; 467 *upper = sym & ~1; 468 } 469 break; 470 } 471} 472 473void 474xcb_key_symbols_get_reply (xcb_key_symbols_t *syms, 475 xcb_generic_error_t **e) 476{ 477 if (!syms) 478 return; 479 480 if (syms->tag == TAG_COOKIE) 481 { 482 syms->tag = TAG_VALUE; 483 syms->u.reply = xcb_get_keyboard_mapping_reply(syms->c, 484 syms->u.cookie, 485 e); 486 } 487} 488