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