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
34enum dead_key_index {
35	kDeadKeyAcute = 1,
36	kDeadKeyGrave,
37	kDeadKeyCircumflex,
38	kDeadKeyDiaeresis,
39	kDeadKeyTilde
40};
41
42
43static const uint32 kModifierKeys = B_SHIFT_KEY | B_CAPS_LOCK | B_CONTROL_KEY
44	| B_OPTION_KEY | B_COMMAND_KEY | B_MENU_KEY;
45
46
47BKeymap::BKeymap()
48	:
49	fChars(NULL),
50	fCharsSize(0)
51{
52	Unset();
53}
54
55
56BKeymap::~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*/
69status_t
70BKeymap::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
81status_t
82BKeymap::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
116status_t
117BKeymap::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
141status_t
142BKeymap::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
164void
165BKeymap::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*/
178bool
179BKeymap::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
197uint32
198BKeymap::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
229uint32
230BKeymap::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*/
263uint8
264BKeymap::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*/
279uint8
280BKeymap::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.
301bool
302BKeymap::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
345void
346BKeymap::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*/
456status_t
457BKeymap::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
487bool
488BKeymap::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
496bool
497BKeymap::operator!=(const BKeymap& other) const
498{
499	return !(*this == other);
500}
501
502
503BKeymap&
504BKeymap::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
517int32
518BKeymap::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
575uint8
576BKeymap::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