1 /*
2 * Copyright (C) 2008 Anthony Liguori <anthony@codemonkey.ws>
3 * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 */
10 #include "config.h"
11
12 #include <gtk/gtk.h>
13 #include <gdk/gdk.h>
14 #include <gdk/gdkkeysyms.h>
15 #include "vncdisplaykeymap.h"
16
17 #include "spice-util.h"
18
19 #undef G_LOG_DOMAIN
20 #define G_LOG_DOMAIN "vnc-keymap"
21 #define VNC_DEBUG(message) SPICE_DEBUG(message);
22
23 /*
24 * This table is taken from QEMU x_keymap.c, under the terms:
25 *
26 * Copyright (c) 2003 Fabrice Bellard
27 *
28 * Permission is hereby granted, free of charge, to any person obtaining a copy
29 * of this software and associated documentation files (the "Software"), to deal
30 * in the Software without restriction, including without limitation the rights
31 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32 * copies of the Software, and to permit persons to whom the Software is
33 * furnished to do so, subject to the following conditions:
34 *
35 * The above copyright notice and this permission notice shall be included in
36 * all copies or substantial portions of the Software.
37 *
38 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
41 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
44 * THE SOFTWARE.
45 */
46
47
48 /* keycode translation for sending ISO_Left_Send
49 * to vncserver
50 */
51 static struct {
52 GdkKeymapKey *keys;
53 gint n_keys;
54 guint keyval;
55 } untranslated_keys[] = {{NULL, 0, GDK_KEY_Tab}};
56
57 static unsigned int ref_count_for_untranslated_keys = 0;
58
59 #ifdef GDK_WINDOWING_WAYLAND
60 #include <gdk/gdkwayland.h>
61 #endif
62
63 #ifdef GDK_WINDOWING_BROADWAY
64 #include <gdk/gdkbroadway.h>
65 #endif
66
67 #if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND)
68 /* Xorg Linux + evdev (offset evdev keycodes) */
69 #include "vncdisplaykeymap_xorgevdev2xtkbd.h"
70 #endif
71
72 #ifdef GDK_WINDOWING_X11
73 #include <gdk/gdkx.h>
74 #include <X11/XKBlib.h>
75 #include <stdbool.h>
76 #include <string.h>
77
78 /* Xorg Linux + kbd (offset + mangled XT keycodes) */
79 #include "vncdisplaykeymap_xorgkbd2xtkbd.h"
80 /* Xorg OS-X aka XQuartz (offset OS-X keycodes) */
81 #include "vncdisplaykeymap_xorgxquartz2xtkbd.h"
82 /* Xorg Cygwin aka XWin (offset + mangled XT keycodes) */
83 #include "vncdisplaykeymap_xorgxwin2xtkbd.h"
84
85 #endif
86
87 #ifdef GDK_WINDOWING_WIN32
88 #include <gdk/gdkwin32.h>
89
90 /* Win32 native virtual keycodes */
91 #include "vncdisplaykeymap_win322xtkbd.h"
92 #endif
93
94 #ifdef GDK_WINDOWING_QUARTZ
95 #include <gdk/gdkquartz.h>
96
97 /* OS-X native keycodes */
98 #include "vncdisplaykeymap_osx2xtkbd.h"
99 #endif
100
101 #ifdef GDK_WINDOWING_BROADWAY
102 /* X11 keysyms */
103 #include "vncdisplaykeymap_x112xtkbd.h"
104 #endif
105
106 #ifdef GDK_WINDOWING_X11
107
108 #define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
109
check_for_xwin(GdkDisplay * dpy)110 static gboolean check_for_xwin(GdkDisplay *dpy)
111 {
112 char *vendor = ServerVendor(gdk_x11_display_get_xdisplay(dpy));
113
114 if (strstr(vendor, "Cygwin/X"))
115 return TRUE;
116
117 return FALSE;
118 }
119
check_for_xquartz(GdkDisplay * dpy)120 static gboolean check_for_xquartz(GdkDisplay *dpy)
121 {
122 int nextensions;
123 int i;
124 gboolean match = FALSE;
125 char **extensions = XListExtensions(gdk_x11_display_get_xdisplay(dpy),
126 &nextensions);
127 for (i = 0 ; extensions != NULL && i < nextensions ; i++) {
128 if (strcmp(extensions[i], "Apple-WM") == 0 ||
129 strcmp(extensions[i], "Apple-DRI") == 0)
130 match = TRUE;
131 }
132 if (extensions)
133 XFreeExtensionList(extensions);
134
135 return match;
136 }
137 #endif
138
vnc_display_keymap_gdk2xtkbd_table(GdkWindow * window,size_t * maplen)139 const guint16 *vnc_display_keymap_gdk2xtkbd_table(GdkWindow *window,
140 size_t *maplen)
141 {
142 #ifdef GDK_WINDOWING_X11
143 if (GDK_IS_X11_WINDOW(window)) {
144 XkbDescPtr desc;
145 const gchar *keycodes = NULL;
146 GdkDisplay *dpy = gdk_window_get_display(window);
147
148 /* There is no easy way to determine what X11 server
149 * and platform & keyboard driver is in use. Thus we
150 * do best guess heuristics.
151 *
152 * This will need more work for people with other
153 * X servers..... patches welcomed.
154 */
155
156 Display *xdisplay = gdk_x11_display_get_xdisplay(dpy);
157 desc = XkbGetMap(xdisplay,
158 XkbGBN_AllComponentsMask,
159 XkbUseCoreKbd);
160 if (desc) {
161 if (XkbGetNames(xdisplay, XkbKeycodesNameMask, desc) == Success) {
162 keycodes = gdk_x11_get_xatom_name(desc->names->keycodes);
163 if (!keycodes)
164 g_warning("could not lookup keycode name");
165 }
166 XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
167 }
168
169 if (check_for_xwin(dpy)) {
170 VNC_DEBUG("Using xwin keycode mapping");
171 *maplen = G_N_ELEMENTS(keymap_xorgxwin2xtkbd);
172 return keymap_xorgxwin2xtkbd;
173 } else if (check_for_xquartz(dpy)) {
174 VNC_DEBUG("Using xquartz keycode mapping");
175 *maplen = G_N_ELEMENTS(keymap_xorgxquartz2xtkbd);
176 return keymap_xorgxquartz2xtkbd;
177 } else if ((keycodes && STRPREFIX(keycodes, "evdev")) ||
178 (XKeysymToKeycode(xdisplay, XK_Page_Up) == 0x70)) {
179 VNC_DEBUG("Using evdev keycode mapping");
180 *maplen = G_N_ELEMENTS(keymap_xorgevdev2xtkbd);
181 return keymap_xorgevdev2xtkbd;
182 } else if ((keycodes && STRPREFIX(keycodes, "xfree86")) ||
183 (XKeysymToKeycode(xdisplay, XK_Page_Up) == 0x63)) {
184 VNC_DEBUG("Using xfree86 keycode mapping");
185 *maplen = G_N_ELEMENTS(keymap_xorgkbd2xtkbd);
186 return keymap_xorgkbd2xtkbd;
187 } else {
188 g_warning("Unknown keycode mapping '%s'.\n"
189 "Please report to gtk-vnc-list@gnome.org\n"
190 "including the following information:\n"
191 "\n"
192 " - Operating system\n"
193 " - GDK build\n"
194 " - X11 Server\n"
195 " - xprop -root\n"
196 " - xdpyinfo\n",
197 keycodes);
198 return NULL;
199 }
200 }
201 #endif
202
203 #ifdef GDK_WINDOWING_WIN32
204 if (GDK_IS_WIN32_WINDOW(window)) {
205 VNC_DEBUG("Using Win32 virtual keycode mapping");
206 *maplen = G_N_ELEMENTS(keymap_win322xtkbd);
207 return keymap_win322xtkbd;
208 }
209 #endif
210
211 #ifdef GDK_WINDOWING_QUARTZ
212 if (GDK_IS_QUARTZ_WINDOW(window)) {
213 VNC_DEBUG("Using OS-X virtual keycode mapping");
214 *maplen = G_N_ELEMENTS(keymap_osx2xtkbd);
215 return keymap_osx2xtkbd;
216 }
217 #endif
218
219 #ifdef GDK_WINDOWING_WAYLAND
220 if (GDK_IS_WAYLAND_WINDOW(window)) {
221 VNC_DEBUG("Using Wayland Xorg/evdev virtual keycode mapping");
222 *maplen = G_N_ELEMENTS(keymap_xorgevdev2xtkbd);
223 return keymap_xorgevdev2xtkbd;
224 }
225 #endif
226
227 #ifdef GDK_WINDOWING_BROADWAY
228 if (GDK_IS_BROADWAY_WINDOW(window)) {
229 g_warning("experimental: using broadway, x11 virtual keysym mapping - with very limited support. See also https://bugzilla.gnome.org/show_bug.cgi?id=700105");
230
231 *maplen = G_N_ELEMENTS(keymap_x112xtkbd);
232 return keymap_x112xtkbd;
233 }
234 #endif
235
236 g_warning("Unsupported GDK Windowing platform.\n"
237 "Disabling extended keycode tables.\n"
238 "Please report to gtk-vnc-list@gnome.org\n"
239 "including the following information:\n"
240 "\n"
241 " - Operating system\n"
242 " - GDK Windowing system build\n");
243 return NULL;
244 }
245
vnc_display_keymap_gdk2xtkbd(const guint16 * keycode_map,size_t keycode_maplen,guint16 keycode)246 guint16 vnc_display_keymap_gdk2xtkbd(const guint16 *keycode_map,
247 size_t keycode_maplen,
248 guint16 keycode)
249 {
250 if (!keycode_map)
251 return 0;
252 if (keycode >= keycode_maplen)
253 return 0;
254 return keycode_map[keycode];
255 }
256
257 /* Set the keymap entries */
vnc_display_keyval_set_entries(void)258 void vnc_display_keyval_set_entries(void)
259 {
260 size_t i;
261 GdkKeymap *keymap = gdk_keymap_get_for_display(gdk_display_get_default());
262
263 if (ref_count_for_untranslated_keys == 0)
264 for (i = 0; i < sizeof(untranslated_keys) / sizeof(untranslated_keys[0]); i++)
265 gdk_keymap_get_entries_for_keyval(keymap,
266 untranslated_keys[i].keyval,
267 &untranslated_keys[i].keys,
268 &untranslated_keys[i].n_keys);
269 ref_count_for_untranslated_keys++;
270 }
271
272 /* Free the keymap entries */
vnc_display_keyval_free_entries(void)273 void vnc_display_keyval_free_entries(void)
274 {
275 size_t i;
276
277 if (ref_count_for_untranslated_keys == 0)
278 return;
279
280 ref_count_for_untranslated_keys--;
281 if (ref_count_for_untranslated_keys == 0)
282 for (i = 0; i < sizeof(untranslated_keys) / sizeof(untranslated_keys[0]); i++)
283 g_free(untranslated_keys[i].keys);
284
285 }
286
287 /* Get the keyval from the keycode without the level. */
vnc_display_keyval_from_keycode(guint keycode,guint keyval)288 guint vnc_display_keyval_from_keycode(guint keycode, guint keyval)
289 {
290 size_t i;
291 for (i = 0; i < sizeof(untranslated_keys) / sizeof(untranslated_keys[0]); i++) {
292 if (keycode == untranslated_keys[i].keys[0].keycode) {
293 return untranslated_keys[i].keyval;
294 }
295 }
296
297 return keyval;
298 }
299 /*
300 * Local variables:
301 * c-indent-level: 8
302 * c-basic-offset: 8
303 * tab-width: 8
304 * End:
305 */
306