Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / libfreerdp-kbd / layouts_xkb.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * XKB-based Keyboard Mapping to Microsoft Keyboard System
4  *
5  * Copyright 2009 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
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
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #ifdef WITH_XKBFILE
25 #include <X11/Xlib.h>
26 #include <X11/XKBlib.h>
27 #include <X11/extensions/XKBfile.h>
28 #include <X11/extensions/XKBrules.h>
29 #endif
30
31 #include "libkbd.h"
32 #include <freerdp/kbd/vkcodes.h>
33 #include "x_layout_id_table.h"
34
35 #include "layouts_xkb.h"
36
37 #ifndef KEYMAP_PATH
38 #define KEYMAP_PATH     "/usr/local/freerdp/keymaps"
39 #endif
40
41 #ifdef WITH_XKBFILE
42
43 int init_xkb(void* dpy)
44 {
45         return XkbQueryExtension(dpy, NULL, NULL, NULL, NULL, NULL);
46 }
47
48 /* return substring starting after nth comma, ending at following comma */
49 static char* comma_substring(char* s, int n)
50 {
51         char *p;
52
53         if (!s)
54                 return "";
55
56         while (n-- > 0)
57         {
58                 if (!(p = strchr(s, ',')))
59                         break;
60
61                 s = p + 1;
62         }
63
64         if ((p = strchr(s, ',')))
65                 *p = 0;
66
67         return s;
68 }
69
70 unsigned int detect_keyboard_layout_from_xkb(void* dpy)
71 {
72         char *layout, *variant;
73         unsigned int keyboard_layout = 0, group = 0;
74         XkbRF_VarDefsRec rules_names;
75         XKeyboardState coreKbdState;
76         XkbStateRec state;
77
78         DEBUG_KBD("display: %p", dpy);
79
80         if (dpy && XkbRF_GetNamesProp(dpy, NULL, &rules_names))
81         {
82                 DEBUG_KBD("layouts: %s", rules_names.layout ? rules_names.layout : "");
83                 DEBUG_KBD("variants: %s", rules_names.variant ? rules_names.variant : "");
84
85                 XGetKeyboardControl(dpy, &coreKbdState);
86
87                 if (XkbGetState(dpy, XkbUseCoreKbd, &state) == Success)
88                         group = state.group;
89
90                 DEBUG_KBD("group: %d", state.group);
91
92                 layout = comma_substring(rules_names.layout, group);
93                 variant = comma_substring(rules_names.variant, group);
94
95                 DEBUG_KBD("layout: %s", layout ? layout : "");
96                 DEBUG_KBD("variant: %s", variant ? variant : "");
97
98                 keyboard_layout = find_keyboard_layout_in_xorg_rules(layout, variant);
99
100                 free(rules_names.model);
101                 free(rules_names.layout);
102                 free(rules_names.variant);
103                 free(rules_names.options);
104         }
105
106         return keyboard_layout;
107 }
108
109 int init_keycodes_from_xkb(void* dpy, RdpScancodes x_keycode_to_rdp_scancode, uint8 rdp_scancode_to_x_keycode[256][2])
110 {
111         int ret = 0;
112         XkbDescPtr xkb;
113
114         if (dpy && (xkb = XkbGetMap(dpy, 0, XkbUseCoreKbd)))
115         {
116                 if (XkbGetNames(dpy, XkbKeyNamesMask, xkb) == Success)
117                 {
118                         int i, j;
119                         char buf[5] = {42, 42, 42, 42, 0}; /* end-of-string at pos 5 */
120
121                         for (i = xkb->min_key_code; i <= xkb->max_key_code; i++)
122                         {
123                                 memcpy(buf, xkb->names->keys[i].name, 4);
124
125                                 /* TODO: Use more efficient search ... but it is so fast that it doesn't matter */
126                                 j = sizeof(virtualKeyboard) / sizeof(virtualKeyboard[0]) - 1;
127
128                                 while (j >= 0)
129                                 {
130                                         if (virtualKeyboard[j].x_keyname && !strcmp(buf, virtualKeyboard[j].x_keyname))
131                                                 break;
132                                         j--;
133                                 }
134
135                                 if (j >= 0)
136                                 {
137                                         DEBUG_KBD("X keycode %3d has keyname %-4s -> RDP scancode %d/%d",
138                                                         i, buf, virtualKeyboard[j].extended, virtualKeyboard[j].scancode);
139
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;
143
144                                         if (x_keycode_to_rdp_scancode[i].extended)
145                                                 rdp_scancode_to_x_keycode[virtualKeyboard[j].scancode][1] = i;
146                                         else
147                                                 rdp_scancode_to_x_keycode[virtualKeyboard[j].scancode][0] = i;
148                                 }
149                                 else
150                                 {
151                                         DEBUG_KBD("X key code %3d has keyname %-4s -> ??? - not found", i, buf);
152                                 }
153                         }
154                         ret = 1;
155                 }
156                 XkbFreeKeyboard(xkb, 0, 1);
157         }
158         return ret;
159 }
160
161 #else
162
163 /* Default built-in keymap */
164 static const KeycodeToVkcode defaultKeycodeToVkcode =
165 {
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
182 };
183
184 static int load_xkb_keyboard(KeycodeToVkcode map, char* kbd)
185 {
186         char* pch;
187         char *beg, *end;
188         char* home;
189         char buffer[1024] = "";
190         char xkbfile[256] = "";
191         char xkbfilepath[512] = "";
192         char xkbmap[256] = "";
193         char xkbinc[256] = "";
194
195         FILE* fp;
196         int kbdFound = 0;
197
198         int i = 0;
199         int keycode = 0;
200         char keycodeString[32] = "";
201         char vkcodeName[128] = "";
202
203         beg = kbd;
204
205         /* Extract file name and keymap name */
206         if ((end = strrchr(kbd, '(')) != NULL)
207         {
208                 strncpy(xkbfile, &kbd[beg - kbd], end - beg);
209
210                 beg = end + 1;
211                 if ((end = strrchr(kbd, ')')) != NULL)
212                 {
213                         strncpy(xkbmap, &kbd[beg - kbd], end - beg);
214                         xkbmap[end - beg] = '\0';
215                 }
216         }
217         else
218         {
219                 /* The keyboard name is the same as the file name */
220                 strcpy(xkbfile, kbd);
221                 strcpy(xkbmap, kbd);
222         }
223
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);
227
228         /*
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.
234          */
235
236         if ((fp = fopen(xkbfilepath, "r")) == NULL)
237         {
238                 /* Look first in path given at compile time (install path) */
239                 snprintf(xkbfilepath, sizeof(xkbfilepath), "%s/%s", KEYMAP_PATH, xkbfile);
240
241                 if ((fp = fopen(xkbfilepath, "r")) == NULL)
242                 {
243                         /* If ran from the root of the source tree */
244                         snprintf(xkbfilepath, sizeof(xkbfilepath), "./keymaps/%s", xkbfile);
245
246                         /* If ran from the client directory */
247                         if((fp = fopen(xkbfilepath, "r")) == NULL)
248                                 snprintf(xkbfilepath, sizeof(xkbfilepath), "../../keymaps/%s", xkbfile);
249
250                         if ((fp = fopen(xkbfilepath, "r")) == NULL)
251                         {
252                                 /* File wasn't found in the source tree, try ~/.freerdp/ folder */
253                                 if ((home = getenv("HOME")) == NULL)
254                                         return 0;
255
256                                 /* Get path to file in ~/.freerdp/ folder */
257                                 snprintf(xkbfilepath, sizeof(xkbfilepath), "%s/.freerdp/keymaps/%s", home, xkbfile);
258
259                                 if ((fp = fopen(xkbfilepath, "r")) == NULL)
260                                 {
261                                         /* Try /usr/share/freerdp folder */
262                                         snprintf(xkbfilepath, sizeof(xkbfilepath), "/usr/share/freerdp/keymaps/%s", xkbfile);
263
264                                         if ((fp = fopen(xkbfilepath, "r")) == NULL)
265                                         {
266                                                 /* Try /usr/local/share/freerdp folder */
267                                                 snprintf(xkbfilepath, sizeof(xkbfilepath), "/usr/local/share/freerdp/keymaps/%s", xkbfile);
268
269                                                 if ((fp = fopen(xkbfilepath, "r")) == NULL)
270                                                 {
271                                                         /* Error: Could not find keymap */
272                                                         DEBUG_KBD("keymaps for %s not found", xkbfile);
273                                                         return 0;
274                                                 }
275                                         }
276                                 }
277                         }
278                 }
279         }
280
281         DEBUG_KBD("xkbfilepath: %s", xkbfilepath);
282
283         while(fgets(buffer, sizeof(buffer), fp) != NULL)
284         {
285                 if (buffer[0] == '#')
286                 {
287                         continue; /* Skip comments */
288                 }
289
290                 if (kbdFound)
291                 {
292                         /* Closing curly bracket and semicolon */
293                         if ((pch = strstr(buffer, "};")) != NULL)
294                         {
295                                 break;
296                         }
297                         else if ((pch = strstr(buffer, "VK_")) != NULL)
298                         {
299                                 /* The end is delimited by the first white space */
300                                 end = strcspn(pch, " \t\n\0") + pch;
301
302                                 /* We copy the virtual key code name in a string */
303                                 beg = pch;
304                                 strncpy(vkcodeName, beg, end - beg);
305                                 vkcodeName[end - beg] = '\0';
306
307                                 /* Now we want to extract the virtual key code itself which is in between '<' and '>' */
308                                 if ((beg = strchr(pch + 3, '<')) == NULL)
309                                         break;
310                                 else
311                                         beg++;
312
313                                 if ((end = strchr(beg, '>')) == NULL)
314                                         break;
315
316                                 /* We copy the string representing the number in a string */
317                                 strncpy(keycodeString, beg, end - beg);
318                                 keycodeString[end - beg] = '\0';
319
320                                 /* Convert the string representing the code to an integer */
321                                 keycode = atoi(keycodeString);
322
323                                 /* Make sure it is a valid keycode */
324                                 if (keycode < 0 || keycode > 255)
325                                         break;
326
327                                 /* Load this key mapping in the keyboard mapping */
328                                 for(i = 0; i < sizeof(virtualKeyboard) / sizeof(virtualKey); i++)
329                                 {
330                                         if (strcmp(vkcodeName, virtualKeyboard[i].name) == 0)
331                                         {
332                                                 map[keycode] = i;
333                                         }
334                                 }
335                         }
336                         else if ((pch = strstr(buffer, ": extends")) != NULL)
337                         {
338                                 /*
339                                  * This map extends another keymap We extract its name
340                                  * and we recursively load the keymap we need to include.
341                                  */
342
343                                 if ((beg = strchr(pch + sizeof(": extends"), '"')) == NULL)
344                                         break;
345                                 beg++;
346
347                                 if ((end = strchr(beg, '"')) == NULL)
348                                         break;
349
350                                 strncpy(xkbinc, beg, end - beg);
351                                 xkbinc[end - beg] = '\0';
352
353                                 load_xkb_keyboard(map, xkbinc); /* Load included keymap */
354                         }
355                 }
356                 else if ((pch = strstr(buffer, "keyboard")) != NULL)
357                 {
358                         /* Keyboard map identifier */
359                         if ((beg = strchr(pch + sizeof("keyboard"), '"')) == NULL)
360                                 break;
361                         beg++;
362
363                         if ((end = strchr(beg, '"')) == NULL)
364                                 break;
365
366                         pch = beg;
367                         buffer[end - beg] = '\0';
368
369                         /* Does it match our keymap name? */
370                         if (strncmp(xkbmap, pch, strlen(xkbmap)) == 0)
371                                 kbdFound = 1;
372                 }
373         }
374
375         fclose(fp); /* Don't forget to close file */
376
377         return 1;
378 }
379
380 void load_keyboard_map(KeycodeToVkcode keycodeToVkcode, char *xkbfile)
381 {
382         char* kbd;
383         char* xkbfileEnd;
384         int keymapLoaded = 0;
385
386         memset(keycodeToVkcode, 0, sizeof(keycodeToVkcode));
387
388         kbd = xkbfile;
389         xkbfileEnd = xkbfile + strlen(xkbfile);
390
391 #ifdef __APPLE__
392         /* Apple X11 breaks XKB detection */
393         keymapLoaded += load_xkb_keyboard(keycodeToVkcode, "macosx(macosx)");
394 #else
395         do
396         {
397                 /* Multiple maps are separated by '+' */
398                 int kbdlen = strcspn(kbd + 1, "+") + 1;
399                 kbd[kbdlen] = '\0';
400
401                 /* Load keyboard map */
402                 keymapLoaded += load_xkb_keyboard(keycodeToVkcode, kbd);
403
404                 kbd += kbdlen + 1;
405         }
406         while (kbd < xkbfileEnd);
407 #endif
408
409         DEBUG_KBD("loaded %d keymaps", keymapLoaded);
410         if (keymapLoaded <= 0)
411         {
412                 /* No keymap was loaded, load default hard-coded keymap */
413                 DEBUG_KBD("using default keymap");
414                 memcpy(keycodeToVkcode, defaultKeycodeToVkcode, sizeof(keycodeToVkcode));
415         }
416 }
417
418 #endif