1 /*
2 
3   Copyright (c) 2003-2018 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 
32 */
33 
34 /*
35  * key conversion utility for uim-gtk
36  */
37 
38 #include <config.h>
39 
40 #include <glib.h>
41 #include <gtk/gtk.h>
42 #if GTK_CHECK_VERSION(2, 90, 0)
43 # include <gdk/gdkkeysyms-compat.h>
44 #else
45 # include <gdk/gdkkeysyms.h>
46 #endif
47 #ifdef GDK_WINDOWING_X11
48 #include <gdk/gdkx.h>
49 #include <X11/Xlib.h>
50 #include <X11/XKBlib.h>
51 #endif
52 
53 #ifdef GDK_WINDOWING_X11
54 #define UIM_GTK_USE_JAPANESE_KANA_KEYBOARD_HACK 1
55 #endif
56 
57 #include "uim/uim.h"
58 #include "uim/uim-scm.h"
59 #if UIM_GTK_USE_JAPANESE_KANA_KEYBOARD_HACK
60 #include "uim/uim-x-util.h"
61 #endif
62 
63 #include "key-util-gtk.h"
64 
65 static gboolean g_use_custom_modifier_masks = FALSE;
66 
67 #ifdef GDK_WINDOWING_X11
68 static guint g_mod1_mask, g_mod2_mask, g_mod3_mask, g_mod4_mask, g_mod5_mask;
69 static gint g_numlock_mask;
70 static guint g_modifier_state, g_pre_modifier_state;
71 #endif
72 
73 void
im_uim_convert_keyevent(GdkEventKey * event,int * ukey,int * umod)74 im_uim_convert_keyevent(GdkEventKey *event, int *ukey, int *umod)
75 {
76   int keyval = event->keyval;
77   int mod = event->state;
78 
79   *umod = 0;
80 #ifdef GDK_WINDOWING_X11
81   if (event->type == GDK_KEY_PRESS) {
82     if (!mod || mod == GDK_LOCK_MASK ||
83 	mod == g_numlock_mask)
84       g_modifier_state = 0;
85   }
86   g_pre_modifier_state = g_modifier_state;
87 #endif
88 
89   /* 1. check key */
90   if (keyval < 256)
91     *ukey = keyval;
92   else if (keyval >= GDK_F1 && keyval <= GDK_F35)
93     *ukey = keyval - GDK_F1 + UKey_F1;
94   else if (keyval >= GDK_KP_0 && keyval <= GDK_KP_9)
95     *ukey = keyval - GDK_KP_0 + UKey_0;
96 #if GTK_CHECK_VERSION(2, 6, 0)
97   else if (keyval >= GDK_dead_grave && keyval <= GDK_dead_horn)
98 #else
99   else if (keyval >= GDK_dead_grave && keyval <= GDK_dead_belowdot)
100 #endif
101     *ukey = keyval - GDK_dead_grave + UKey_Dead_Grave;
102   else if (keyval >= GDK_Kanji && keyval <= GDK_Eisu_toggle)
103     *ukey = keyval - GDK_Kanji + UKey_Kanji;
104   else if (keyval >= GDK_Hangul && keyval <= GDK_Hangul_Special)
105     *ukey = keyval - GDK_Hangul + UKey_Hangul;
106   else if (keyval >= GDK_kana_fullstop && keyval <= GDK_semivoicedsound)
107     *ukey = keyval - GDK_kana_fullstop + UKey_Kana_Fullstop;
108   else {
109     switch (keyval) {
110     case GDK_BackSpace:
111       *ukey = UKey_Backspace;
112       break;
113     case GDK_Delete:
114       *ukey = UKey_Delete;
115       break;
116     case GDK_Insert:
117       *ukey = UKey_Insert;
118       break;
119     case GDK_Escape:
120       *ukey = UKey_Escape;
121       break;
122     case GDK_Tab:
123     case GDK_ISO_Left_Tab:
124       *ukey = UKey_Tab;
125       break;
126     case GDK_Return:
127       *ukey = UKey_Return;
128       break;
129     case GDK_Left:
130       *ukey = UKey_Left;
131       break;
132     case GDK_Up:
133       *ukey = UKey_Up;
134       break;
135     case GDK_Right:
136       *ukey = UKey_Right;
137       break;
138     case GDK_Down:
139       *ukey = UKey_Down;
140       break;
141     case GDK_Prior:
142       *ukey = UKey_Prior;
143       break;
144     case GDK_Next:
145       *ukey = UKey_Next;
146       break;
147     case GDK_Home:
148       *ukey = UKey_Home;
149       break;
150     case GDK_End:
151       *ukey = UKey_End;
152       break;
153     case GDK_Multi_key:
154       *ukey = UKey_Multi_key;
155       break;
156     case GDK_Codeinput:
157       *ukey = UKey_Codeinput;
158       break;
159     case GDK_SingleCandidate:
160       *ukey = UKey_SingleCandidate;
161       break;
162     case GDK_MultipleCandidate:
163       *ukey = UKey_MultipleCandidate;
164       break;
165     case GDK_PreviousCandidate:
166       *ukey = UKey_PreviousCandidate;
167       break;
168     case GDK_Mode_switch:
169       *ukey = UKey_Mode_switch;
170       break;
171     case GDK_Shift_L:
172     case GDK_Shift_R:
173 #ifdef GDK_WINDOWING_X11
174       if (event->type == GDK_KEY_PRESS)
175 	g_modifier_state |= UMod_Shift;
176       else
177 	g_modifier_state &= ~UMod_Shift;
178 #endif
179       *ukey = UKey_Shift_key;
180       break;
181     case GDK_Control_L:
182     case GDK_Control_R:
183 #ifdef GDK_WINDOWING_X11
184       if (event->type == GDK_KEY_PRESS)
185 	g_modifier_state |= UMod_Control;
186       else
187 	g_modifier_state &= ~UMod_Control;
188 #endif
189       *ukey = UKey_Control_key;
190       break;
191     case GDK_Alt_L:
192     case GDK_Alt_R:
193 #ifdef GDK_WINDOWING_X11
194       if (event->type == GDK_KEY_PRESS)
195 	g_modifier_state |= UMod_Alt;
196       else
197 	g_modifier_state &= ~UMod_Alt;
198 #endif
199       *ukey = UKey_Alt_key;
200       break;
201     case GDK_Meta_L:
202     case GDK_Meta_R:
203 #ifdef GDK_WINDOWING_X11
204       if (event->type == GDK_KEY_PRESS)
205 	g_modifier_state |= UMod_Meta;
206       else
207 	g_modifier_state &= ~UMod_Meta;
208 #endif
209       *ukey = UKey_Meta_key;
210       break;
211     case GDK_Super_L:
212     case GDK_Super_R:
213 #ifdef GDK_WINDOWING_X11
214       if (event->type == GDK_KEY_PRESS)
215 	g_modifier_state |= UMod_Super;
216       else
217 	g_modifier_state &= ~UMod_Super;
218 #endif
219       *ukey = UKey_Super_key;
220       break;
221     case GDK_Hyper_L:
222     case GDK_Hyper_R:
223 #ifdef GDK_WINDOWING_X11
224       if (event->type == GDK_KEY_PRESS)
225 	g_modifier_state |= UMod_Hyper;
226       else
227 	g_modifier_state &= ~UMod_Hyper;
228 #endif
229       *ukey = UKey_Hyper_key;
230       break;
231     case GDK_Caps_Lock:
232       *ukey = UKey_Caps_Lock;
233       break;
234     case GDK_Num_Lock:
235       *ukey = UKey_Num_Lock;
236       break;
237     case GDK_Scroll_Lock:
238       *ukey = UKey_Scroll_Lock;
239       break;
240     default:
241       *ukey = UKey_Other;
242      break;
243     }
244   }
245 #if UIM_GTK_USE_JAPANESE_KANA_KEYBOARD_HACK
246   /* 1'. replace keysym for Japanese keyboard */
247   *ukey = uim_x_kana_input_hack_translate_key(*ukey, event->hardware_keycode);
248 #endif
249 
250   /* 2. check modifier */
251   if (mod & GDK_SHIFT_MASK)
252     *umod |= UMod_Shift;
253   if (mod & GDK_CONTROL_MASK)
254     *umod |= UMod_Control;
255   if (g_use_custom_modifier_masks) {
256 #ifdef GDK_WINDOWING_X11
257     if (mod & GDK_MOD1_MASK)
258       *umod |= (g_mod1_mask & g_pre_modifier_state);
259     if (mod & GDK_MOD2_MASK)
260       *umod |= (g_mod2_mask & g_pre_modifier_state);
261     if (mod & GDK_MOD3_MASK)
262       *umod |= (g_mod3_mask & g_pre_modifier_state);
263     if (mod & GDK_MOD4_MASK)
264       *umod |= (g_mod4_mask & g_pre_modifier_state);
265     if (mod & GDK_MOD5_MASK)
266       *umod |= (g_mod5_mask & g_pre_modifier_state);
267 #endif
268   } else {
269     if (mod & GDK_MOD1_MASK)
270       *umod |= UMod_Alt;
271     if (mod & GDK_MOD3_MASK)  /* assuming mod3 */
272       *umod |= UMod_Super;
273     if (mod & GDK_MOD4_MASK)  /* assuming mod4 */
274       *umod |= UMod_Hyper;
275   }
276 }
277 
278 #ifdef GDK_WINDOWING_X11
279 static int
check_modifier(GSList * slist)280 check_modifier(GSList *slist)
281 {
282   int ret;
283   GSList *tmp_list;
284 
285   ret = 0;
286   for (tmp_list = slist; tmp_list; tmp_list = tmp_list->next) {
287     switch (GPOINTER_TO_UINT(tmp_list->data)) {
288     case XK_Shift_L:
289     case XK_Shift_R:
290       ret |= UMod_Shift;
291       break;
292     case XK_Control_L:
293     case XK_Control_R:
294       ret |= UMod_Control;
295       break;
296     case XK_Meta_L:
297     case XK_Meta_R:
298       ret |= UMod_Meta;
299       break;
300     case XK_Alt_L:
301     case XK_Alt_R:
302       ret |= UMod_Alt;
303       break;
304     case XK_Super_L:
305     case XK_Super_R:
306       ret |= UMod_Super;
307       break;
308     case XK_Hyper_L:
309     case XK_Hyper_R:
310       ret |= UMod_Hyper;
311       break;
312     default:
313       break;
314     }
315   }
316   return ret;
317 }
318 #endif
319 
320 void
im_uim_init_modifier_keys()321 im_uim_init_modifier_keys()
322 {
323 #ifdef GDK_WINDOWING_X11
324   int i, k = 0;
325   int min_keycode, max_keycode, keysyms_per_keycode = 0;
326   GdkDisplay *gdk_display;
327   Display *display;
328   GSList *mod1_list, *mod2_list, *mod3_list, *mod4_list, *mod5_list;
329   XModifierKeymap *map;
330   KeySym *sym;
331 
332   g_modifier_state = 0;
333   g_numlock_mask = 0;
334 
335   mod1_list = mod2_list = mod3_list = mod4_list = mod5_list = NULL;
336 
337   gdk_display = gdk_display_get_default();
338 #  ifdef GDK_TYPE_X11_DISPLAY
339   if (!GDK_IS_X11_DISPLAY(gdk_display)) {
340     /* TODO: We may need to something for Wayland. */
341     return;
342   }
343 #  endif
344   display = GDK_DISPLAY_XDISPLAY(gdk_display);
345   map = XGetModifierMapping(display);
346   XDisplayKeycodes(display, &min_keycode, &max_keycode);
347   sym = XGetKeyboardMapping(display, min_keycode,
348 		  	    (max_keycode - min_keycode + 1),
349 			    &keysyms_per_keycode);
350   for (i = 0; i < 8; i++) {
351     int j;
352     for (j = 0; j < map->max_keypermod; j++) {
353       if (map->modifiermap[k]) {
354 	KeySym ks;
355 	int index = 0;
356 	do {
357 	  ks = XkbKeycodeToKeysym(display, map->modifiermap[k], 0, index);
358 	  index++;
359 	} while (!ks && index < keysyms_per_keycode);
360 
361 	switch (i) {
362 	case ShiftMapIndex:
363 	  break;
364 	case LockMapIndex:
365 	  break;
366 	case ControlMapIndex:
367 	  break;
368 	case Mod1MapIndex:
369 	  mod1_list = g_slist_prepend(mod1_list, GUINT_TO_POINTER(ks));
370 	  g_mod1_mask = check_modifier(mod1_list);
371 	  break;
372 	case Mod2MapIndex:
373 	  mod2_list = g_slist_prepend(mod2_list, GUINT_TO_POINTER(ks));
374 	  g_mod2_mask = check_modifier(mod2_list);
375 	  break;
376 	case Mod3MapIndex:
377 	  mod3_list = g_slist_prepend(mod3_list, GUINT_TO_POINTER(ks));
378 	  g_mod3_mask = check_modifier(mod3_list);
379 	  break;
380 	case Mod4MapIndex:
381 	  mod4_list = g_slist_prepend(mod4_list, GUINT_TO_POINTER(ks));
382 	  g_mod4_mask = check_modifier(mod4_list);
383 	  break;
384 	case Mod5MapIndex:
385 	  mod5_list = g_slist_prepend(mod5_list, GUINT_TO_POINTER(ks));
386 	  g_mod5_mask = check_modifier(mod5_list);
387 	  break;
388 	default:
389 	  break;
390 	}
391 	if (ks == XK_Num_Lock)
392 	  g_numlock_mask |= (1 << i);
393       }
394       k++;
395     }
396   }
397   g_slist_free(mod1_list);
398   g_slist_free(mod2_list);
399   g_slist_free(mod3_list);
400   g_slist_free(mod4_list);
401   g_slist_free(mod5_list);
402   XFreeModifiermap(map);
403   XFree(sym);
404 
405   g_use_custom_modifier_masks = TRUE;
406 
407   if (uim_scm_c_bool(uim_scm_callf("require-dynlib", "s", "xkb")))
408     uim_scm_callf("%xkb-set-display", "p", display);
409 
410 #if UIM_GTK_USE_JAPANESE_KANA_KEYBOARD_HACK
411   uim_x_kana_input_hack_init(display);
412 #endif
413 #endif
414 }
415