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