xref: /haiku/src/kits/shared/Keymap.cpp (revision f7db27344f77c8de8b1a79fb2ee697d65f591b0b)
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 
BKeymap()47 BKeymap::BKeymap()
48 	:
49 	fChars(NULL),
50 	fCharsSize(0)
51 {
52 	Unset();
53 }
54 
55 
~BKeymap()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
SetTo(const char * path)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
SetTo(BDataIO & stream)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
SetToCurrent()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
SetToDefault()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
Unset()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
IsModifierKey(uint32 keyCode) const179 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
Modifier(uint32 keyCode) const198 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
KeyForModifier(uint32 modifier) const230 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
ActiveDeadKey(uint32 keyCode,uint32 modifiers) const264 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
DeadKey(uint32 keyCode,uint32 modifiers,bool * _isEnabled) const280 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
IsDeadSecondKey(uint32 keyCode,uint32 modifiers,uint8 activeDeadKey) const302 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
GetChars(uint32 keyCode,uint32 modifiers,uint8 activeDeadKey,char ** chars,int32 * numBytes) const346 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 += 2) {
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 	}
445 
446 	// if not found we return the current char mapped
447 	*chars = new char[*numBytes + 1];
448 	strncpy(*chars, &fChars[offset + 1], *numBytes);
449 	(*chars)[*numBytes] = 0;
450 }
451 
452 
453 /*!	Get a list of characters translated from a given character and
454 	set of modifiers to another set of modifiers.
455 */
456 status_t
GetModifiedCharacters(const char * in,int32 inModifiers,int32 outModifiers,BObjectList<const char> * _outList)457 BKeymap::GetModifiedCharacters(const char* in, int32 inModifiers,
458 	int32 outModifiers, BObjectList<const char>* _outList)
459 {
460 	if (in == NULL || *in == '\0' || _outList == NULL)
461 		return B_BAD_VALUE;
462 
463 	for(uint32 i = 0; i < 128; i++) {
464 		int32 inOffset = Offset(i, inModifiers);
465 		size_t sizeIn = fChars[inOffset++];
466 		if (sizeIn == 0 || memcmp(in, fChars + inOffset, sizeIn) != 0) {
467 			// this character isn't mapped or doesn't match
468 			continue;
469 		}
470 
471 		int32 outOffset = Offset(i, outModifiers);
472 		size_t sizeOut = fChars[outOffset++];
473 		char* out = (char*)malloc(sizeOut + 1);
474 		if (out == NULL)
475 			return B_NO_MEMORY;
476 
477 		memcpy(out, fChars + outOffset, sizeOut);
478 		out[sizeOut] = '\0';
479 
480 		_outList->AddItem((const char*)out);
481 	}
482 
483 	return B_OK;
484 }
485 
486 
487 bool
operator ==(const BKeymap & other) const488 BKeymap::operator==(const BKeymap& other) const
489 {
490 	return fCharsSize == other.fCharsSize
491 		&& !memcmp(&fKeys, &other.fKeys, sizeof(fKeys))
492 		&& !memcmp(fChars, other.fChars, fCharsSize);
493 }
494 
495 
496 bool
operator !=(const BKeymap & other) const497 BKeymap::operator!=(const BKeymap& other) const
498 {
499 	return !(*this == other);
500 }
501 
502 
503 BKeymap&
operator =(const BKeymap & other)504 BKeymap::operator=(const BKeymap& other)
505 {
506 	Unset();
507 
508 	fCharsSize = other.fCharsSize;
509 	fChars = new char[fCharsSize];
510 	memcpy(fChars, other.fChars, fCharsSize);
511 	memcpy(&fKeys, &other.fKeys, sizeof(fKeys));
512 
513 	return *this;
514 }
515 
516 
517 int32
Offset(uint32 keyCode,uint32 modifiers,uint32 * _table) const518 BKeymap::Offset(uint32 keyCode, uint32 modifiers, uint32* _table) const
519 {
520 	int32 offset;
521 	uint32 table;
522 
523 	if (keyCode >= 128)
524 		return -1;
525 
526 	switch (modifiers & kModifierKeys) {
527 		case B_SHIFT_KEY:
528 			offset = fKeys.shift_map[keyCode];
529 			table = B_SHIFT_TABLE;
530 			break;
531 		case B_CAPS_LOCK:
532 			offset = fKeys.caps_map[keyCode];
533 			table = B_CAPS_TABLE;
534 			break;
535 		case B_CAPS_LOCK | B_SHIFT_KEY:
536 			offset = fKeys.caps_shift_map[keyCode];
537 			table = B_CAPS_SHIFT_TABLE;
538 			break;
539 		case B_CONTROL_KEY:
540 			offset = fKeys.control_map[keyCode];
541 			table = B_CONTROL_TABLE;
542 			break;
543 		case B_OPTION_KEY:
544 			offset = fKeys.option_map[keyCode];
545 			table = B_OPTION_TABLE;
546 			break;
547 		case B_OPTION_KEY | B_SHIFT_KEY:
548 			offset = fKeys.option_shift_map[keyCode];
549 			table = B_OPTION_SHIFT_TABLE;
550 			break;
551 		case B_OPTION_KEY | B_CAPS_LOCK:
552 			offset = fKeys.option_caps_map[keyCode];
553 			table = B_OPTION_CAPS_TABLE;
554 			break;
555 		case B_OPTION_KEY | B_SHIFT_KEY | B_CAPS_LOCK:
556 			offset = fKeys.option_caps_shift_map[keyCode];
557 			table = B_OPTION_CAPS_SHIFT_TABLE;
558 			break;
559 		default:
560 			offset = fKeys.normal_map[keyCode];
561 			table = B_NORMAL_TABLE;
562 			break;
563 	}
564 
565 	if (_table != NULL)
566 		*_table = table;
567 
568 	if (offset >= (int32)fCharsSize)
569 		return -1;
570 
571 	return offset;
572 }
573 
574 
575 uint8
DeadKeyIndex(int32 offset) const576 BKeymap::DeadKeyIndex(int32 offset) const
577 {
578 	if (fChars == NULL || offset <= 0)
579 		return 0;
580 
581 	uint32 numBytes = fChars[offset];
582 	if (!numBytes || numBytes > 4)
583 		return 0;
584 
585 	char chars[5];
586 	strncpy(chars, &fChars[offset + 1], numBytes);
587 	chars[numBytes] = 0;
588 
589 	const int32 deadOffsets[] = {
590 		fKeys.acute_dead_key[1],
591 		fKeys.grave_dead_key[1],
592 		fKeys.circumflex_dead_key[1],
593 		fKeys.dieresis_dead_key[1],
594 		fKeys.tilde_dead_key[1]
595 	};
596 
597 	uint8 result = 0;
598 	for (int32 i = 0; i < 5; i++) {
599 		if (offset == deadOffsets[i])
600 			return i + 1;
601 
602 		uint32 deadNumBytes = fChars[deadOffsets[i]];
603 		if (!deadNumBytes)
604 			continue;
605 
606 		if (strncmp(chars, &fChars[deadOffsets[i] + 1], deadNumBytes) == 0)
607 			return i + 1;
608 	}
609 
610 	return result;
611 }
612