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