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