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