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