1 /* 2 * Copyright 2004-2009, 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 <errno.h> 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <ByteOrder.h> 18 #include <Entry.h> 19 #include <File.h> 20 #include <FindDirectory.h> 21 #include <Path.h> 22 #include <String.h> 23 24 25 #define CHARS_TABLE_MAXSIZE 10000 26 27 28 extern status_t _restore_key_map_(); 29 30 31 // i couldn't put patterns and pattern bufs on the stack without segfaulting 32 // regexp patterns 33 const char versionPattern[] 34 = "Version[[:space:]]+=[[:space:]]+\\([[:digit:]]+\\)"; 35 const char capslockPattern[] 36 = "CapsLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 37 const char scrolllockPattern[] 38 = "ScrollLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 39 const char numlockPattern[] 40 = "NumLock[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 41 const char lshiftPattern[] = "LShift[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 42 const char rshiftPattern[] = "RShift[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 43 const char lcommandPattern[] 44 = "LCommand[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 45 const char rcommandPattern[] 46 = "RCommand[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 47 const char lcontrolPattern[] 48 = "LControl[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 49 const char rcontrolPattern[] 50 = "RControl[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 51 const char loptionPattern[] 52 = "LOption[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 53 const char roptionPattern[] 54 = "ROption[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 55 const char menuPattern[] = "Menu[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\)"; 56 const char locksettingsPattern[] 57 = "LockSettings[[:space:]]+=[[:space:]]+\\([[:alnum:]]*\\)" 58 "[[:space:]]*\\([[:alnum:]]*\\)" 59 "[[:space:]]*\\([[:alnum:]]*\\)[[:space:]]*"; 60 const char keyPattern[] = "Key[[:space:]]+\\([[:alnum:]]+\\)[[:space:]]+=" 61 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 62 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 63 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 64 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 65 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 66 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 67 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 68 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 69 "[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 70 "[[:space:]]+"; 71 72 73 const char acutePattern[] = "Acute[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 74 "[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+"; 75 const char gravePattern[] = "Grave[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 76 "[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+"; 77 const char circumflexPattern[] = "Circumflex[[:space:]]+\\([[:alnum:]]" 78 "+\\|'.*'\\)[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+"; 79 const char diaeresisPattern[] = "Diaeresis[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 80 "[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+"; 81 const char tildePattern[] = "Tilde[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)" 82 "[[:space:]]+=[[:space:]]+\\([[:alnum:]]+\\|'.*'\\)[[:space:]]+"; 83 const char acutetabPattern[] = "AcuteTab[[:space:]]+=" 84 "[[:space:]]+\\([[:alnum:]-]*\\)" 85 "[[:space:]]*\\([[:alnum:]-]*\\)" 86 "[[:space:]]*\\([[:alnum:]-]*\\)" 87 "[[:space:]]*\\([[:alnum:]-]*\\)" 88 "[[:space:]]*\\([[:alnum:]-]*\\)" 89 "[[:space:]]*\\([[:alnum:]-]*\\)" 90 "[[:space:]]*\\([[:alnum:]-]*\\)" 91 "[[:space:]]*\\([[:alnum:]-]*\\)" 92 "[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ; 93 const char gravetabPattern[] = "GraveTab[[:space:]]+=" 94 "[[:space:]]+\\([[:alnum:]-]*\\)" 95 "[[:space:]]*\\([[:alnum:]-]*\\)" 96 "[[:space:]]*\\([[:alnum:]-]*\\)" 97 "[[:space:]]*\\([[:alnum:]-]*\\)" 98 "[[:space:]]*\\([[:alnum:]-]*\\)" 99 "[[:space:]]*\\([[:alnum:]-]*\\)" 100 "[[:space:]]*\\([[:alnum:]-]*\\)" 101 "[[:space:]]*\\([[:alnum:]-]*\\)" 102 "[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ; 103 const char circumflextabPattern[] = "CircumflexTab[[:space:]]+=" 104 "[[:space:]]+\\([[:alnum:]-]*\\)" 105 "[[:space:]]*\\([[:alnum:]-]*\\)" 106 "[[:space:]]*\\([[:alnum:]-]*\\)" 107 "[[:space:]]*\\([[:alnum:]-]*\\)" 108 "[[:space:]]*\\([[:alnum:]-]*\\)" 109 "[[:space:]]*\\([[:alnum:]-]*\\)" 110 "[[:space:]]*\\([[:alnum:]-]*\\)" 111 "[[:space:]]*\\([[:alnum:]-]*\\)" 112 "[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ; 113 const char diaeresistabPattern[] = "DiaeresisTab[[:space:]]+=" 114 "[[:space:]]+\\([[:alnum:]-]*\\)" 115 "[[:space:]]*\\([[:alnum:]-]*\\)" 116 "[[:space:]]*\\([[:alnum:]-]*\\)" 117 "[[:space:]]*\\([[:alnum:]-]*\\)" 118 "[[:space:]]*\\([[:alnum:]-]*\\)" 119 "[[:space:]]*\\([[:alnum:]-]*\\)" 120 "[[:space:]]*\\([[:alnum:]-]*\\)" 121 "[[:space:]]*\\([[:alnum:]-]*\\)" 122 "[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ; 123 const char tildetabPattern[] = "TildeTab[[:space:]]+=" 124 "[[:space:]]+\\([[:alnum:]-]*\\)" 125 "[[:space:]]*\\([[:alnum:]-]*\\)" 126 "[[:space:]]*\\([[:alnum:]-]*\\)" 127 "[[:space:]]*\\([[:alnum:]-]*\\)" 128 "[[:space:]]*\\([[:alnum:]-]*\\)" 129 "[[:space:]]*\\([[:alnum:]-]*\\)" 130 "[[:space:]]*\\([[:alnum:]-]*\\)" 131 "[[:space:]]*\\([[:alnum:]-]*\\)" 132 "[[:space:]]*\\([[:alnum:]-]*\\)[[:space:]]*" ; 133 134 135 // re_pattern_buffer buffers 136 struct re_pattern_buffer versionBuf; 137 struct re_pattern_buffer capslockBuf; 138 struct re_pattern_buffer scrolllockBuf; 139 struct re_pattern_buffer numlockBuf; 140 struct re_pattern_buffer lshiftBuf; 141 struct re_pattern_buffer rshiftBuf; 142 struct re_pattern_buffer lcommandBuf; 143 struct re_pattern_buffer rcommandBuf; 144 struct re_pattern_buffer lcontrolBuf; 145 struct re_pattern_buffer rcontrolBuf; 146 struct re_pattern_buffer loptionBuf; 147 struct re_pattern_buffer roptionBuf; 148 struct re_pattern_buffer menuBuf; 149 struct re_pattern_buffer locksettingsBuf; 150 struct re_pattern_buffer keyBuf; 151 struct re_pattern_buffer acuteBuf; 152 struct re_pattern_buffer graveBuf; 153 struct re_pattern_buffer circumflexBuf; 154 struct re_pattern_buffer diaeresisBuf; 155 struct re_pattern_buffer tildeBuf; 156 struct re_pattern_buffer acutetabBuf; 157 struct re_pattern_buffer gravetabBuf; 158 struct re_pattern_buffer circumflextabBuf; 159 struct re_pattern_buffer diaeresistabBuf; 160 struct re_pattern_buffer tildetabBuf; 161 162 163 void 164 dump_map(FILE* file, const char* name, int32* map) 165 { 166 fprintf(file, "\t%s:{\n", name); 167 168 for (uint32 i = 0; i < 16; i++) { 169 fprintf(file, "\t\t"); 170 for (uint32 j = 0; j < 8; j++) { 171 fprintf(file, "0x%04" B_PRIx32 ",%s", map[i * 8 + j], 172 j < 7 ? " " : ""); 173 } 174 fprintf(file, "\n"); 175 } 176 fprintf(file, "\t},\n"); 177 } 178 179 180 void 181 dump_keys(FILE* file, const char* name, int32* keys) 182 { 183 fprintf(file, "\t%s:{\n", name); 184 185 for (uint32 i = 0; i < 4; i++) { 186 fprintf(file, "\t\t"); 187 for (uint32 j = 0; j < 8; j++) { 188 fprintf(file, "0x%04" B_PRIx32 ",%s", keys[i * 8 + j], 189 j < 7 ? " " : ""); 190 } 191 fprintf(file, "\n"); 192 } 193 fprintf(file, "\t},\n"); 194 } 195 196 197 // #pragma mark - 198 199 200 Keymap::Keymap() 201 { 202 } 203 204 205 Keymap::~Keymap() 206 { 207 } 208 209 210 status_t 211 Keymap::LoadSource(const char* name) 212 { 213 FILE* file = fopen(name, "r"); 214 if (file == NULL) 215 return errno; 216 217 status_t status = LoadSource(file); 218 fclose(file); 219 220 return status; 221 } 222 223 224 status_t 225 Keymap::LoadSource(FILE* file) 226 { 227 // Setup regexp parser 228 229 reg_syntax_t syntax = RE_CHAR_CLASSES; 230 re_set_syntax(syntax); 231 232 const char* error = re_compile_pattern(versionPattern, 233 strlen(versionPattern), &versionBuf); 234 if (error) 235 fprintf(stderr, error); 236 error = re_compile_pattern(capslockPattern, strlen(capslockPattern), 237 &capslockBuf); 238 if (error) 239 fprintf(stderr, error); 240 error = re_compile_pattern(scrolllockPattern, strlen(scrolllockPattern), 241 &scrolllockBuf); 242 if (error) 243 fprintf(stderr, error); 244 error = re_compile_pattern(numlockPattern, strlen(numlockPattern), 245 &numlockBuf); 246 if (error) 247 fprintf(stderr, error); 248 error = re_compile_pattern(lshiftPattern, strlen(lshiftPattern), &lshiftBuf); 249 if (error) 250 fprintf(stderr, error); 251 error = re_compile_pattern(rshiftPattern, strlen(rshiftPattern), &rshiftBuf); 252 if (error) 253 fprintf(stderr, error); 254 error = re_compile_pattern(lcommandPattern, strlen(lcommandPattern), 255 &lcommandBuf); 256 if (error) 257 fprintf(stderr, error); 258 error = re_compile_pattern(rcommandPattern, strlen(rcommandPattern), 259 &rcommandBuf); 260 if (error) 261 fprintf(stderr, error); 262 error = re_compile_pattern(lcontrolPattern, strlen(lcontrolPattern), 263 &lcontrolBuf); 264 if (error) 265 fprintf(stderr, error); 266 error = re_compile_pattern(rcontrolPattern, strlen(rcontrolPattern), 267 &rcontrolBuf); 268 if (error) 269 fprintf(stderr, error); 270 error = re_compile_pattern(loptionPattern, strlen(loptionPattern), 271 &loptionBuf); 272 if (error) 273 fprintf(stderr, error); 274 error = re_compile_pattern(roptionPattern, strlen(roptionPattern), 275 &roptionBuf); 276 if (error) 277 fprintf(stderr, error); 278 error = re_compile_pattern(menuPattern, strlen(menuPattern), &menuBuf); 279 if (error) 280 fprintf(stderr, error); 281 error = re_compile_pattern(locksettingsPattern, strlen(locksettingsPattern), 282 &locksettingsBuf); 283 if (error) 284 fprintf(stderr, error); 285 error = re_compile_pattern(keyPattern, strlen(keyPattern), &keyBuf); 286 if (error) 287 fprintf(stderr, error); 288 error = re_compile_pattern(acutePattern, strlen(acutePattern), ´Buf); 289 if (error) 290 fprintf(stderr, error); 291 error = re_compile_pattern(gravePattern, strlen(gravePattern), &graveBuf); 292 if (error) 293 fprintf(stderr, error); 294 error = re_compile_pattern(circumflexPattern, strlen(circumflexPattern), 295 &circumflexBuf); 296 if (error) 297 fprintf(stderr, error); 298 error = re_compile_pattern(diaeresisPattern, strlen(diaeresisPattern), 299 &diaeresisBuf); 300 if (error) 301 fprintf(stderr, error); 302 error = re_compile_pattern(tildePattern, strlen(tildePattern), &tildeBuf); 303 if (error) 304 fprintf(stderr, error); 305 error = re_compile_pattern(acutetabPattern, strlen(acutetabPattern), 306 ´tabBuf); 307 if (error) 308 fprintf(stderr, error); 309 error = re_compile_pattern(gravetabPattern, strlen(gravetabPattern), 310 &gravetabBuf); 311 if (error) 312 fprintf(stderr, error); 313 error = re_compile_pattern(circumflextabPattern, 314 strlen(circumflextabPattern), &circumflextabBuf); 315 if (error) 316 fprintf(stderr, error); 317 error = re_compile_pattern(diaeresistabPattern, strlen(diaeresistabPattern), 318 &diaeresistabBuf); 319 if (error) 320 fprintf(stderr, error); 321 error = re_compile_pattern(tildetabPattern, strlen(tildetabPattern), 322 &tildetabBuf); 323 if (error) 324 fprintf(stderr, error); 325 326 // Read file 327 328 delete[] fChars; 329 fChars = new char[CHARS_TABLE_MAXSIZE]; 330 fCharsSize = CHARS_TABLE_MAXSIZE; 331 int offset = 0; 332 int acuteOffset = 0; 333 int graveOffset = 0; 334 int circumflexOffset = 0; 335 int diaeresisOffset = 0; 336 int tildeOffset = 0; 337 338 int32* maps[] = { 339 fKeys.normal_map, 340 fKeys.shift_map, 341 fKeys.control_map, 342 fKeys.option_map, 343 fKeys.option_shift_map, 344 fKeys.caps_map, 345 fKeys.caps_shift_map, 346 fKeys.option_caps_map, 347 fKeys.option_caps_shift_map 348 }; 349 350 char buffer[1024]; 351 352 while (fgets(buffer, sizeof(buffer) - 1, file) != NULL) { 353 if (buffer[0] == '#' || buffer[0] == '\n') 354 continue; 355 356 size_t length = strlen(buffer); 357 358 struct re_registers regs; 359 if (re_search(&versionBuf, buffer, length, 0, length, ®s) >= 0) { 360 sscanf(buffer + regs.start[1], "%" B_SCNu32, &fKeys.version); 361 } else if (re_search(&capslockBuf, buffer, length, 0, length, ®s) 362 >= 0) { 363 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &fKeys.caps_key); 364 } else if (re_search(&scrolllockBuf, buffer, length, 0, length, ®s) 365 >= 0) { 366 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &fKeys.scroll_key); 367 } else if (re_search(&numlockBuf, buffer, length, 0, length, ®s) 368 >= 0) { 369 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &fKeys.num_key); 370 } else if (re_search(&lshiftBuf, buffer, length, 0, length, ®s) 371 >= 0) { 372 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, 373 &fKeys.left_shift_key); 374 } else if (re_search(&rshiftBuf, buffer, length, 0, length, ®s) 375 >= 0) { 376 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, 377 &fKeys.right_shift_key); 378 } else if (re_search(&lcommandBuf, buffer, length, 0, length, ®s) 379 >= 0) { 380 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, 381 &fKeys.left_command_key); 382 } else if (re_search(&rcommandBuf, buffer, length, 0, length, ®s) 383 >= 0) { 384 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, 385 &fKeys.right_command_key); 386 } else if (re_search(&lcontrolBuf, buffer, length, 0, length, ®s) 387 >= 0) { 388 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, 389 &fKeys.left_control_key); 390 } else if (re_search(&rcontrolBuf, buffer, length, 0, length, ®s) 391 >= 0) { 392 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, 393 &fKeys.right_control_key); 394 } else if (re_search(&loptionBuf, buffer, length, 0, length, ®s) 395 >= 0) { 396 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, 397 &fKeys.left_option_key); 398 } else if (re_search(&roptionBuf, buffer, length, 0, length, ®s) 399 >= 0) { 400 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, 401 &fKeys.right_option_key); 402 } else if (re_search(&menuBuf, buffer, length, 0, length, ®s) >= 0) { 403 sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &fKeys.menu_key); 404 } else if (re_search(&locksettingsBuf, buffer, length, 0, length, ®s) 405 >= 0) { 406 fKeys.lock_settings = 0; 407 for (int32 i = 1; i <= 3; i++) { 408 const char* start = buffer + regs.start[i]; 409 length = regs.end[i] - regs.start[i]; 410 if (length == 0) 411 break; 412 413 if (!strncmp(start, "CapsLock", length)) 414 fKeys.lock_settings |= B_CAPS_LOCK; 415 else if (!strncmp(start, "NumLock", length)) 416 fKeys.lock_settings |= B_NUM_LOCK; 417 else if (!strncmp(start, "ScrollLock", length)) 418 fKeys.lock_settings |= B_SCROLL_LOCK; 419 } 420 } else if (re_search(&keyBuf, buffer, length, 0, length, ®s) >= 0) { 421 uint32 keyCode; 422 if (sscanf(buffer + regs.start[1], "0x%" B_SCNx32, &keyCode) > 0) { 423 for (int i = 2; i <= 10; i++) { 424 maps[i - 2][keyCode] = offset; 425 _ComputeChars(buffer, regs, i, offset); 426 } 427 } 428 } else if (re_search(´Buf, buffer, length, 0, length, ®s) >= 0) { 429 for (int i = 1; i <= 2; i++) { 430 fKeys.acute_dead_key[acuteOffset++] = offset; 431 _ComputeChars(buffer, regs, i, offset); 432 } 433 } else if (re_search(&graveBuf, buffer, length, 0, length, ®s) >= 0) { 434 for (int i = 1; i <= 2; i++) { 435 fKeys.grave_dead_key[graveOffset++] = offset; 436 _ComputeChars(buffer, regs, i, offset); 437 } 438 } else if (re_search(&circumflexBuf, buffer, length, 0, length, ®s) 439 >= 0) { 440 for (int i = 1; i <= 2; i++) { 441 fKeys.circumflex_dead_key[circumflexOffset++] = offset; 442 _ComputeChars(buffer, regs, i, offset); 443 } 444 } else if (re_search(&diaeresisBuf, buffer, length, 0, length, ®s) 445 >= 0) { 446 for (int i = 1; i <= 2; i++) { 447 fKeys.dieresis_dead_key[diaeresisOffset++] = offset; 448 _ComputeChars(buffer, regs, i, offset); 449 } 450 } else if (re_search(&tildeBuf, buffer, length, 0, length, ®s) >= 0) { 451 for (int i = 1; i <= 2; i++) { 452 fKeys.tilde_dead_key[tildeOffset++] = offset; 453 _ComputeChars(buffer, regs, i, offset); 454 } 455 } else if (re_search(´tabBuf, buffer, length, 0, length, ®s) 456 >= 0) { 457 _ComputeTables(buffer, regs, fKeys.acute_tables); 458 } else if (re_search(&gravetabBuf, buffer, length, 0, length, ®s) 459 >= 0) { 460 _ComputeTables(buffer, regs, fKeys.grave_tables); 461 } else if (re_search(&circumflextabBuf, buffer, length, 0, length, ®s) 462 >= 0) { 463 _ComputeTables(buffer, regs, fKeys.circumflex_tables); 464 } else if (re_search(&diaeresistabBuf, buffer, length, 0, length, ®s) 465 >= 0) { 466 _ComputeTables(buffer, regs, fKeys.dieresis_tables); 467 } else if (re_search(&tildetabBuf, buffer, length, 0, length, ®s) 468 >= 0) { 469 _ComputeTables(buffer, regs, fKeys.tilde_tables); 470 } 471 } 472 473 fCharsSize = offset; 474 475 if (fKeys.version != 3) 476 return KEYMAP_ERROR_UNKNOWN_VERSION; 477 478 return B_OK; 479 } 480 481 482 status_t 483 Keymap::SaveAsCurrent() 484 { 485 #if (defined(__BEOS__) || defined(__HAIKU__)) 486 BPath path; 487 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true); 488 if (status != B_OK) 489 return status; 490 491 path.Append("Key_map"); 492 493 status = Save(path.Path()); 494 if (status != B_OK) 495 return status; 496 497 Use(); 498 return B_OK; 499 #else // ! __BEOS__ 500 fprintf(stderr, "Unsupported operation on this platform!\n"); 501 exit(1); 502 #endif // ! __BEOS__ 503 } 504 505 506 //! Save a binary keymap to a file. 507 status_t 508 Keymap::Save(const char* name) 509 { 510 BFile file; 511 status_t status = file.SetTo(name, 512 B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); 513 if (status != B_OK) 514 return status; 515 516 // convert to big endian 517 for (uint32 i = 0; i < sizeof(fKeys) / sizeof(uint32); i++) { 518 ((uint32*)&fKeys)[i] = B_HOST_TO_BENDIAN_INT32(((uint32*)&fKeys)[i]); 519 } 520 521 ssize_t bytesWritten = file.Write(&fKeys, sizeof(fKeys)); 522 523 // convert endian back 524 for (uint32 i = 0; i < sizeof(fKeys) / sizeof(uint32); i++) { 525 ((uint32*)&fKeys)[i] = B_BENDIAN_TO_HOST_INT32(((uint32*)&fKeys)[i]); 526 } 527 528 if (bytesWritten < (ssize_t)sizeof(fKeys)) 529 return B_ERROR; 530 if (bytesWritten < B_OK) 531 return bytesWritten; 532 533 uint32 charSize = B_HOST_TO_BENDIAN_INT32(fCharsSize); 534 535 bytesWritten = file.Write(&charSize, sizeof(uint32)); 536 if (bytesWritten < (ssize_t)sizeof(uint32)) 537 return B_ERROR; 538 if (bytesWritten < B_OK) 539 return bytesWritten; 540 541 bytesWritten = file.Write(fChars, fCharsSize); 542 if (bytesWritten < (ssize_t)fCharsSize) 543 return B_ERROR; 544 if (bytesWritten < B_OK) 545 return bytesWritten; 546 547 return B_OK; 548 } 549 550 551 status_t 552 Keymap::SaveAsSource(const char* name) 553 { 554 FILE* file = fopen(name, "w"); 555 if (file == NULL) 556 return errno; 557 558 #if (defined(__BEOS__) || defined(__HAIKU__)) 559 text_run_array* textRuns; 560 _SaveSourceText(file, &textRuns); 561 562 if (textRuns != NULL) { 563 ssize_t dataSize; 564 void* data = BTextView::FlattenRunArray(textRuns, &dataSize); 565 if (data != NULL) { 566 BNode node(name); 567 node.WriteAttr("styles", B_RAW_TYPE, 0, data, dataSize); 568 569 free(data); 570 } 571 572 BTextView::FreeRunArray(textRuns); 573 } 574 #else 575 _SaveSourceText(file); 576 #endif 577 578 return B_OK; 579 } 580 581 582 status_t 583 Keymap::SaveAsSource(FILE* file) 584 { 585 _SaveSourceText(file); 586 return B_OK; 587 } 588 589 590 /*! Save a keymap as C source file - this is used to get the default keymap 591 into the input_server, for example. 592 \a mapName is usually the path of the input keymap, and is used as the 593 name of the keymap (the path will be removed, as well as its suffix). 594 */ 595 status_t 596 Keymap::SaveAsCppHeader(const char* fileName, const char* mapName) 597 { 598 BString name = mapName; 599 600 // cut off path 601 int32 index = name.FindLast('/'); 602 if (index > 0) 603 name.Remove(0, index + 1); 604 605 // prune ".keymap" 606 index = name.FindLast('.'); 607 if (index > 0) 608 name.Truncate(index); 609 610 FILE* file = fopen(fileName, "w"); 611 if (file == NULL) 612 return errno; 613 614 fprintf(file, "/*\n" 615 " * Haiku Keymap\n" 616 " * This file is generated automatically. Don't edit!\n" 617 " */\n\n"); 618 fprintf(file, "#include <InterfaceDefs.h>\n\n"); 619 fprintf(file, "const char *kSystemKeymapName = \"%s\";\n\n", name.String()); 620 fprintf(file, "const key_map kSystemKeymap = {\n"); 621 fprintf(file, "\tversion:%" B_PRIu32 ",\n", fKeys.version); 622 fprintf(file, "\tcaps_key:0x%" B_PRIx32 ",\n", fKeys.caps_key); 623 fprintf(file, "\tscroll_key:0x%" B_PRIx32 ",\n", fKeys.scroll_key); 624 fprintf(file, "\tnum_key:0x%" B_PRIx32 ",\n", fKeys.num_key); 625 fprintf(file, "\tleft_shift_key:0x%" B_PRIx32 ",\n", fKeys.left_shift_key); 626 fprintf(file, "\tright_shift_key:0x%" B_PRIx32 ",\n", 627 fKeys.right_shift_key); 628 fprintf(file, "\tleft_command_key:0x%" B_PRIx32 ",\n", 629 fKeys.left_command_key); 630 fprintf(file, "\tright_command_key:0x%" B_PRIx32 ",\n", 631 fKeys.right_command_key); 632 fprintf(file, "\tleft_control_key:0x%" B_PRIx32 ",\n", 633 fKeys.left_control_key); 634 fprintf(file, "\tright_control_key:0x%" B_PRIx32 ",\n", 635 fKeys.right_control_key); 636 fprintf(file, "\tleft_option_key:0x%" B_PRIx32 ",\n", 637 fKeys.left_option_key); 638 fprintf(file, "\tright_option_key:0x%" B_PRIx32 ",\n", 639 fKeys.right_option_key); 640 fprintf(file, "\tmenu_key:0x%" B_PRIx32 ",\n", fKeys.menu_key); 641 fprintf(file, "\tlock_settings:0x%" B_PRIx32 ",\n", fKeys.lock_settings); 642 643 dump_map(file, "control_map", fKeys.control_map); 644 dump_map(file, "option_caps_shift_map", fKeys.option_caps_shift_map); 645 dump_map(file, "option_caps_map", fKeys.option_caps_map); 646 dump_map(file, "option_shift_map", fKeys.option_shift_map); 647 dump_map(file, "option_map", fKeys.option_map); 648 dump_map(file, "caps_shift_map", fKeys.caps_shift_map); 649 dump_map(file, "caps_map", fKeys.caps_map); 650 dump_map(file, "shift_map", fKeys.shift_map); 651 dump_map(file, "normal_map", fKeys.normal_map); 652 653 dump_keys(file, "acute_dead_key", fKeys.acute_dead_key); 654 dump_keys(file, "grave_dead_key", fKeys.grave_dead_key); 655 656 dump_keys(file, "circumflex_dead_key", fKeys.circumflex_dead_key); 657 dump_keys(file, "dieresis_dead_key", fKeys.dieresis_dead_key); 658 dump_keys(file, "tilde_dead_key", fKeys.tilde_dead_key); 659 660 fprintf(file, "\tacute_tables:0x%" B_PRIx32 ",\n", fKeys.acute_tables); 661 fprintf(file, "\tgrave_tables:0x%" B_PRIx32 ",\n", fKeys.grave_tables); 662 fprintf(file, "\tcircumflex_tables:0x%" B_PRIx32 ",\n", 663 fKeys.circumflex_tables); 664 fprintf(file, "\tdieresis_tables:0x%" B_PRIx32 ",\n", 665 fKeys.dieresis_tables); 666 fprintf(file, "\ttilde_tables:0x%" B_PRIx32 ",\n", fKeys.tilde_tables); 667 668 fprintf(file, "};\n\n"); 669 670 fprintf(file, "const char kSystemKeyChars[] = {\n"); 671 for (uint32 i = 0; i < fCharsSize; i++) { 672 if (i % 10 == 0) { 673 if (i > 0) 674 fprintf(file, "\n"); 675 fprintf(file, "\t"); 676 } else 677 fprintf(file, " "); 678 679 fprintf(file, "0x%02x,", (uint8)fChars[i]); 680 } 681 fprintf(file, "\n};\n\n"); 682 683 fprintf(file, "const uint32 kSystemKeyCharsSize = %" B_PRIu32 ";\n", 684 fCharsSize); 685 fclose(file); 686 687 return B_OK; 688 } 689 690 691 //! We make our input server use the map in /boot/home/config/settings/Keymap 692 status_t 693 Keymap::Use() 694 { 695 #if (defined(__BEOS__) || defined(__HAIKU__)) 696 return _restore_key_map_(); 697 698 #else // ! __BEOS__ 699 fprintf(stderr, "Unsupported operation on this platform!\n"); 700 exit(1); 701 #endif // ! __BEOS__ 702 } 703 704 705 void 706 Keymap::RestoreSystemDefault() 707 { 708 #if (defined(__BEOS__) || defined(__HAIKU__)) 709 // work-around to get rid of this stupid find_directory_r() on Zeta 710 # ifdef find_directory 711 # undef find_directory 712 # endif 713 BPath path; 714 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 715 return; 716 717 path.Append("Key_map"); 718 719 BEntry entry(path.Path()); 720 entry.Remove(); 721 722 _restore_key_map_(); 723 #else // ! __BEOS__ 724 fprintf(stderr, "Unsupported operation on this platform!\n"); 725 exit(1); 726 #endif // ! __BEOS__ 727 } 728 729 730 /*static*/ bool 731 Keymap::GetKey(const char* chars, int32 offset, char* buffer, size_t bufferSize) 732 { 733 uint8 size = (uint8)chars[offset++]; 734 char string[1024]; 735 736 switch (size) { 737 case 0: 738 // Not mapped 739 strlcpy(buffer, "''", bufferSize); 740 return false; 741 742 case 1: 743 // 1-byte UTF-8/ASCII character 744 if ((uint8)chars[offset] < 0x20 || (uint8)chars[offset] > 0x7e) 745 sprintf(string, "0x%02x", (uint8)chars[offset]); 746 else { 747 sprintf(string, "'%s%c'", 748 (chars[offset] == '\\' || chars[offset] == '\'') ? "\\" : "", 749 chars[offset]); 750 } 751 break; 752 753 default: 754 // multi-byte UTF-8 character 755 sprintf(string, "0x"); 756 for (int i = 0; i < size; i++) { 757 sprintf(string + 2 * (i + 1), "%02x", (uint8)chars[offset + i]); 758 } 759 break; 760 } 761 762 strlcpy(buffer, string, bufferSize); 763 return true; 764 } 765 766 767 #if (defined(__BEOS__) || defined(__HAIKU__)) 768 void 769 Keymap::_SaveSourceText(FILE* file, text_run_array** _textRuns) 770 { 771 text_run_array* runs = NULL; 772 if (_textRuns != NULL) { 773 runs = BTextView::AllocRunArray(8); 774 *_textRuns = runs; 775 } 776 #else 777 void 778 Keymap::_SaveSourceText(FILE* file) 779 { 780 #endif 781 782 static const rgb_color kCommentColor = (rgb_color){200, 92, 92, 255}; 783 static const rgb_color kTextColor = (rgb_color){0, 0, 0, 255}; 784 785 #if (defined(__BEOS__) || defined(__HAIKU__)) 786 BFont font = *be_fixed_font; 787 788 if (runs != NULL) { 789 runs->runs[0].offset = 0; 790 runs->runs[0].font = font; 791 runs->runs[0].color = kCommentColor; 792 } 793 #endif 794 795 int bytes = fprintf(file, "#!/bin/keymap -s\n" 796 "#\n" 797 "#\tRaw key numbering for 101 keyboard...\n"); 798 799 #if (defined(__BEOS__) || defined(__HAIKU__)) 800 if (runs != NULL) { 801 runs->runs[1].offset = bytes; 802 runs->runs[1].font = font; 803 runs->runs[1].font.SetSize(9); 804 runs->runs[1].color = kCommentColor; 805 } 806 #endif 807 808 bytes += fprintf(file, "# [sys] [brk]\n" 809 "# 0x7e 0x7f\n" 810 "# [esc] [ f1] [ f2] [ f3] [ f4] [ f5] [ f6] [ f7] [ f8] [ f9] [f10] [f11] [f12] [prn] [scr] [pau]\n" 811 "# 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 K E Y P A D K E Y S\n" 812 "#\n" 813 "# [ ` ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 0 ] [ - ] [ = ] [bck] [ins] [hme] [pup] [num] [ / ] [ * ] [ - ]\n" 814 "# 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f 0x20 0x21 0x22 0x23 0x24 0x25\n" 815 "#\n" 816 "# [tab] [ q ] [ w ] [ e ] [ r ] [ t ] [ y ] [ u ] [ i ] [ o ] [ p ] [ [ ] [ ] ] [ \\ ] [del] [end] [pdn] [ 7 ] [ 8 ] [ 9 ] [ + ]\n" 817 "# 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a\n" 818 "#\n" 819 "# [cap] [ a ] [ s ] [ d ] [ f ] [ g ] [ h ] [ j ] [ k ] [ l ] [ ; ] [ ' ] [ enter ] [ 4 ] [ 5 ] [ 6 ]\n" 820 "# 0x3b 0x3c 0x3d 0x3e 0x3f 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4a\n" 821 "#\n" 822 "# [shift] [ z ] [ x ] [ c ] [ v ] [ b ] [ n ] [ m ] [ , ] [ . ] [ / ] [shift] [ up] [ 1 ] [ 2 ] [ 3 ] [ent]\n" 823 "# 0x4b 0x4c 0x4d 0x4e 0x4f 0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5a 0x5b\n" 824 "#\n" 825 "# [ctr] [cmd] [ space ] [cmd] [ctr] [lft] [dwn] [rgt] [ 0 ] [ . ]\n" 826 "# 0x5c 0x5d 0x5e 0x5f 0x60 0x61 0x62 0x63 0x64 0x65\n"); 827 828 #if (defined(__BEOS__) || defined(__HAIKU__)) 829 if (runs != NULL) { 830 runs->runs[2].offset = bytes; 831 runs->runs[2].font = font; 832 runs->runs[2].color = kCommentColor; 833 } 834 #endif 835 836 bytes += fprintf(file, "#\n" 837 "#\tNOTE: On a Microsoft Natural Keyboard:\n" 838 "#\t\t\tleft option = 0x66\n" 839 "#\t\t\tright option = 0x67\n" 840 "#\t\t\tmenu key = 0x68\n" 841 "#\tNOTE: On an Apple Extended Keyboard:\n" 842 "#\t\t\tleft option = 0x66\n" 843 "#\t\t\tright option = 0x67\n" 844 "#\t\t\tkeypad '=' = 0x6a\n" 845 "#\t\t\tpower key = 0x6b\n"); 846 847 #if (defined(__BEOS__) || defined(__HAIKU__)) 848 if (runs != NULL) { 849 runs->runs[3].offset = bytes; 850 runs->runs[3].font = *be_fixed_font; 851 runs->runs[3].color = kTextColor; 852 } 853 #endif 854 855 bytes += fprintf(file, "Version = %" B_PRIu32 "\n" 856 "CapsLock = 0x%02" B_PRIx32 "\n" 857 "ScrollLock = 0x%02" B_PRIx32 "\n" 858 "NumLock = 0x%02" B_PRIx32 "\n" 859 "LShift = 0x%02" B_PRIx32 "\n" 860 "RShift = 0x%02" B_PRIx32 "\n" 861 "LCommand = 0x%02" B_PRIx32 "\n" 862 "RCommand = 0x%02" B_PRIx32 "\n" 863 "LControl = 0x%02" B_PRIx32 "\n" 864 "RControl = 0x%02" B_PRIx32 "\n" 865 "LOption = 0x%02" B_PRIx32 "\n" 866 "ROption = 0x%02" B_PRIx32 "\n" 867 "Menu = 0x%02" B_PRIx32 "\n", 868 fKeys.version, fKeys.caps_key, fKeys.scroll_key, fKeys.num_key, 869 fKeys.left_shift_key, fKeys.right_shift_key, fKeys.left_command_key, 870 fKeys.right_command_key, fKeys.left_control_key, fKeys.right_control_key, 871 fKeys.left_option_key, fKeys.right_option_key, fKeys.menu_key); 872 873 #if (defined(__BEOS__) || defined(__HAIKU__)) 874 if (runs != NULL) { 875 runs->runs[4].offset = bytes; 876 runs->runs[4].font = *be_fixed_font; 877 runs->runs[4].color = kCommentColor; 878 } 879 #endif 880 881 bytes += fprintf(file, "#\n" 882 "# Lock settings\n" 883 "# To set NumLock, do the following:\n" 884 "# LockSettings = NumLock\n" 885 "#\n" 886 "# To set everything, do the following:\n" 887 "# LockSettings = CapsLock NumLock ScrollLock\n" 888 "#\n"); 889 890 #if (defined(__BEOS__) || defined(__HAIKU__)) 891 if (runs != NULL) { 892 runs->runs[5].offset = bytes; 893 runs->runs[5].font = *be_fixed_font; 894 runs->runs[5].color = kTextColor; 895 } 896 #endif 897 898 bytes += fprintf(file, "LockSettings = "); 899 if ((fKeys.lock_settings & B_CAPS_LOCK) != 0) 900 bytes += fprintf(file, "CapsLock "); 901 if ((fKeys.lock_settings & B_NUM_LOCK) != 0) 902 bytes += fprintf(file, "NumLock "); 903 if ((fKeys.lock_settings & B_SCROLL_LOCK) != 0) 904 bytes += fprintf(file, "ScrollLock "); 905 bytes += fprintf(file, "\n"); 906 907 #if (defined(__BEOS__) || defined(__HAIKU__)) 908 if (runs != NULL) { 909 runs->runs[6].offset = bytes; 910 runs->runs[6].font = *be_fixed_font; 911 runs->runs[6].color = kCommentColor; 912 } 913 #endif 914 915 bytes += fprintf(file, "# Legend:\n" 916 "# n = Normal\n" 917 "# s = Shift\n" 918 "# c = Control\n" 919 "# C = CapsLock\n" 920 "# o = Option\n" 921 "# Key n s c o os C Cs Co Cos \n"); 922 923 #if (defined(__BEOS__) || defined(__HAIKU__)) 924 if (runs != NULL) { 925 runs->runs[7].offset = bytes; 926 runs->runs[7].font = *be_fixed_font; 927 runs->runs[7].color = kTextColor; 928 } 929 #endif 930 931 for (int i = 0; i < 128; i++) { 932 char normalKey[32]; 933 char shiftKey[32]; 934 char controlKey[32]; 935 char optionKey[32]; 936 char optionShiftKey[32]; 937 char capsKey[32]; 938 char capsShiftKey[32]; 939 char optionCapsKey[32]; 940 char optionCapsShiftKey[32]; 941 942 GetKey(fChars, fKeys.normal_map[i], normalKey, 32); 943 GetKey(fChars, fKeys.shift_map[i], shiftKey, 32); 944 GetKey(fChars, fKeys.control_map[i], controlKey, 32); 945 GetKey(fChars, fKeys.option_map[i], optionKey, 32); 946 GetKey(fChars, fKeys.option_shift_map[i], optionShiftKey, 32); 947 GetKey(fChars, fKeys.caps_map[i], capsKey, 32); 948 GetKey(fChars, fKeys.caps_shift_map[i], capsShiftKey, 32); 949 GetKey(fChars, fKeys.option_caps_map[i], optionCapsKey, 32); 950 GetKey(fChars, fKeys.option_caps_shift_map[i], optionCapsShiftKey, 32); 951 952 fprintf(file, "Key 0x%02x = %-9s%-9s%-9s%-9s%-9s%-9s%-9s%-9s%-9s\n", i, 953 normalKey, shiftKey, controlKey, optionKey, optionShiftKey, capsKey, 954 capsShiftKey, optionCapsKey, optionCapsShiftKey); 955 } 956 957 int32* deadOffsets[] = { 958 fKeys.acute_dead_key, 959 fKeys.grave_dead_key, 960 fKeys.circumflex_dead_key, 961 fKeys.dieresis_dead_key, 962 fKeys.tilde_dead_key 963 }; 964 965 char labels[][12] = { 966 "Acute", 967 "Grave", 968 "Circumflex", 969 "Diaeresis", 970 "Tilde" 971 }; 972 973 uint32 deadTables[] = { 974 fKeys.acute_tables, 975 fKeys.grave_tables, 976 fKeys.circumflex_tables, 977 fKeys.dieresis_tables, 978 fKeys.tilde_tables 979 }; 980 981 for (int i = 0; i < 5; i++) { 982 for (int deadIndex = 0; deadIndex < 32; deadIndex++) { 983 char deadKey[32]; 984 char secondKey[32]; 985 if (!GetKey(fChars, deadOffsets[i][deadIndex++], deadKey, 32)) 986 break; 987 988 GetKey(fChars, deadOffsets[i][deadIndex], secondKey, 32); 989 fprintf(file, "%s %-9s = %-9s\n", labels[i], deadKey, secondKey); 990 } 991 992 fprintf(file, "%sTab = ", labels[i]); 993 994 if (deadTables[i] & B_NORMAL_TABLE) 995 fprintf(file, "Normal "); 996 if (deadTables[i] & B_SHIFT_TABLE) 997 fprintf(file, "Shift "); 998 if (deadTables[i] & B_CONTROL_TABLE) 999 fprintf(file, "Control "); 1000 if (deadTables[i] & B_OPTION_TABLE) 1001 fprintf(file, "Option "); 1002 if (deadTables[i] & B_OPTION_SHIFT_TABLE) 1003 fprintf(file, "Option-Shift "); 1004 if (deadTables[i] & B_CAPS_TABLE) 1005 fprintf(file, "CapsLock "); 1006 if (deadTables[i] & B_CAPS_SHIFT_TABLE) 1007 fprintf(file, "CapsLock-Shift "); 1008 if (deadTables[i] & B_OPTION_CAPS_TABLE) 1009 fprintf(file, "CapsLock-Option "); 1010 if (deadTables[i] & B_OPTION_CAPS_SHIFT_TABLE) 1011 fprintf(file, "CapsLock-Option-Shift "); 1012 fprintf(file, "\n"); 1013 } 1014 } 1015 1016 1017 void 1018 Keymap::_ComputeChars(const char* buffer, struct re_registers& regs, int i, 1019 int& offset) 1020 { 1021 char* current = &fChars[offset + 1]; 1022 char hexChars[12]; 1023 uint32 length = 0; 1024 1025 if (strncmp(buffer + regs.start[i], "''", regs.end[i] - regs.start[i]) == 0) 1026 length = 0; 1027 else if (sscanf(buffer + regs.start[i], "'%s'", current) > 0) { 1028 if (current[0] == '\\') 1029 current[0] = current[1]; 1030 else if (current[0] == '\'') 1031 current[0] = ' '; 1032 length = 1; 1033 } else if (sscanf(buffer + regs.start[i], "0x%s", hexChars) > 0) { 1034 length = strlen(hexChars) / 2; 1035 for (uint32 j = 0; j < length; j++) 1036 sscanf(hexChars + 2*j, "%02hhx", current + j); 1037 } 1038 1039 fChars[offset] = length; 1040 offset += length + 1; 1041 } 1042 1043 1044 void 1045 Keymap::_ComputeTables(const char* buffer, struct re_registers& regs, 1046 uint32& table) 1047 { 1048 for (int32 i = 1; i <= 9; i++) { 1049 int32 length = regs.end[i] - regs.start[i]; 1050 if (length <= 0) 1051 break; 1052 1053 const char* start = buffer + regs.start[i]; 1054 1055 if (strncmp(start, "Normal", length) == 0) 1056 table |= B_NORMAL_TABLE; 1057 else if (strncmp(start, "Shift", length) == 0) 1058 table |= B_SHIFT_TABLE; 1059 else if (strncmp(start, "Control", length) == 0) 1060 table |= B_CONTROL_TABLE; 1061 else if (strncmp(start, "Option", length) == 0) 1062 table |= B_OPTION_TABLE; 1063 else if (strncmp(start, "Option-Shift", length) == 0) 1064 table |= B_OPTION_SHIFT_TABLE; 1065 else if (strncmp(start, "CapsLock", length) == 0) 1066 table |= B_CAPS_TABLE; 1067 else if (strncmp(start, "CapsLock-Shift", length) == 0) 1068 table |= B_CAPS_SHIFT_TABLE; 1069 else if (strncmp(start, "CapsLock-Option", length) == 0) 1070 table |= B_OPTION_CAPS_TABLE; 1071 else if (strncmp(start, "CapsLock-Option-Shift", length) == 0) 1072 table |= B_OPTION_CAPS_SHIFT_TABLE; 1073 } 1074 } 1075