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