xref: /haiku/src/add-ons/input_server/devices/keyboard/Keymap.cpp (revision 64e57f07ce6c09400c5515447e149e5cafbe46db)
1 /*
2  * Copyright 2004-2010, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jérôme Duval
7  */
8 
9 
10 #include "Keymap.h"
11 
12 #include <ByteOrder.h>
13 #include <File.h>
14 #include <InputServerTypes.h>
15 #include <Message.h>
16 #ifdef CONSOLED
17 #	include <FindDirectory.h>
18 #else
19 #	include <input_globals.h>
20 #endif
21 
22 #include <errno.h>
23 #include <new>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 
28 
29 static void
30 print_key(char *chars, int32 offset)
31 {
32 	int size = chars[offset++];
33 
34 	switch (size) {
35 		case 0:
36 			// Not mapped
37 			printf("N/A");
38 			break;
39 
40 		case 1:
41 			// 1-byte UTF-8/ASCII character
42 			printf("%c", chars[offset]);
43 			break;
44 
45 		default:
46 		{
47 			// 2-, 3-, or 4-byte UTF-8 character
48 			char *str = new (std::nothrow) char[size + 1];
49 			if (str == NULL)
50 				break;
51 
52 			strncpy(str, &(chars[offset]), size);
53 			str[size] = 0;
54 			printf("%s", str);
55 			delete [] str;
56 			break;
57 		}
58 	}
59 
60 	printf("\t");
61 }
62 
63 
64 //	#pragma mark -
65 
66 
67 void
68 Keymap::DumpKeymap()
69 {
70 	// Print a chart of the normal, shift, option, and option+shift
71 	// keys.
72 	printf("Key #\tNormal\tShift\tCaps\tC+S\tOption\tO+S\tO+C\tO+C+S\tControl\n");
73 
74 	for (int i = 0; i < 128; i++) {
75 		printf(" 0x%x\t", i);
76 		print_key(fChars, fKeys.normal_map[i]);
77 		print_key(fChars, fKeys.shift_map[i]);
78 		print_key(fChars, fKeys.caps_map[i]);
79 		print_key(fChars, fKeys.caps_shift_map[i]);
80 		print_key(fChars, fKeys.option_map[i]);
81 		print_key(fChars, fKeys.option_shift_map[i]);
82 		print_key(fChars, fKeys.option_caps_map[i]);
83 		print_key(fChars, fKeys.option_caps_shift_map[i]);
84 		print_key(fChars, fKeys.control_map[i]);
85 		printf("\n");
86 	}
87 }
88 
89 
90 Keymap::Keymap()
91 	:
92 	fChars(NULL)
93 {
94 #ifndef CONSOLED
95 	key_map *keys;
96 	_get_key_map(&keys, &fChars, &fCharsSize);
97 
98 	if (keys) {
99 		memcpy(&fKeys, keys, sizeof(key_map));
100 		free(keys);
101 	}
102 #endif
103 }
104 
105 
106 Keymap::~Keymap()
107 {
108 	free(fChars);
109 }
110 
111 
112 /* we need to know if a key is a modifier key to choose
113 	a valid key when several are pressed together
114 */
115 bool
116 Keymap::IsModifierKey(uint32 keyCode)
117 {
118 	return keyCode == fKeys.caps_key
119 		|| keyCode == fKeys.num_key
120 		|| keyCode == fKeys.scroll_key
121 		|| keyCode == fKeys.left_shift_key
122 		|| keyCode == fKeys.right_shift_key
123 		|| keyCode == fKeys.left_command_key
124 		|| keyCode == fKeys.right_command_key
125 		|| keyCode == fKeys.left_control_key
126 		|| keyCode == fKeys.right_control_key
127 		|| keyCode == fKeys.left_option_key
128 		|| keyCode == fKeys.right_option_key
129 		|| keyCode == fKeys.menu_key;
130 }
131 
132 
133 //! We need to know a modifier for a key
134 uint32
135 Keymap::Modifier(uint32 keyCode)
136 {
137 	if (keyCode == fKeys.caps_key)
138 		return B_CAPS_LOCK;
139 	if (keyCode == fKeys.num_key)
140 		return B_NUM_LOCK;
141 	if (keyCode == fKeys.scroll_key)
142 		return B_SCROLL_LOCK;
143 	if (keyCode == fKeys.left_shift_key)
144 		return B_LEFT_SHIFT_KEY | B_SHIFT_KEY;
145 	if (keyCode == fKeys.right_shift_key)
146 		return B_RIGHT_SHIFT_KEY | B_SHIFT_KEY;
147 	if (keyCode == fKeys.left_command_key)
148 		return B_LEFT_COMMAND_KEY | B_COMMAND_KEY;
149 	if (keyCode == fKeys.right_command_key)
150 		return B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
151 	if (keyCode == fKeys.left_control_key)
152 		return B_LEFT_CONTROL_KEY | B_CONTROL_KEY;
153 	if (keyCode == fKeys.right_control_key)
154 		return B_RIGHT_CONTROL_KEY | B_CONTROL_KEY;
155 	if (keyCode == fKeys.left_option_key)
156 		return B_LEFT_OPTION_KEY | B_OPTION_KEY;
157 	if (keyCode == fKeys.right_option_key)
158 		return B_RIGHT_OPTION_KEY | B_OPTION_KEY;
159 	if (keyCode == fKeys.menu_key)
160 		return B_MENU_KEY;
161 
162 	return 0;
163 }
164 
165 
166 uint32
167 Keymap::KeyForModifier(uint32 modifier)
168 {
169 	if (modifier == B_CAPS_LOCK)
170 		return fKeys.caps_key;
171 	if (modifier == B_NUM_LOCK)
172 		return fKeys.num_key;
173 	if (modifier == B_SCROLL_LOCK)
174 		return fKeys.scroll_key;
175 	if (modifier == B_LEFT_SHIFT_KEY || modifier == B_SHIFT_KEY)
176 		return fKeys.left_shift_key;
177 	if (modifier == B_RIGHT_SHIFT_KEY)
178 		return fKeys.right_shift_key;
179 	if (modifier == B_LEFT_COMMAND_KEY || modifier == B_COMMAND_KEY)
180 		return fKeys.left_command_key;
181 	if (modifier == B_RIGHT_COMMAND_KEY)
182 		return fKeys.right_command_key;
183 	if (modifier == B_LEFT_CONTROL_KEY || modifier == B_CONTROL_KEY)
184 		return fKeys.left_control_key;
185 	if (modifier == B_RIGHT_CONTROL_KEY)
186 		return fKeys.right_control_key;
187 	if (modifier == B_LEFT_OPTION_KEY || modifier == B_OPTION_KEY)
188 		return fKeys.left_option_key;
189 	if (modifier == B_RIGHT_OPTION_KEY)
190 		return fKeys.right_option_key;
191 	if (modifier == B_MENU_KEY)
192 		return fKeys.menu_key;
193 
194 	return 0;
195 }
196 
197 
198 //! Tell if a key is a dead key, needed for draw a dead key
199 uint8
200 Keymap::IsDeadKey(uint32 keyCode, uint32 modifiers)
201 {
202 	if (keyCode >= 256)
203 		return 0;
204 
205 	int32 offset;
206 	uint32 tableMask = 0;
207 
208 	switch (modifiers & 0xcf) {
209 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; tableMask = B_SHIFT_TABLE; break;
210 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; tableMask = B_CAPS_TABLE; break;
211 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; tableMask = B_CAPS_SHIFT_TABLE; break;
212 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; tableMask = B_OPTION_TABLE; break;
213 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; tableMask = B_OPTION_SHIFT_TABLE; break;
214 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; tableMask = B_OPTION_CAPS_TABLE; break;
215 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; tableMask = B_OPTION_CAPS_SHIFT_TABLE; break;
216 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; tableMask = B_CONTROL_TABLE; break;
217 		default: offset = fKeys.normal_map[keyCode]; tableMask = B_NORMAL_TABLE; break;
218 	}
219 
220 	if (offset <= 0 || offset > fCharsSize)
221 		return 0;
222 
223 	uint32 numBytes = fChars[offset];
224 	if (!numBytes)
225 		return 0;
226 
227 	char chars[4];
228 	strncpy(chars, &(fChars[offset+1]), numBytes );
229 	chars[numBytes] = 0;
230 
231 	int32 deadOffsets[] = {
232 		fKeys.acute_dead_key[1],
233 		fKeys.grave_dead_key[1],
234 		fKeys.circumflex_dead_key[1],
235 		fKeys.dieresis_dead_key[1],
236 		fKeys.tilde_dead_key[1]
237 	};
238 
239 	uint32 deadTables[] = {
240 		fKeys.acute_tables,
241 		fKeys.grave_tables,
242 		fKeys.circumflex_tables,
243 		fKeys.dieresis_tables,
244 		fKeys.tilde_tables
245 	};
246 
247 	for (int32 i = 0; i < 5; i++) {
248 		if ((deadTables[i] & tableMask) == 0)
249 			continue;
250 
251 		if (offset == deadOffsets[i])
252 			return i + 1;
253 
254 		uint32 deadNumBytes = fChars[deadOffsets[i]];
255 		if (!deadNumBytes)
256 			continue;
257 
258 		if (!strncmp(chars, &(fChars[deadOffsets[i] + 1]), deadNumBytes))
259 			return i + 1;
260 	}
261 	return 0;
262 }
263 
264 
265 //! Tell if a key is a dead second key, needed for draw a dead second key
266 bool
267 Keymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey)
268 {
269 	if (!activeDeadKey || keyCode >= 256)
270 		return false;
271 
272 	int32 offset;
273 
274 	switch (modifiers & 0xcf) {
275 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; break;
276 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; break;
277 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; break;
278 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; break;
279 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; break;
280 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; break;
281 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; break;
282 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; break;
283 		default: offset = fKeys.normal_map[keyCode]; break;
284 	}
285 
286 	if (offset <= 0 || offset > fCharsSize)
287 		return false;
288 
289 	uint32 numBytes = fChars[offset];
290 	if (!numBytes)
291 		return false;
292 
293 	int32* deadOffsets[] = {
294 		fKeys.acute_dead_key,
295 		fKeys.grave_dead_key,
296 		fKeys.circumflex_dead_key,
297 		fKeys.dieresis_dead_key,
298 		fKeys.tilde_dead_key
299 	};
300 
301 	int32 *deadOffset = deadOffsets[activeDeadKey-1];
302 
303 	for (int32 i=0; i<32; i++) {
304 		if (offset == deadOffset[i])
305 			return true;
306 
307 		uint32 deadNumBytes = fChars[deadOffset[i]];
308 		if (!deadNumBytes)
309 			continue;
310 
311 		if (!strncmp(&(fChars[offset+1]), &(fChars[deadOffset[i]+1]), deadNumBytes))
312 			return true;
313 
314 		i++;
315 	}
316 
317 	return false;
318 }
319 
320 
321 //! Get the char for a key given modifiers and active dead key
322 void
323 Keymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey,
324 	char** chars, int32* numBytes)
325 {
326 	*numBytes = 0;
327 	*chars = NULL;
328 
329 	if (keyCode >= 256)
330 		return;
331 
332 	// here we take NUMLOCK into account
333 	if (modifiers & B_NUM_LOCK) {
334 		switch (keyCode) {
335 			case 0x37:
336 			case 0x38:
337 			case 0x39:
338 			case 0x48:
339 			case 0x49:
340 			case 0x4a:
341 			case 0x58:
342 			case 0x59:
343 			case 0x5a:
344 			case 0x64:
345 			case 0x65:
346 				modifiers ^= B_SHIFT_KEY;
347 				break;
348 		}
349 	}
350 
351 	int32 offset;
352 
353 	// here we choose the right map given the modifiers
354 	switch (modifiers & 0xcf) {
355 		case B_SHIFT_KEY: offset = fKeys.shift_map[keyCode]; break;
356 		case B_CAPS_LOCK: offset = fKeys.caps_map[keyCode]; break;
357 		case B_CAPS_LOCK|B_SHIFT_KEY: offset = fKeys.caps_shift_map[keyCode]; break;
358 		case B_OPTION_KEY: offset = fKeys.option_map[keyCode]; break;
359 		case B_OPTION_KEY|B_SHIFT_KEY: offset = fKeys.option_shift_map[keyCode]; break;
360 		case B_OPTION_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_map[keyCode]; break;
361 		case B_OPTION_KEY|B_SHIFT_KEY|B_CAPS_LOCK: offset = fKeys.option_caps_shift_map[keyCode]; break;
362 		case B_CONTROL_KEY: offset = fKeys.control_map[keyCode]; break;
363 
364 		default:
365 			offset = fKeys.normal_map[keyCode];
366 			break;
367 	}
368 
369 	if (offset <= 0)
370 		return;
371 
372 	// here we get the char size
373 	*numBytes = fChars[offset];
374 	if (!*numBytes)
375 		return;
376 
377 	// here we take an potential active dead key
378 	int32 *deadKey;
379 	switch (activeDeadKey) {
380 		case 1: deadKey = fKeys.acute_dead_key; break;
381 		case 2: deadKey = fKeys.grave_dead_key; break;
382 		case 3: deadKey = fKeys.circumflex_dead_key; break;
383 		case 4: deadKey = fKeys.dieresis_dead_key; break;
384 		case 5: deadKey = fKeys.tilde_dead_key; break;
385 		default:
386 		{
387 			// if not dead, we copy and return the char
388 			char *str = *chars = new (std::nothrow) char[*numBytes + 1];
389 			if (str == NULL) {
390 				*numBytes = 0;
391 				return;
392 			}
393 			strncpy(str, &(fChars[offset + 1]), *numBytes);
394 			str[*numBytes] = 0;
395 			return;
396 		}
397 	}
398 
399 	// if dead key, we search for our current offset char in the dead key offset table
400 	// string comparison is needed
401 	for (int32 i = 0; i < 32; i++) {
402 		if (strncmp(&(fChars[offset + 1]), &(fChars[deadKey[i] + 1]), *numBytes) == 0) {
403 			*numBytes = fChars[deadKey[i + 1]];
404 
405 			switch (*numBytes) {
406 				case 0:
407 					// Not mapped
408 					*chars = NULL;
409 					break;
410 				default:
411 				{
412 					// 1-, 2-, 3-, or 4-byte UTF-8 character
413 					char *str = *chars = new (std::nothrow) char[*numBytes + 1];
414 					if (str == NULL) {
415 						*numBytes = 0;
416 						return;
417 					}
418 					strncpy(str, &fChars[deadKey[i + 1] + 1], *numBytes);
419 					str[*numBytes] = 0;
420 					break;
421 				}
422 			}
423 			return;
424 		}
425 		i++;
426 	}
427 
428 	// if not found we return the whole sequence (the dead key itself plus
429 	// the following char)
430 	int32 deadKeyNumBytes = fChars[deadKey[1]];
431 	int32 fullNumBytes = deadKeyNumBytes + *numBytes;
432 	*chars = new (std::nothrow) char[fullNumBytes + 1];
433 	if (*chars == NULL) {
434 		*numBytes = 0;
435 		return;
436 	}
437 	strncpy(*chars, &fChars[deadKey[1] + 1], deadKeyNumBytes);
438 	strncpy(*chars + deadKeyNumBytes, &fChars[offset + 1], *numBytes);
439 	*numBytes = fullNumBytes;
440 	(*chars)[fullNumBytes] = 0;
441 }
442 
443 
444 status_t
445 Keymap::LoadCurrent()
446 {
447 #ifndef CONSOLED
448 	key_map* keys = NULL;
449 	free(fChars);
450 	fChars = NULL;
451 
452 	_get_key_map(&keys, &fChars, &fCharsSize);
453 	if (!keys) {
454 		fprintf(stderr, "error while getting current keymap!\n");
455 		return B_ERROR;
456 	}
457 	memcpy(&fKeys, keys, sizeof(fKeys));
458 	free(keys);
459 	return B_OK;
460 #else	// CONSOLED
461 	char path[PATH_MAX];
462 	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, -1, false,
463 		path, sizeof(path));
464 	if (status != B_OK)
465 		return status;
466 
467 	strlcat(path, "/Key_map", sizeof(path));
468 	int file = open(path, O_RDONLY);
469 	if (file < 0)
470 		return errno;
471 
472 	if (read(file, &fKeys, sizeof(fKeys)) < (ssize_t)sizeof(fKeys)) {
473 		fprintf(stderr, "error reading keymap keys\n");
474 		return B_BAD_VALUE;
475 	}
476 
477 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++)
478 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
479 
480 	if (read(file, &fCharsSize, sizeof(uint32)) < (ssize_t)sizeof(uint32)) {
481 		fprintf(stderr, "error reading keymap size\n");
482 		return B_BAD_VALUE;
483 	}
484 
485 	fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
486 	if (fCharsSize < 0)
487 		return B_BAD_VALUE;
488 
489 	fChars = new char[fCharsSize];
490 
491 	if (read(file, fChars, fCharsSize) < 0) {
492 		fprintf(stderr, "error reading keymap chars\n");
493 		return B_ERROR;
494 	}
495 
496 	return B_OK;
497 #endif	// CONSOLED
498 }
499 
500