1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include "config.h"
21 #include "keydef.h"
22 #include "log.h"
23
24 #include <assert.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 /**
31 * All known keys.
32 */
33 const guaclog_keydef known_keys[] = {
34 { 0xFE03, "AltGr", "", true },
35 { 0xFF08, "Backspace" },
36 { 0xFF09, "Tab" },
37 { 0xFF0B, "Clear" },
38 { 0xFF0D, "Return", "\n" },
39 { 0xFF13, "Pause" },
40 { 0xFF14, "Scroll" },
41 { 0xFF15, "SysReq" },
42 { 0xFF1B, "Escape" },
43 { 0xFF50, "Home" },
44 { 0xFF51, "Left" },
45 { 0xFF52, "Up" },
46 { 0xFF53, "Right" },
47 { 0xFF54, "Down" },
48 { 0xFF55, "Page Up" },
49 { 0xFF56, "Page Down" },
50 { 0xFF57, "End" },
51 { 0xFF63, "Insert" },
52 { 0xFF65, "Undo" },
53 { 0xFF6A, "Help" },
54 { 0xFF7F, "Num" },
55 { 0xFF80, "Space", " " },
56 { 0xFF8D, "Enter", "\n" },
57 { 0xFF95, "Home" },
58 { 0xFF96, "Left" },
59 { 0xFF97, "Up" },
60 { 0xFF98, "Right" },
61 { 0xFF99, "Down" },
62 { 0xFF9A, "Page Up" },
63 { 0xFF9B, "Page Down" },
64 { 0xFF9C, "End" },
65 { 0xFF9E, "Insert" },
66 { 0xFFAA, "*", "*" },
67 { 0xFFAB, "+", "+" },
68 { 0xFFAD, "-", "-" },
69 { 0xFFAE, ".", "." },
70 { 0xFFAF, "/", "/" },
71 { 0xFFB0, "0", "0" },
72 { 0xFFB1, "1", "1" },
73 { 0xFFB2, "2", "2" },
74 { 0xFFB3, "3", "3" },
75 { 0xFFB4, "4", "4" },
76 { 0xFFB5, "5", "5" },
77 { 0xFFB6, "6", "6" },
78 { 0xFFB7, "7", "7" },
79 { 0xFFB8, "8", "8" },
80 { 0xFFB9, "9", "9" },
81 { 0xFFBE, "F1" },
82 { 0xFFBF, "F2" },
83 { 0xFFC0, "F3" },
84 { 0xFFC1, "F4" },
85 { 0xFFC2, "F5" },
86 { 0xFFC3, "F6" },
87 { 0xFFC4, "F7" },
88 { 0xFFC5, "F8" },
89 { 0xFFC6, "F9" },
90 { 0xFFC7, "F10" },
91 { 0xFFC8, "F11" },
92 { 0xFFC9, "F12" },
93 { 0xFFCA, "F13" },
94 { 0xFFCB, "F14" },
95 { 0xFFCC, "F15" },
96 { 0xFFCD, "F16" },
97 { 0xFFCE, "F17" },
98 { 0xFFCF, "F18" },
99 { 0xFFD0, "F19" },
100 { 0xFFD1, "F20" },
101 { 0xFFD2, "F21" },
102 { 0xFFD3, "F22" },
103 { 0xFFD4, "F23" },
104 { 0xFFD5, "F24" },
105 { 0xFFE1, "Shift", "", true },
106 { 0xFFE2, "Shift", "", true },
107 { 0xFFE3, "Ctrl", NULL, true },
108 { 0xFFE4, "Ctrl", NULL, true },
109 { 0xFFE5, "Caps" },
110 { 0xFFE7, "Meta", NULL, true },
111 { 0xFFE8, "Meta", NULL, true },
112 { 0xFFE9, "Alt", NULL, true },
113 { 0xFFEA, "Alt", NULL, true },
114 { 0xFFEB, "Super", NULL, true },
115 { 0xFFEC, "Super", NULL, true },
116 { 0xFFED, "Hyper", NULL, true },
117 { 0xFFEE, "Hyper", NULL, true },
118 { 0xFFFF, "Delete" }
119 };
120
121 /**
122 * Comparator for the standard bsearch() function which compares an integer
123 * keysym against the keysym associated with a guaclog_keydef.
124 *
125 * @param key
126 * The key value being compared against the member. This MUST be the
127 * keysym value, passed through typecasting to an intptr_t (NOT a pointer
128 * to the int itself).
129 *
130 * @param member
131 * The member within the known_keys array being compared against the given
132 * key.
133 *
134 * @return
135 * Zero if the given keysym is equal to that of the given member, a
136 * positive value if the given keysym is greater than that of the given
137 * member, or a negative value if the given keysym is less than that of the
138 * given member.
139 */
guaclog_keydef_bsearch_compare(const void * key,const void * member)140 static int guaclog_keydef_bsearch_compare(const void* key,
141 const void* member) {
142
143 int keysym = (int) ((intptr_t) key);
144 guaclog_keydef* current = (guaclog_keydef*) member;
145
146 /* Compare given keysym to keysym of current member */
147 return keysym - current->keysym;
148
149 }
150
151 /**
152 * Searches through the known_keys array of known keys for the name of the key
153 * having the given keysym, returning a pointer to the static guaclog_keydef
154 * within the array if found.
155 *
156 * @param keysym
157 * The X11 keysym of the key.
158 *
159 * @return
160 * A pointer to the static guaclog_keydef associated with the given keysym,
161 * or NULL if the key could not be found.
162 */
guaclog_get_known_key(int keysym)163 static guaclog_keydef* guaclog_get_known_key(int keysym) {
164
165 /* Search through known keys for given keysym */
166 return bsearch((void*) ((intptr_t) keysym),
167 known_keys, sizeof(known_keys) / sizeof(known_keys[0]),
168 sizeof(known_keys[0]), guaclog_keydef_bsearch_compare);
169
170 }
171
172 /**
173 * Returns a statically-allocated guaclog_keydef representing an unknown key,
174 * deriving the name of the key from the hexadecimal value of the keysym.
175 *
176 * @param keysym
177 * The X11 keysym of the key.
178 *
179 * @return
180 * A statically-allocated guaclog_keydef representing the key associated
181 * with the given keysym.
182 */
guaclog_get_unknown_key(int keysym)183 static guaclog_keydef* guaclog_get_unknown_key(int keysym) {
184
185 static char unknown_keydef_name[64];
186 static guaclog_keydef unknown_keydef;
187
188 /* Write keysym as hex */
189 int size = snprintf(unknown_keydef_name, sizeof(unknown_keydef_name),
190 "0x%X", keysym);
191
192 /* Hex string is guaranteed to fit within the provided 64 bytes */
193 assert(size < sizeof(unknown_keydef_name));
194
195 /* Return static key definition */
196 unknown_keydef.keysym = keysym;
197 unknown_keydef.name = unknown_keydef_name;
198 return &unknown_keydef;
199
200 }
201
202 /**
203 * Returns a statically-allocated guaclog_keydef representing the key
204 * associated with the given keysym, deriving the name and value of the key
205 * using its corresponding Unicode character.
206 *
207 * @param keysym
208 * The X11 keysym of the key.
209 *
210 * @return
211 * A statically-allocated guaclog_keydef representing the key associated
212 * with the given keysym, or NULL if the given keysym has no corresponding
213 * Unicode character.
214 */
guaclog_get_unicode_key(int keysym)215 static guaclog_keydef* guaclog_get_unicode_key(int keysym) {
216
217 static char unicode_keydef_name[8];
218
219 static guaclog_keydef unicode_keydef;
220
221 int i;
222 int mask, bytes;
223
224 /* Translate only if keysym maps to Unicode */
225 if (keysym < 0x00 || (keysym > 0xFF && (keysym | 0xFFFF) != 0x0100FFFF))
226 return NULL;
227
228 int codepoint = keysym & 0xFFFF;
229
230 /* Determine size and initial byte mask */
231 if (codepoint <= 0x007F) {
232 mask = 0x00;
233 bytes = 1;
234 }
235 else if (codepoint <= 0x7FF) {
236 mask = 0xC0;
237 bytes = 2;
238 }
239 else {
240 assert(codepoint <= 0xFFFF);
241 mask = 0xE0;
242 bytes = 3;
243 }
244
245 /* Offset buffer by size */
246 char* key_name = unicode_keydef_name + bytes;
247
248 /* Add null terminator */
249 *(key_name--) = '\0';
250
251 /* Add trailing bytes, if any */
252 for (i=1; i<bytes; i++) {
253 *(key_name--) = 0x80 | (codepoint & 0x3F);
254 codepoint >>= 6;
255 }
256
257 /* Set initial byte */
258 *key_name = mask | codepoint;
259
260 /* Return static key definition */
261 unicode_keydef.keysym = keysym;
262 unicode_keydef.name = unicode_keydef.value = unicode_keydef_name;
263 unicode_keydef.modifier = false;
264 return &unicode_keydef;
265
266 }
267
268 /**
269 * Copies the given guaclog_keydef into a newly-allocated guaclog_keydef
270 * structure. The resulting guaclog_keydef must eventually be freed through a
271 * call to guaclog_keydef_free().
272 *
273 * @param keydef
274 * The guaclog_keydef to copy.
275 *
276 * @return
277 * A newly-allocated guaclog_keydef structure copied from the given
278 * guaclog_keydef.
279 */
guaclog_copy_key(guaclog_keydef * keydef)280 static guaclog_keydef* guaclog_copy_key(guaclog_keydef* keydef) {
281
282 guaclog_keydef* copy = malloc(sizeof(guaclog_keydef));
283
284 /* Always copy keysym and name */
285 copy->keysym = keydef->keysym;
286 copy->name = strdup(keydef->name);
287 copy->modifier = keydef->modifier;
288
289 /* Copy value only if defined */
290 if (keydef->value != NULL)
291 copy->value = strdup(keydef->value);
292 else
293 copy->value = NULL;
294
295 return copy;
296
297 }
298
guaclog_keydef_alloc(int keysym)299 guaclog_keydef* guaclog_keydef_alloc(int keysym) {
300
301 guaclog_keydef* keydef;
302
303 /* Check list of known keys first */
304 keydef = guaclog_get_known_key(keysym);
305 if (keydef != NULL)
306 return guaclog_copy_key(keydef);
307
308 /* Failing that, attempt to translate straight into a Unicode character */
309 keydef = guaclog_get_unicode_key(keysym);
310 if (keydef != NULL)
311 return guaclog_copy_key(keydef);
312
313 /* Key not known */
314 guaclog_log(GUAC_LOG_DEBUG, "Definition not found for key 0x%X.", keysym);
315 return guaclog_copy_key(guaclog_get_unknown_key(keysym));
316
317 }
318
guaclog_keydef_free(guaclog_keydef * keydef)319 void guaclog_keydef_free(guaclog_keydef* keydef) {
320
321 /* Ignore NULL keydef */
322 if (keydef == NULL)
323 return;
324
325 free(keydef->name);
326 free(keydef->value);
327 free(keydef);
328
329 }
330
331