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 47 BKeymap::BKeymap() 48 : 49 fChars(NULL), 50 fCharsSize(0) 51 { 52 Unset(); 53 } 54 55 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 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 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 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 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 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 179 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 198 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 230 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 264 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 280 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 302 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 346 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 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 488 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 497 BKeymap::operator!=(const BKeymap& other) const 498 { 499 return !(*this == other); 500 } 501 502 503 BKeymap& 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 518 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 576 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