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