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
31enum dead_key_index {
32	kDeadKeyAcute = 1,
33	kDeadKeyGrave,
34	kDeadKeyCircumflex,
35	kDeadKeyDiaeresis,
36	kDeadKeyTilde
37};
38
39
40static const uint32 kModifierKeys = B_SHIFT_KEY | B_CAPS_LOCK | B_CONTROL_KEY
41	| B_OPTION_KEY | B_COMMAND_KEY | B_MENU_KEY;
42
43
44BKeymap::BKeymap()
45	:
46	fChars(NULL),
47	fCharsSize(0)
48{
49	Unset();
50}
51
52
53BKeymap::~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*/
66status_t
67BKeymap::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
78status_t
79BKeymap::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
113status_t
114BKeymap::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
132status_t
133BKeymap::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
154void
155BKeymap::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*/
168bool
169BKeymap::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
187uint32
188BKeymap::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
219uint32
220BKeymap::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*/
253uint8
254BKeymap::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*/
269uint8
270BKeymap::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.
291bool
292BKeymap::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
335void
336BKeymap::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
444bool
445BKeymap::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
453bool
454BKeymap::operator!=(const BKeymap& other) const
455{
456	return !(*this == other);
457}
458
459
460BKeymap&
461BKeymap::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
474int32
475BKeymap::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
532uint8
533BKeymap::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