xref: /haiku/src/kits/shared/Keymap.cpp (revision 8a990d5228b2d1099e3062180532ba709dfeef6d)
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  *		Axel Dörfler, axeld@pinc-software.de.
8  */
9 
10 
11 #include <Keymap.h>
12 
13 #include <new>
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include <ByteOrder.h>
20 #include <File.h>
21 
22 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
23 #	include "SystemKeymap.h"
24 	// generated by the build system
25 #endif
26 
27 
28 // Private only at this point, as we might want to improve the dead key
29 // implementation in the future
30 enum dead_key_index {
31 	kDeadKeyAcute = 1,
32 	kDeadKeyGrave,
33 	kDeadKeyCircumflex,
34 	kDeadKeyDiaeresis,
35 	kDeadKeyTilde
36 };
37 
38 
39 static const uint32 kModifierKeys = B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
40 	| B_CAPS_LOCK | B_OPTION_KEY | B_MENU_KEY;
41 
42 
43 BKeymap::BKeymap()
44 	:
45 	fChars(NULL),
46 	fCharsSize(0)
47 {
48 	Unset();
49 }
50 
51 
52 BKeymap::~BKeymap()
53 {
54 	delete[] fChars;
55 }
56 
57 
58 /*!	Load a map from a file.
59 	File format in big endian:
60 		struct key_map
61 		uint32 size of following charset
62 		charset (offsets go into this with size of character followed by
63 		  character)
64 */
65 status_t
66 BKeymap::SetTo(const char* path)
67 {
68 	BFile file;
69 	status_t status = file.SetTo(path, B_READ_ONLY);
70 	if (status != B_OK)
71 		return status;
72 
73 	return SetTo(file);
74 }
75 
76 
77 status_t
78 BKeymap::SetTo(BDataIO& stream)
79 {
80 	if (stream.Read(&fKeys, sizeof(fKeys)) < 1)
81 		return B_IO_ERROR;
82 
83 	// convert from big-endian
84 	for (uint32 i = 0; i < sizeof(fKeys) / 4; i++) {
85 		((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]);
86 	}
87 
88 	if (fKeys.version != 3)
89 		return B_BAD_DATA;
90 
91 	if (stream.Read(&fCharsSize, sizeof(uint32)) < 1)
92 		return B_IO_ERROR;
93 
94 	fCharsSize = B_BENDIAN_TO_HOST_INT32(fCharsSize);
95 	if (fCharsSize > 16 * 1024) {
96 		Unset();
97 		return B_BAD_DATA;
98 	}
99 
100 	delete[] fChars;
101 	fChars = new char[fCharsSize];
102 
103 	if (stream.Read(fChars, fCharsSize) != (ssize_t)fCharsSize) {
104 		Unset();
105 		return B_IO_ERROR;
106 	}
107 
108 	return B_OK;
109 }
110 
111 
112 status_t
113 BKeymap::SetToCurrent()
114 {
115 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
116 	key_map* keys = NULL;
117 	get_key_map(&keys, &fChars);
118 	if (!keys)
119 		return B_ERROR;
120 
121 	memcpy(&fKeys, keys, sizeof(fKeys));
122 	free(keys);
123 	return B_OK;
124 #else	// ! __BEOS__
125 	fprintf(stderr, "Unsupported operation on this platform!\n");
126 	exit(1);
127 #endif	// ! __BEOS__
128 }
129 
130 
131 status_t
132 BKeymap::SetToDefault()
133 {
134 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
135 	fKeys = kSystemKeymap;
136 	fCharsSize = kSystemKeyCharsSize;
137 
138 	fChars = new (std::nothrow) char[fCharsSize];
139 	if (fChars == NULL) {
140 		Unset();
141 		return B_NO_MEMORY;
142 	}
143 
144 	memcpy(fChars, kSystemKeyChars, fCharsSize);
145 	return B_OK;
146 #else	// ! __BEOS__
147 	fprintf(stderr, "Unsupported operation on this platform!\n");
148 	exit(1);
149 #endif	// ! __BEOS__
150 }
151 
152 
153 void
154 BKeymap::Unset()
155 {
156 	delete[] fChars;
157 	fChars = NULL;
158 	fCharsSize = 0;
159 
160 	memset(&fKeys, 0, sizeof(fKeys));
161 }
162 
163 
164 /*!	We need to know if a key is a modifier key to choose
165 	a valid key when several are pressed together
166 */
167 bool
168 BKeymap::IsModifierKey(uint32 keyCode) const
169 {
170 	return keyCode == fKeys.caps_key
171 		|| keyCode == fKeys.num_key
172 		|| keyCode == fKeys.scroll_key
173 		|| keyCode == fKeys.left_shift_key
174 		|| keyCode == fKeys.right_shift_key
175 		|| keyCode == fKeys.left_command_key
176 		|| keyCode == fKeys.right_command_key
177 		|| keyCode == fKeys.left_control_key
178 		|| keyCode == fKeys.right_control_key
179 		|| keyCode == fKeys.left_option_key
180 		|| keyCode == fKeys.right_option_key
181 		|| keyCode == fKeys.menu_key;
182 }
183 
184 
185 //! We need to know a modifier for a key
186 uint32
187 BKeymap::Modifier(uint32 keyCode) const
188 {
189 	if (keyCode == fKeys.caps_key)
190 		return B_CAPS_LOCK;
191 	if (keyCode == fKeys.num_key)
192 		return B_NUM_LOCK;
193 	if (keyCode == fKeys.scroll_key)
194 		return B_SCROLL_LOCK;
195 	if (keyCode == fKeys.left_shift_key)
196 		return B_LEFT_SHIFT_KEY | B_SHIFT_KEY;
197 	if (keyCode == fKeys.right_shift_key)
198 		return B_RIGHT_SHIFT_KEY | B_SHIFT_KEY;
199 	if (keyCode == fKeys.left_command_key)
200 		return B_LEFT_COMMAND_KEY | B_COMMAND_KEY;
201 	if (keyCode == fKeys.right_command_key)
202 		return B_RIGHT_COMMAND_KEY | B_COMMAND_KEY;
203 	if (keyCode == fKeys.left_control_key)
204 		return B_LEFT_CONTROL_KEY | B_CONTROL_KEY;
205 	if (keyCode == fKeys.right_control_key)
206 		return B_RIGHT_CONTROL_KEY | B_CONTROL_KEY;
207 	if (keyCode == fKeys.left_option_key)
208 		return B_LEFT_OPTION_KEY | B_OPTION_KEY;
209 	if (keyCode == fKeys.right_option_key)
210 		return B_RIGHT_OPTION_KEY | B_OPTION_KEY;
211 	if (keyCode == fKeys.menu_key)
212 		return B_MENU_KEY;
213 
214 	return 0;
215 }
216 
217 
218 uint32
219 BKeymap::KeyForModifier(uint32 modifier) const
220 {
221 	if (modifier == B_CAPS_LOCK)
222 		return fKeys.caps_key;
223 	if (modifier == B_NUM_LOCK)
224 		return fKeys.num_key;
225 	if (modifier == B_SCROLL_LOCK)
226 		return fKeys.scroll_key;
227 	if (modifier == B_LEFT_SHIFT_KEY || modifier == B_SHIFT_KEY)
228 		return fKeys.left_shift_key;
229 	if (modifier == B_RIGHT_SHIFT_KEY)
230 		return fKeys.right_shift_key;
231 	if (modifier == B_LEFT_COMMAND_KEY || modifier == B_COMMAND_KEY)
232 		return fKeys.left_command_key;
233 	if (modifier == B_RIGHT_COMMAND_KEY)
234 		return fKeys.right_command_key;
235 	if (modifier == B_LEFT_CONTROL_KEY || modifier == B_CONTROL_KEY)
236 		return fKeys.left_control_key;
237 	if (modifier == B_RIGHT_CONTROL_KEY)
238 		return fKeys.right_control_key;
239 	if (modifier == B_LEFT_OPTION_KEY || modifier == B_OPTION_KEY)
240 		return fKeys.left_option_key;
241 	if (modifier == B_RIGHT_OPTION_KEY)
242 		return fKeys.right_option_key;
243 	if (modifier == B_MENU_KEY)
244 		return fKeys.menu_key;
245 
246 	return 0;
247 }
248 
249 
250 /*! Checks whether a key is an active dead key.
251 */
252 uint8
253 BKeymap::ActiveDeadKey(uint32 keyCode, uint32 modifiers) const
254 {
255 	bool enabled;
256 	uint8 deadKey = DeadKey(keyCode, modifiers, &enabled);
257 	if (deadKey == 0 || !enabled)
258 		return 0;
259 
260 	return deadKey;
261 }
262 
263 
264 /*! Checks whether a key is a dead key.
265 	If it is, the enabled/disabled state of that dead key will be passed
266 	out via isEnabled (isEnabled is not touched for non-dead keys).
267 */
268 uint8
269 BKeymap::DeadKey(uint32 keyCode, uint32 modifiers, bool* _isEnabled) const
270 {
271 	uint32 tableMask = 0;
272 	int32 offset = Offset(keyCode, modifiers, &tableMask);
273 	uint8 deadKeyIndex = DeadKeyIndex(offset);
274 	if (deadKeyIndex > 0 && _isEnabled != NULL) {
275 		uint32 deadTables[] = {
276 			fKeys.acute_tables,
277 			fKeys.grave_tables,
278 			fKeys.circumflex_tables,
279 			fKeys.dieresis_tables,
280 			fKeys.tilde_tables
281 		};
282 		*_isEnabled = (deadTables[deadKeyIndex - 1] & tableMask) != 0;
283 	}
284 
285 	return deadKeyIndex;
286 }
287 
288 
289 //! Tell if a key is a dead second key.
290 bool
291 BKeymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers,
292 	uint8 activeDeadKey) const
293 {
294 	if (!activeDeadKey)
295 		return false;
296 
297 	int32 offset = Offset(keyCode, modifiers);
298 	if (offset < 0)
299 		return false;
300 
301 	uint32 numBytes = fChars[offset];
302 	if (!numBytes)
303 		return false;
304 
305 	const int32* deadOffsets[] = {
306 		fKeys.acute_dead_key,
307 		fKeys.grave_dead_key,
308 		fKeys.circumflex_dead_key,
309 		fKeys.dieresis_dead_key,
310 		fKeys.tilde_dead_key
311 	};
312 
313 	const int32* deadOffset = deadOffsets[activeDeadKey - 1];
314 
315 	for (int32 i = 0; i < 32; i++) {
316 		if (offset == deadOffset[i])
317 			return true;
318 
319 		uint32 deadNumBytes = fChars[deadOffset[i]];
320 
321 		if (!deadNumBytes)
322 			continue;
323 
324 		if (strncmp(&fChars[offset + 1], &fChars[deadOffset[i] + 1],
325 				deadNumBytes) == 0)
326 			return true;
327 		i++;
328 	}
329 	return false;
330 }
331 
332 
333 //! Get the char for a key given modifiers and active dead key
334 void
335 BKeymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey,
336 	char** chars, int32* numBytes) const
337 {
338 	*numBytes = 0;
339 	*chars = NULL;
340 
341 	if (keyCode > 128 || fChars == NULL)
342 		return;
343 
344 	// here we take NUMLOCK into account
345 	if ((modifiers & B_NUM_LOCK) != 0) {
346 		switch (keyCode) {
347 			case 0x37:
348 			case 0x38:
349 			case 0x39:
350 			case 0x48:
351 			case 0x49:
352 			case 0x4a:
353 			case 0x58:
354 			case 0x59:
355 			case 0x5a:
356 			case 0x64:
357 			case 0x65:
358 				modifiers ^= B_SHIFT_KEY;
359 		}
360 	}
361 
362 	int32 offset = Offset(keyCode, modifiers);
363 	if (offset < 0)
364 		return;
365 
366 	// here we get the char size
367 	*numBytes = fChars[offset];
368 	if (!*numBytes)
369 		return;
370 
371 	// here we take an potential active dead key
372 	const int32* deadKey;
373 	switch (activeDeadKey) {
374 		case kDeadKeyAcute:
375 			deadKey = fKeys.acute_dead_key;
376 			break;
377 		case kDeadKeyGrave:
378 			deadKey = fKeys.grave_dead_key;
379 			break;
380 		case kDeadKeyCircumflex:
381 			deadKey = fKeys.circumflex_dead_key;
382 			break;
383 		case kDeadKeyDiaeresis:
384 			deadKey = fKeys.dieresis_dead_key;
385 			break;
386 		case kDeadKeyTilde:
387 			deadKey = fKeys.tilde_dead_key;
388 			break;
389 		default:
390 		{
391 			// if not dead, we copy and return the char
392 			char* str = *chars = new char[*numBytes + 1];
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
400 	// offset table string comparison is needed
401 	for (int32 i = 0; i < 32; i++) {
402 		if (strncmp(&fChars[offset + 1], &fChars[deadKey[i] + 1], *numBytes)
403 				== 0) {
404 			*numBytes = fChars[deadKey[i + 1]];
405 
406 			switch (*numBytes) {
407 				case 0:
408 					// Not mapped
409 					*chars = NULL;
410 					break;
411 				default:
412 				{
413 					// 1-, 2-, 3-, or 4-byte UTF-8 character
414 					char *str = *chars = new char[*numBytes + 1];
415 					strncpy(str, &fChars[deadKey[i + 1] + 1], *numBytes);
416 					str[*numBytes] = 0;
417 					break;
418 				}
419 			}
420 			return;
421 		}
422 		i++;
423 	}
424 
425 	// if not found we return the current char mapped
426 	*chars = new char[*numBytes + 1];
427 	strncpy(*chars, &fChars[offset + 1], *numBytes);
428 	(*chars)[*numBytes] = 0;
429 }
430 
431 
432 bool
433 BKeymap::operator==(const BKeymap& other) const
434 {
435 	return fCharsSize == other.fCharsSize
436 		&& !memcmp(&fKeys, &other.fKeys, sizeof(fKeys))
437 		&& !memcmp(fChars, other.fChars, fCharsSize);
438 }
439 
440 
441 bool
442 BKeymap::operator!=(const BKeymap& other) const
443 {
444 	return !(*this == other);
445 }
446 
447 
448 BKeymap&
449 BKeymap::operator=(const BKeymap& other)
450 {
451 	Unset();
452 
453 	fChars = new char[fCharsSize];
454 	fCharsSize = other.fCharsSize;
455 	memcpy(fChars, other.fChars, fCharsSize);
456 	memcpy(&fKeys, &other.fKeys, sizeof(fKeys));
457 
458 	return *this;
459 }
460 
461 
462 int32
463 BKeymap::Offset(uint32 keyCode, uint32 modifiers, uint32* _table) const
464 {
465 	int32 offset;
466 	uint32 table;
467 
468 	if (keyCode >= 128)
469 		return -1;
470 
471 	switch (modifiers & kModifierKeys) {
472 		case B_SHIFT_KEY:
473 			offset = fKeys.shift_map[keyCode];
474 			table = B_SHIFT_TABLE;
475 			break;
476 		case B_CAPS_LOCK:
477 			offset = fKeys.caps_map[keyCode];
478 			table = B_CAPS_TABLE;
479 			break;
480 		case B_CAPS_LOCK | B_SHIFT_KEY:
481 			offset = fKeys.caps_shift_map[keyCode];
482 			table = B_CAPS_SHIFT_TABLE;
483 			break;
484 		case B_OPTION_KEY:
485 			offset = fKeys.option_map[keyCode];
486 			table = B_OPTION_TABLE;
487 			break;
488 		case B_OPTION_KEY | B_SHIFT_KEY:
489 			offset = fKeys.option_shift_map[keyCode];
490 			table = B_OPTION_SHIFT_TABLE;
491 			break;
492 		case B_OPTION_KEY | B_CAPS_LOCK:
493 			offset = fKeys.option_caps_map[keyCode];
494 			table = B_OPTION_CAPS_TABLE;
495 			break;
496 		case B_OPTION_KEY | B_SHIFT_KEY | B_CAPS_LOCK:
497 			offset = fKeys.option_caps_shift_map[keyCode];
498 			table = B_OPTION_CAPS_SHIFT_TABLE;
499 			break;
500 		case B_CONTROL_KEY:
501 			offset = fKeys.control_map[keyCode];
502 			table = B_CONTROL_TABLE;
503 			break;
504 		default:
505 			offset = fKeys.normal_map[keyCode];
506 			table = B_NORMAL_TABLE;
507 			break;
508 	}
509 
510 	if (_table != NULL)
511 		*_table = table;
512 
513 	if (offset >= (int32)fCharsSize)
514 		return -1;
515 
516 	return offset;
517 }
518 
519 
520 uint8
521 BKeymap::DeadKeyIndex(int32 offset) const
522 {
523 	if (fChars == NULL || offset <= 0)
524 		return 0;
525 
526 	uint32 numBytes = fChars[offset];
527 	if (!numBytes || numBytes > 4)
528 		return 0;
529 
530 	char chars[5];
531 	strncpy(chars, &fChars[offset + 1], numBytes);
532 	chars[numBytes] = 0;
533 
534 	const int32 deadOffsets[] = {
535 		fKeys.acute_dead_key[1],
536 		fKeys.grave_dead_key[1],
537 		fKeys.circumflex_dead_key[1],
538 		fKeys.dieresis_dead_key[1],
539 		fKeys.tilde_dead_key[1]
540 	};
541 
542 	uint8 result = 0;
543 	for (int32 i = 0; i < 5; i++) {
544 		if (offset == deadOffsets[i])
545 			return i + 1;
546 
547 		uint32 deadNumBytes = fChars[deadOffsets[i]];
548 		if (!deadNumBytes)
549 			continue;
550 
551 		if (strncmp(chars, &fChars[deadOffsets[i] + 1], deadNumBytes) == 0)
552 			return i + 1;
553 	}
554 
555 	return result;
556 }
557