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));