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