FreeRDP
Loading...
Searching...
No Matches
client/X11/keyboard_x11.c
1
21#include <string.h>
22
23#include <X11/X.h>
24#include <X11/Xatom.h>
25#include <X11/Xlib.h>
26
27#include "xf_debug.h"
28#include "keyboard_x11.h"
29#include "xkb_layout_ids.h"
30#include "xf_utils.h"
31
32static BOOL parse_xkb_rule_names(char* xkb_rule, unsigned long num_bytes, char** layout,
33 char** variant)
34{
35 /* Sample output for "Canadian Multilingual Standard"
36 *
37 * _XKB_RULES_NAMES_BACKUP(STRING) = "xorg", "pc105", "ca", "multi", "magic"
38 *
39 * Format: "rules", "model", "layout", "variant", "options"
40 *
41 * Where "xorg" is the set of rules
42 * "pc105" the keyboard model
43 * "ca" the keyboard layout(s) (can also be something like 'us,uk')
44 * "multi" the keyboard layout variant(s) (in the examples, “,winkeys” - which means first
45 * layout uses some “default” variant and second uses “winkeys” variant)
46 * "magic" - configuration option (in the examples,
47 * “eurosign:e,lv3:ralt_switch,grp:rctrl_toggle”
48 * - three options)
49 */
50 for (size_t i = 0, index = 0; i < num_bytes; i++, index++)
51 {
52 char* ptr = xkb_rule + i;
53 i += strnlen(ptr, num_bytes - i);
54
55 switch (index)
56 {
57 case 0: // rules
58 break;
59 case 1: // model
60 break;
61 case 2: // layout
62 {
63 /* If multiple languages are present we just take the first one */
64 char* delimiter = strchr(ptr, ',');
65 if (delimiter)
66 *delimiter = '\0';
67 *layout = ptr;
68 break;
69 }
70 case 3: // variant
71 {
72 /* If multiple variants are present we just take the first one */
73 char* delimiter = strchr(ptr, ',');
74 if (delimiter)
75 *delimiter = '\0';
76 *variant = ptr;
77 }
78 break;
79 case 4: // option
80 break;
81 default:
82 break;
83 }
84 }
85 return TRUE;
86}
87
88static DWORD kbd_layout_id_from_x_property(wLog* log, Display* display, Window root,
89 char* property_name)
90{
91 char* layout = NULL;
92 char* variant = NULL;
93 char* rule = NULL;
94 Atom type = None;
95 int item_size = 0;
96 unsigned long items = 0;
97 unsigned long unread_items = 0;
98 DWORD layout_id = 0;
99
100 Atom property = XInternAtom(display, property_name, False);
101 if (property == None)
102 return 0;
103
104 if (LogDynAndXGetWindowProperty(log, display, root, property, 0, 1024, False, XA_STRING, &type,
105 &item_size, &items, &unread_items,
106 (unsigned char**)&rule) != Success)
107 return 0;
108
109 if (type != XA_STRING || item_size != 8 || unread_items != 0)
110 {
111 XFree(rule);
112 return 0;
113 }
114
115 parse_xkb_rule_names(rule, items, &layout, &variant);
116
117 DEBUG_X11("%s layout: %s, variant: %s", property_name, layout, variant);
118 layout_id = xf_find_keyboard_layout_in_xorg_rules(layout, variant);
119
120 XFree(rule);
121
122 return layout_id;
123}
124
125int xf_detect_keyboard_layout_from_xkb(wLog* log, DWORD* keyboardLayoutId)
126{
127 Display* display = XOpenDisplay(NULL);
128
129 if (!display)
130 return 0;
131
132 Window root = DefaultRootWindow(display);
133 if (!root)
134 return 0;
135
136 /* We start by looking for _XKB_RULES_NAMES_BACKUP which appears to be used by libxklavier */
137 DWORD id = kbd_layout_id_from_x_property(log, display, root, "_XKB_RULES_NAMES_BACKUP");
138
139 if (0 == id)
140 id = kbd_layout_id_from_x_property(log, display, root, "_XKB_RULES_NAMES");
141
142 if (0 != id)
143 *keyboardLayoutId = id;
144
145 LogDynAndXCloseDisplay(log, display);
146 return (int)id;
147}