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