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 126 fCharsSize = sizeof(fChars); 127 128 return B_OK; 129 #else // ! __BEOS__ 130 fprintf(stderr, "Unsupported operation on this platform!\n"); 131 exit(1); 132 #endif // ! __BEOS__ 133 } 134 135 136 status_t 137 BKeymap::SetToDefault() 138 { 139 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 140 fKeys = kSystemKeymap; 141 fCharsSize = kSystemKeyCharsSize; 142 143 fChars = new (std::nothrow) char[fCharsSize]; 144 if (fChars == NULL) { 145 Unset(); 146 return B_NO_MEMORY; 147 } 148 149 memcpy(fChars, kSystemKeyChars, fCharsSize); 150 return B_OK; 151 #else // ! __BEOS__ 152 fprintf(stderr, "Unsupported operation on this platform!\n"); 153 exit(1); 154 #endif // ! __BEOS__ 155 } 156 157 158 void 159 BKeymap::Unset() 160 { 161 delete[] fChars; 162 fChars = NULL; 163 fCharsSize = 0; 164 165 memset(&fKeys, 0, sizeof(fKeys)); 166 } 167 168 169 /*! We need to know if a key is a modifier key to choose 170 a valid key when several are pressed together 171 */ 172 bool 173 BKeymap::IsModifierKey(uint32 keyCode) const 174 { 175 return keyCode == fKeys.caps_key 176 || keyCode == fKeys.num_key 177 || keyCode == fKeys.scroll_key 178 || keyCode == fKeys.left_shift_key 179 || keyCode == fKeys.right_shift_key 180 || keyCode == fKeys.left_command_key 181 || keyCode == fKeys.right_command_key 182 || keyCode == fKeys.left_control_key 183 || keyCode == fKeys.right_control_key 184 || keyCode == fKeys.left_option_key 185 || keyCode == fKeys.right_option_key 186 || keyCode == fKeys.menu_key; 187 } 188 189 190 //! We need to know a modifier for a key 191 uint32 192 BKeymap::Modifier(uint32 keyCode) const 193 { 194 if (keyCode == fKeys.caps_key) 195 return B_CAPS_LOCK; 196 if (keyCode == fKeys.num_key) 197 return B_NUM_LOCK; 198 if (keyCode == fKeys.scroll_key) 199 return B_SCROLL_LOCK; 200 if (keyCode == fKeys.left_shift_key) 201 return B_LEFT_SHIFT_KEY | B_SHIFT_KEY; 202 if (keyCode == fKeys.right_shift_key) 203 return B_RIGHT_SHIFT_KEY | B_SHIFT_KEY; 204 if (keyCode == fKeys.left_command_key) 205 return B_LEFT_COMMAND_KEY | B_COMMAND_KEY; 206 if (keyCode == fKeys.right_command_key) 207 return B_RIGHT_COMMAND_KEY | B_COMMAND_KEY; 208 if (keyCode == fKeys.left_control_key) 209 return B_LEFT_CONTROL_KEY | B_CONTROL_KEY; 210 if (keyCode == fKeys.right_control_key) 211 return B_RIGHT_CONTROL_KEY | B_CONTROL_KEY; 212 if (keyCode == fKeys.left_option_key) 213 return B_LEFT_OPTION_KEY | B_OPTION_KEY; 214 if (keyCode == fKeys.right_option_key) 215 return B_RIGHT_OPTION_KEY | B_OPTION_KEY; 216 if (keyCode == fKeys.menu_key) 217 return B_MENU_KEY; 218 219 return 0; 220 } 221 222 223 uint32 224 BKeymap::KeyForModifier(uint32 modifier) const 225 { 226 if (modifier == B_CAPS_LOCK) 227 return fKeys.caps_key; 228 if (modifier == B_NUM_LOCK) 229 return fKeys.num_key; 230 if (modifier == B_SCROLL_LOCK) 231 return fKeys.scroll_key; 232 if (modifier == B_LEFT_SHIFT_KEY || modifier == B_SHIFT_KEY) 233 return fKeys.left_shift_key; 234 if (modifier == B_RIGHT_SHIFT_KEY) 235 return fKeys.right_shift_key; 236 if (modifier == B_LEFT_COMMAND_KEY || modifier == B_COMMAND_KEY) 237 return fKeys.left_command_key; 238 if (modifier == B_RIGHT_COMMAND_KEY) 239 return fKeys.right_command_key; 240 if (modifier == B_LEFT_CONTROL_KEY || modifier == B_CONTROL_KEY) 241 return fKeys.left_control_key; 242 if (modifier == B_RIGHT_CONTROL_KEY) 243 return fKeys.right_control_key; 244 if (modifier == B_LEFT_OPTION_KEY || modifier == B_OPTION_KEY) 245 return fKeys.left_option_key; 246 if (modifier == B_RIGHT_OPTION_KEY) 247 return fKeys.right_option_key; 248 if (modifier == B_MENU_KEY) 249 return fKeys.menu_key; 250 251 return 0; 252 } 253 254 255 /*! Checks whether a key is an active dead key. 256 */ 257 uint8 258 BKeymap::ActiveDeadKey(uint32 keyCode, uint32 modifiers) const 259 { 260 bool enabled; 261 uint8 deadKey = DeadKey(keyCode, modifiers, &enabled); 262 if (deadKey == 0 || !enabled) 263 return 0; 264 265 return deadKey; 266 } 267 268 269 /*! Checks whether a key is a dead key. 270 If it is, the enabled/disabled state of that dead key will be passed 271 out via isEnabled (isEnabled is not touched for non-dead keys). 272 */ 273 uint8 274 BKeymap::DeadKey(uint32 keyCode, uint32 modifiers, bool* _isEnabled) const 275 { 276 uint32 tableMask = 0; 277 int32 offset = Offset(keyCode, modifiers, &tableMask); 278 uint8 deadKeyIndex = DeadKeyIndex(offset); 279 if (deadKeyIndex > 0 && _isEnabled != NULL) { 280 uint32 deadTables[] = { 281 fKeys.acute_tables, 282 fKeys.grave_tables, 283 fKeys.circumflex_tables, 284 fKeys.dieresis_tables, 285 fKeys.tilde_tables 286 }; 287 *_isEnabled = (deadTables[deadKeyIndex - 1] & tableMask) != 0; 288 } 289 290 return deadKeyIndex; 291 } 292 293 294 //! Tell if a key is a dead second key. 295 bool 296 BKeymap::IsDeadSecondKey(uint32 keyCode, uint32 modifiers, 297 uint8 activeDeadKey) const 298 { 299 if (!activeDeadKey) 300 return false; 301 302 int32 offset = Offset(keyCode, modifiers); 303 if (offset < 0) 304 return false; 305 306 uint32 numBytes = fChars[offset]; 307 if (!numBytes) 308 return false; 309 310 const int32* deadOffsets[] = { 311 fKeys.acute_dead_key, 312 fKeys.grave_dead_key, 313 fKeys.circumflex_dead_key, 314 fKeys.dieresis_dead_key, 315 fKeys.tilde_dead_key 316 }; 317 318 const int32* deadOffset = deadOffsets[activeDeadKey - 1]; 319 320 for (int32 i = 0; i < 32; i++) { 321 if (offset == deadOffset[i]) 322 return true; 323 324 uint32 deadNumBytes = fChars[deadOffset[i]]; 325 326 if (!deadNumBytes) 327 continue; 328 329 if (strncmp(&fChars[offset + 1], &fChars[deadOffset[i] + 1], 330 deadNumBytes) == 0) 331 return true; 332 i++; 333 } 334 return false; 335 } 336 337 338 //! Get the char for a key given modifiers and active dead key 339 void 340 BKeymap::GetChars(uint32 keyCode, uint32 modifiers, uint8 activeDeadKey, 341 char** chars, int32* numBytes) const 342 { 343 *numBytes = 0; 344 *chars = NULL; 345 346 if (keyCode > 128 || fChars == NULL) 347 return; 348 349 // here we take NUMLOCK into account 350 if ((modifiers & B_NUM_LOCK) != 0) { 351 switch (keyCode) { 352 case 0x37: 353 case 0x38: 354 case 0x39: 355 case 0x48: 356 case 0x49: 357 case 0x4a: 358 case 0x58: 359 case 0x59: 360 case 0x5a: 361 case 0x64: 362 case 0x65: 363 modifiers ^= B_SHIFT_KEY; 364 } 365 } 366 367 int32 offset = Offset(keyCode, modifiers); 368 if (offset < 0) 369 return; 370 371 // here we get the char size 372 *numBytes = fChars[offset]; 373 if (*numBytes <= 0) { 374 // if key is not mapped in the option table, fall-through. 375 if ((modifiers & B_OPTION_KEY) != 0) { 376 offset = Offset(keyCode, modifiers & ~B_OPTION_KEY); 377 if (offset < 0) 378 return; 379 // get the char size again 380 *numBytes = fChars[offset]; 381 if (*numBytes <= 0) 382 return; 383 } else 384 return; 385 } 386 387 // here we take an potential active dead key 388 const int32* deadKey; 389 switch (activeDeadKey) { 390 case kDeadKeyAcute: 391 deadKey = fKeys.acute_dead_key; 392 break; 393 case kDeadKeyGrave: 394 deadKey = fKeys.grave_dead_key; 395 break; 396 case kDeadKeyCircumflex: 397 deadKey = fKeys.circumflex_dead_key; 398 break; 399 case kDeadKeyDiaeresis: 400 deadKey = fKeys.dieresis_dead_key; 401 break; 402 case kDeadKeyTilde: 403 deadKey = fKeys.tilde_dead_key; 404 break; 405 default: 406 { 407 // if not dead, we copy and return the char 408 char* str = *chars = new char[*numBytes + 1]; 409 strncpy(str, &fChars[offset + 1], *numBytes); 410 str[*numBytes] = 0; 411 return; 412 } 413 } 414 415 // if dead key, we search for our current offset char in the dead key 416 // offset table string comparison is needed 417 for (int32 i = 0; i < 32; i++) { 418 if (strncmp(&fChars[offset + 1], &fChars[deadKey[i] + 1], *numBytes) 419 == 0) { 420 *numBytes = fChars[deadKey[i + 1]]; 421 422 switch (*numBytes) { 423 case 0: 424 // Not mapped 425 *chars = NULL; 426 break; 427 default: 428 { 429 // 1-, 2-, 3-, or 4-byte UTF-8 character 430 char *str = *chars = new char[*numBytes + 1]; 431 strncpy(str, &fChars[deadKey[i + 1] + 1], *numBytes); 432 str[*numBytes] = 0; 433 break; 434 } 435 } 436 return; 437 } 438 i++; 439 } 440 441 // if not found we return the current char mapped 442 *chars = new char[*numBytes + 1]; 443 strncpy(*chars, &fChars[offset + 1], *numBytes); 444 (*chars)[*numBytes] = 0; 445 } 446 447 448 /*! Get a list of characters translated from a given character and 449 set of modifiers to another set of modifiers. 450 */ 451 status_t 452 BKeymap::GetModifiedCharacters(const char* in, int32 inModifiers, 453 int32 outModifiers, BObjectList<const char>* _outList) 454 { 455 if (in == NULL || *in == '\0' || _outList == NULL) 456 return B_BAD_VALUE; 457 458 for(uint32 i = 0; i < 128; i++) { 459 int32 inOffset = Offset(i, inModifiers); 460 size_t sizeIn = fChars[inOffset++]; 461 if (sizeIn == 0 || memcmp(in, fChars + inOffset, sizeIn) != 0) { 462 // this character isn't mapped or doesn't match 463 continue; 464 } 465 466 int32 outOffset = Offset(i, outModifiers); 467 size_t sizeOut = fChars[outOffset++]; 468 char* out = (char*)malloc(sizeOut + 1); 469 if (out == NULL) 470 return B_NO_MEMORY; 471 472 memcpy(out, fChars + outOffset, sizeOut); 473 out[sizeOut] = '\0'; 474 475 _outList->AddItem((const char*)out); 476 } 477 478 return B_OK; 479 } 480 481 482 bool 483 BKeymap::operator==(const BKeymap& other) const 484 { 485 return fCharsSize == other.fCharsSize 486 && !memcmp(&fKeys, &other.fKeys, sizeof(fKeys)) 487 && !memcmp(fChars, other.fChars, fCharsSize); 488 } 489 490 491 bool 492 BKeymap::operator!=(const BKeymap& other) const 493 { 494 return !(*this == other); 495 } 496 497 498 BKeymap& 499 BKeymap::operator=(const BKeymap& other) 500 { 501 Unset(); 502 503 fChars = new char[fCharsSize]; 504 fCharsSize = other.fCharsSize; 505 memcpy(fChars, other.fChars, fCharsSize); 506 memcpy(&fKeys, &other.fKeys, sizeof(fKeys)); 507 508 return *this; 509 } 510 511 512 int32 513 BKeymap::Offset(uint32 keyCode, uint32 modifiers, uint32* _table) const 514 { 515 int32 offset; 516 uint32 table; 517 518 if (keyCode >= 128) 519 return -1; 520 521 switch (modifiers & kModifierKeys) { 522 case B_SHIFT_KEY: 523 offset = fKeys.shift_map[keyCode]; 524 table = B_SHIFT_TABLE; 525 break; 526 case B_CAPS_LOCK: 527 offset = fKeys.caps_map[keyCode]; 528 table = B_CAPS_TABLE; 529 break; 530 case B_CAPS_LOCK | B_SHIFT_KEY: 531 offset = fKeys.caps_shift_map[keyCode]; 532 table = B_CAPS_SHIFT_TABLE; 533 break; 534 case B_CONTROL_KEY: 535 offset = fKeys.control_map[keyCode]; 536 table = B_CONTROL_TABLE; 537 break; 538 case B_OPTION_KEY: 539 offset = fKeys.option_map[keyCode]; 540 table = B_OPTION_TABLE; 541 break; 542 case B_OPTION_KEY | B_SHIFT_KEY: 543 offset = fKeys.option_shift_map[keyCode]; 544 table = B_OPTION_SHIFT_TABLE; 545 break; 546 case B_OPTION_KEY | B_CAPS_LOCK: 547 offset = fKeys.option_caps_map[keyCode]; 548 table = B_OPTION_CAPS_TABLE; 549 break; 550 case B_OPTION_KEY | B_SHIFT_KEY | B_CAPS_LOCK: 551 offset = fKeys.option_caps_shift_map[keyCode]; 552 table = B_OPTION_CAPS_SHIFT_TABLE; 553 break; 554 default: 555 offset = fKeys.normal_map[keyCode]; 556 table = B_NORMAL_TABLE; 557 break; 558 } 559 560 if (_table != NULL) 561 *_table = table; 562 563 if (offset >= (int32)fCharsSize) 564 return -1; 565 566 return offset; 567 } 568 569 570 uint8 571 BKeymap::DeadKeyIndex(int32 offset) const 572 { 573 if (fChars == NULL || offset <= 0) 574 return 0; 575 576 uint32 numBytes = fChars[offset]; 577 if (!numBytes || numBytes > 4) 578 return 0; 579 580 char chars[5]; 581 strncpy(chars, &fChars[offset + 1], numBytes); 582 chars[numBytes] = 0; 583 584 const int32 deadOffsets[] = { 585 fKeys.acute_dead_key[1], 586 fKeys.grave_dead_key[1], 587 fKeys.circumflex_dead_key[1], 588 fKeys.dieresis_dead_key[1], 589 fKeys.tilde_dead_key[1] 590 }; 591 592 uint8 result = 0; 593 for (int32 i = 0; i < 5; i++) { 594 if (offset == deadOffsets[i]) 595 return i + 1; 596 597 uint32 deadNumBytes = fChars[deadOffsets[i]]; 598 if (!deadNumBytes) 599 continue; 600 601 if (strncmp(chars, &fChars[deadOffsets[i] + 1], deadNumBytes) == 0) 602 return i + 1; 603 } 604 605 return result; 606 } 607