2 * FreeRDP: A Remote Desktop Protocol Client
3 * XKB-based Keyboard Mapping to Microsoft Keyboard System
5 * Copyright 2009 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
26 #include <X11/XKBlib.h>
27 #include <X11/extensions/XKBfile.h>
28 #include <X11/extensions/XKBrules.h>
32 #include <freerdp/kbd/vkcodes.h>
33 #include "x_layout_id_table.h"
35 #include "layouts_xkb.h"
38 #define KEYMAP_PATH "/usr/local/freerdp/keymaps"
43 int init_xkb(void* dpy)
45 return XkbQueryExtension(dpy, NULL, NULL, NULL, NULL, NULL);
48 /* return substring starting after nth comma, ending at following comma */
49 static char* comma_substring(char* s, int n)
58 if (!(p = strchr(s, ',')))
64 if ((p = strchr(s, ',')))
70 unsigned int detect_keyboard_layout_from_xkb(void* dpy)
72 char *layout, *variant;
73 unsigned int keyboard_layout = 0, group = 0;
74 XkbRF_VarDefsRec rules_names;
75 XKeyboardState coreKbdState;
78 DEBUG_KBD("display: %p", dpy);
80 if (dpy && XkbRF_GetNamesProp(dpy, NULL, &rules_names))
82 DEBUG_KBD("layouts: %s", rules_names.layout ? rules_names.layout : "");
83 DEBUG_KBD("variants: %s", rules_names.variant ? rules_names.variant : "");
85 XGetKeyboardControl(dpy, &coreKbdState);
87 if (XkbGetState(dpy, XkbUseCoreKbd, &state) == Success)
90 DEBUG_KBD("group: %d", state.group);
92 layout = comma_substring(rules_names.layout, group);
93 variant = comma_substring(rules_names.variant, group);
95 DEBUG_KBD("layout: %s", layout ? layout : "");
96 DEBUG_KBD("variant: %s", variant ? variant : "");
98 keyboard_layout = find_keyboard_layout_in_xorg_rules(layout, variant);
100 free(rules_names.model);
101 free(rules_names.layout);
102 free(rules_names.variant);
103 free(rules_names.options);
106 return keyboard_layout;
109 int init_keycodes_from_xkb(void* dpy, RdpScancodes x_keycode_to_rdp_scancode, uint8 rdp_scancode_to_x_keycode[256][2])
114 if (dpy && (xkb = XkbGetMap(dpy, 0, XkbUseCoreKbd)))
116 if (XkbGetNames(dpy, XkbKeyNamesMask, xkb) == Success)
119 char buf[5] = {42, 42, 42, 42, 0}; /* end-of-string at pos 5 */
121 for (i = xkb->min_key_code; i <= xkb->max_key_code; i++)
123 memcpy(buf, xkb->names->keys[i].name, 4);
125 /* TODO: Use more efficient search ... but it is so fast that it doesn't matter */
126 j = sizeof(virtualKeyboard) / sizeof(virtualKeyboard[0]) - 1;
130 if (virtualKeyboard[j].x_keyname && !strcmp(buf, virtualKeyboard[j].x_keyname))
137 DEBUG_KBD("X keycode %3d has keyname %-4s -> RDP scancode %d/%d",
138 i, buf, virtualKeyboard[j].extended, virtualKeyboard[j].scancode);
140 x_keycode_to_rdp_scancode[i].extended = virtualKeyboard[j].extended;
141 x_keycode_to_rdp_scancode[i].keycode = virtualKeyboard[j].scancode;
142 x_keycode_to_rdp_scancode[i].keyname = virtualKeyboard[j].x_keyname;
144 if (x_keycode_to_rdp_scancode[i].extended)
145 rdp_scancode_to_x_keycode[virtualKeyboard[j].scancode][1] = i;
147 rdp_scancode_to_x_keycode[virtualKeyboard[j].scancode][0] = i;
151 DEBUG_KBD("X key code %3d has keyname %-4s -> ??? - not found", i, buf);
156 XkbFreeKeyboard(xkb, 0, 1);
163 /* Default built-in keymap */
164 static const KeycodeToVkcode defaultKeycodeToVkcode =
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
167 0x37, 0x38, 0x39, 0x30, 0xBD, 0xBB, 0x08, 0x09, 0x51, 0x57, 0x45, 0x52, 0x54, 0x59, 0x55, 0x49,
168 0x4F, 0x50, 0xDB, 0xDD, 0x0D, 0xA2, 0x41, 0x53, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B, 0x4C, 0xBA,
169 0xDE, 0xC0, 0xA0, 0x00, 0x5A, 0x58, 0x43, 0x56, 0x42, 0x4E, 0x4D, 0xBC, 0xBE, 0xBF, 0xA1, 0x6A,
170 0x12, 0x20, 0x14, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x90, 0x91, 0x67,
171 0x68, 0x69, 0x6D, 0x64, 0x65, 0x66, 0x6B, 0x61, 0x62, 0x63, 0x60, 0x6E, 0x00, 0x00, 0x00, 0x7A,
172 0x7B, 0x24, 0x26, 0x21, 0x25, 0x00, 0x27, 0x23, 0x28, 0x22, 0x2D, 0x2E, 0x0D, 0xA3, 0x13, 0x2C,
173 0x6F, 0x12, 0x00, 0x5B, 0x5C, 0x5D, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
184 static int load_xkb_keyboard(KeycodeToVkcode map, char* kbd)
189 char buffer[1024] = "";
190 char xkbfile[256] = "";
191 char xkbfilepath[512] = "";
192 char xkbmap[256] = "";
193 char xkbinc[256] = "";
200 char keycodeString[32] = "";
201 char vkcodeName[128] = "";
205 /* Extract file name and keymap name */
206 if ((end = strrchr(kbd, '(')) != NULL)
208 strncpy(xkbfile, &kbd[beg - kbd], end - beg);
211 if ((end = strrchr(kbd, ')')) != NULL)
213 strncpy(xkbmap, &kbd[beg - kbd], end - beg);
214 xkbmap[end - beg] = '\0';
219 /* The keyboard name is the same as the file name */
220 strcpy(xkbfile, kbd);
224 /* Get path to file relative to freerdp's directory */
225 snprintf(xkbfilepath, sizeof(xkbfilepath), "keymaps/%s", xkbfile);
226 DEBUG_KBD("Loading keymap %s, first trying %s", kbd, xkbfilepath);
229 * Open the file for reading only
230 * It can happen that the same file is opened twice at the same time
231 * in order to load multiple keyboard maps from the same file, but
232 * it does not matter: files can be opened as many times as we want
233 * when it is for reading only.
236 if ((fp = fopen(xkbfilepath, "r")) == NULL)
238 /* Look first in path given at compile time (install path) */
239 snprintf(xkbfilepath, sizeof(xkbfilepath), "%s/%s", KEYMAP_PATH, xkbfile);
241 if ((fp = fopen(xkbfilepath, "r")) == NULL)
243 /* If ran from the root of the source tree */
244 snprintf(xkbfilepath, sizeof(xkbfilepath), "./keymaps/%s", xkbfile);
246 /* If ran from the client directory */
247 if((fp = fopen(xkbfilepath, "r")) == NULL)
248 snprintf(xkbfilepath, sizeof(xkbfilepath), "../../keymaps/%s", xkbfile);
250 if ((fp = fopen(xkbfilepath, "r")) == NULL)
252 /* File wasn't found in the source tree, try ~/.freerdp/ folder */
253 if ((home = getenv("HOME")) == NULL)
256 /* Get path to file in ~/.freerdp/ folder */
257 snprintf(xkbfilepath, sizeof(xkbfilepath), "%s/.freerdp/keymaps/%s", home, xkbfile);
259 if ((fp = fopen(xkbfilepath, "r")) == NULL)
261 /* Try /usr/share/freerdp folder */
262 snprintf(xkbfilepath, sizeof(xkbfilepath), "/usr/share/freerdp/keymaps/%s", xkbfile);
264 if ((fp = fopen(xkbfilepath, "r")) == NULL)
266 /* Try /usr/local/share/freerdp folder */
267 snprintf(xkbfilepath, sizeof(xkbfilepath), "/usr/local/share/freerdp/keymaps/%s", xkbfile);
269 if ((fp = fopen(xkbfilepath, "r")) == NULL)
271 /* Error: Could not find keymap */
272 DEBUG_KBD("keymaps for %s not found", xkbfile);
281 DEBUG_KBD("xkbfilepath: %s", xkbfilepath);
283 while(fgets(buffer, sizeof(buffer), fp) != NULL)
285 if (buffer[0] == '#')
287 continue; /* Skip comments */
292 /* Closing curly bracket and semicolon */
293 if ((pch = strstr(buffer, "};")) != NULL)
297 else if ((pch = strstr(buffer, "VK_")) != NULL)
299 /* The end is delimited by the first white space */
300 end = strcspn(pch, " \t\n\0") + pch;
302 /* We copy the virtual key code name in a string */
304 strncpy(vkcodeName, beg, end - beg);
305 vkcodeName[end - beg] = '\0';
307 /* Now we want to extract the virtual key code itself which is in between '<' and '>' */
308 if ((beg = strchr(pch + 3, '<')) == NULL)
313 if ((end = strchr(beg, '>')) == NULL)
316 /* We copy the string representing the number in a string */
317 strncpy(keycodeString, beg, end - beg);
318 keycodeString[end - beg] = '\0';
320 /* Convert the string representing the code to an integer */
321 keycode = atoi(keycodeString);
323 /* Make sure it is a valid keycode */
324 if (keycode < 0 || keycode > 255)
327 /* Load this key mapping in the keyboard mapping */
328 for(i = 0; i < sizeof(virtualKeyboard) / sizeof(virtualKey); i++)
330 if (strcmp(vkcodeName, virtualKeyboard[i].name) == 0)
336 else if ((pch = strstr(buffer, ": extends")) != NULL)
339 * This map extends another keymap We extract its name
340 * and we recursively load the keymap we need to include.
343 if ((beg = strchr(pch + sizeof(": extends"), '"')) == NULL)
347 if ((end = strchr(beg, '"')) == NULL)
350 strncpy(xkbinc, beg, end - beg);
351 xkbinc[end - beg] = '\0';
353 load_xkb_keyboard(map, xkbinc); /* Load included keymap */
356 else if ((pch = strstr(buffer, "keyboard")) != NULL)
358 /* Keyboard map identifier */
359 if ((beg = strchr(pch + sizeof("keyboard"), '"')) == NULL)
363 if ((end = strchr(beg, '"')) == NULL)
367 buffer[end - beg] = '\0';
369 /* Does it match our keymap name? */
370 if (strncmp(xkbmap, pch, strlen(xkbmap)) == 0)
375 fclose(fp); /* Don't forget to close file */
380 void load_keyboard_map(KeycodeToVkcode keycodeToVkcode, char *xkbfile)
384 int keymapLoaded = 0;
386 memset(keycodeToVkcode, 0, sizeof(keycodeToVkcode));
389 xkbfileEnd = xkbfile + strlen(xkbfile);
392 /* Apple X11 breaks XKB detection */
393 keymapLoaded += load_xkb_keyboard(keycodeToVkcode, "macosx(macosx)");
397 /* Multiple maps are separated by '+' */
398 int kbdlen = strcspn(kbd + 1, "+") + 1;
401 /* Load keyboard map */
402 keymapLoaded += load_xkb_keyboard(keycodeToVkcode, kbd);
406 while (kbd < xkbfileEnd);
409 DEBUG_KBD("loaded %d keymaps", keymapLoaded);
410 if (keymapLoaded <= 0)
412 /* No keymap was loaded, load default hard-coded keymap */
413 DEBUG_KBD("using default keymap");
414 memcpy(keycodeToVkcode, defaultKeycodeToVkcode, sizeof(keycodeToVkcode));