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