1 /* 2 * Copyright 2009, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "KeyboardLayout.h" 8 9 #include <ctype.h> 10 #include <stdarg.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <vector> 14 15 #include <File.h> 16 #include <InterfaceDefs.h> 17 18 19 #undef TRACE 20 21 //#define TRACE_LAYOUT 22 #ifdef TRACE_LAYOUT 23 # define TRACE(x...) printf(x) 24 #else 25 # define TRACE(x...) 26 #endif 27 28 29 KeyboardLayout::KeyboardLayout() 30 : 31 fKeys(NULL), 32 fKeyCount(0), 33 fKeyCapacity(0), 34 fIndicators(5, true), 35 fIsDefault(true) 36 { 37 SetDefault(); 38 } 39 40 41 KeyboardLayout::~KeyboardLayout() 42 { 43 free(fKeys); 44 } 45 46 47 const char* 48 KeyboardLayout::Name() 49 { 50 return fName.String(); 51 } 52 53 54 int32 55 KeyboardLayout::CountKeys() 56 { 57 return fKeyCount; 58 } 59 60 61 Key* 62 KeyboardLayout::KeyAt(int32 index) 63 { 64 if (index < 0 || index >= fKeyCount) 65 return NULL; 66 67 return &fKeys[index]; 68 } 69 70 71 int32 72 KeyboardLayout::CountIndicators() 73 { 74 return fIndicators.CountItems(); 75 } 76 77 78 Indicator* 79 KeyboardLayout::IndicatorAt(int32 index) 80 { 81 return fIndicators.ItemAt(index); 82 } 83 84 85 BRect 86 KeyboardLayout::Bounds() 87 { 88 return fBounds; 89 } 90 91 92 BSize 93 KeyboardLayout::DefaultKeySize() 94 { 95 return fDefaultKeySize; 96 } 97 98 99 int32 100 KeyboardLayout::IndexForModifier(int32 modifier) 101 { 102 switch(modifier) { 103 case B_CAPS_LOCK: 104 return 58; 105 case B_NUM_LOCK: 106 return 33; 107 case B_SCROLL_LOCK: 108 return 14; 109 case B_LEFT_SHIFT_KEY: 110 return 74; 111 case B_RIGHT_SHIFT_KEY: 112 return 85; 113 case B_LEFT_CONTROL_KEY: 114 return 91; 115 case B_RIGHT_CONTROL_KEY: 116 return 98; 117 case B_LEFT_OPTION_KEY: 118 return 92; 119 case B_RIGHT_OPTION_KEY: 120 return 96; 121 case B_LEFT_COMMAND_KEY: 122 return 93; 123 case B_RIGHT_COMMAND_KEY: 124 return 95; 125 case B_MENU_KEY: 126 return 97; 127 } 128 129 return 0; 130 } 131 132 133 status_t 134 KeyboardLayout::Load(const char* path) 135 { 136 entry_ref ref; 137 get_ref_for_path(path, &ref); 138 139 return Load(ref); 140 } 141 142 143 status_t 144 KeyboardLayout::Load(entry_ref& ref) 145 { 146 BFile file; 147 status_t status = file.SetTo(&ref, B_READ_ONLY); 148 if (status != B_OK) 149 return status; 150 151 off_t size; 152 status = file.GetSize(&size); 153 if (status != B_OK) 154 return status; 155 156 if (size > 65536) { 157 // We don't accept files larger than this 158 return B_BAD_VALUE; 159 } 160 161 char* data = (char*)malloc(size + 1); 162 if (data == NULL) 163 return B_NO_MEMORY; 164 165 ssize_t bytesRead = file.Read(data, size); 166 if (bytesRead != size) { 167 free(data); 168 169 if (bytesRead < 0) 170 return bytesRead; 171 172 return B_IO_ERROR; 173 } 174 175 data[size] = '\0'; 176 177 status = _InitFrom(data); 178 if (status == B_OK) 179 fIsDefault = false; 180 181 free(data); 182 183 return status; 184 } 185 186 187 void 188 KeyboardLayout::SetDefault() 189 { 190 // The keyboard layout description language defines the position, 191 // size, shape, and scancodes of the keys on the keyboard, row by 192 // row. 193 // You can define variables that are substituted in the row 194 // descriptions. Variables are always prefixed with a '$' symbol. 195 196 // On top level, only two different terms are accepted: value pairs, 197 // and row descriptions. 198 // Value pairs are in the form "<name> = <value>". There are only two 199 // predefined names at this moment "name" that specifies the name of 200 // a layout, and "default-size" that specifies the size of keys when 201 // no specific size is given. Also, variables can be specified this 202 // way. 203 204 // Row descriptions are embedded in the '[' and ']' brackets. 205 // The first term within a row is the position of the row, written as 206 // "<x>,<y>;" - the delimiter between terms/keys is usually the 207 // semi-colon. After the initial position, key descriptions are expected 208 // until the row is closed with a ']'. 209 210 // A key description is of the form "<shape>:<scancodes>", where <shape> 211 // is combined of the shape of the character, and its size, written as 212 // "<kind><width>,<height>[,<second-row-width>]". 213 // The kind can either be 'r' (the default, can also be omitted) for 214 // rectangular keys, or 'l' for the enter key. Additionally, you can use 215 // the 'd' character to mark dark keys. The size can be omitted completely, 216 // in which case the defined "default-size" is used. The default size is 217 // also used to determine the height of the first row of the enter key; 218 // the "<second-row-width>" specifier is only valid for enter keys, too. 219 220 // The scancodes can be written in different ways: 221 // 1) "+<count>": adds <count> keys, the scancodes will be used relative 222 // to the previous keys. 223 // 2) "<first>-<last>": keys are added for scancode <first> to scancode 224 // <last>. 225 // 3) "<first>+<count>": one key with the <first> scancode is added, plus 226 // <count> ones with relative scancode from that one. 227 228 // Finally, you can also define LED indicator. Those can be made instead 229 // of a key, it accepts a size, but no shape modifiers. Also, instead of 230 // a scancode, the name of the modifer is given, currently only the 231 // following are allowed: "led-num", "led-caps", and "led-scroll". 232 233 // See for examples in the default layout below. 234 #if 1 235 static const char* kDefaultLayout105 = 236 "name = Generic 105-key International\n" 237 // Size shortcuts 238 "default-size = 10,10\n" 239 "$b = 5,10\n" 240 "$c = 20,10\n" 241 "$d = 15,10\n" 242 "$e = l15,20,9\n" 243 "$f = 10,20\n" 244 "$g = 13,10\n" 245 // Key rows 246 "[ 0,0; d:0x01; :-; :+4; $b:-; d:+4; $b:-; :+4; $b:-; d:+3; $b:-; " 247 "$g:led-num; $g:led-caps; $g:led-scroll ]\n" 248 "[ 0,20; :+13; d$c:+; $b:-; d:+3; $b:-; d:+4 ]\n" 249 "[ 0,30; d$d:0x26; :+12; d$e:0x47; $b:-; d:0x34-0x36; $b:-; :+3; " 250 "d$f:+1 ]\n" 251 "[ 0,40; d19,10:0x3b; :+11; :0x33; 51,10:-; :0x48-0x4a ]\n" 252 "[ 0,50; d$g:0x4b; :0x69; :0x4c+9; d27,10:+1; 15,10:-; d:+1; " 253 " 15,10:-; :+3; d$f:+1 ]\n" 254 "[ 0,60; d$g:0x5c; d$g:0x66; d$g:0x5d; 59,10:+1; d$g:+1; d$g:0x67+1; " 255 "d$g:0x60; $b:-; d:+3; $b:-; $c:+1; :+1 ]\n"; 256 257 _InitFrom(kDefaultLayout105); 258 #endif 259 #if 0 260 static const char* kDefaultLayout104 = "name = Generic 104-key\n" 261 // Size shortcuts 262 "default-size = 10,10\n" 263 "$b = 5,10\n" 264 "$c = 20,10\n" 265 "$d = 15,10\n" 266 "$enter = d20,10\n" 267 "$f = 10,20\n" 268 "$g = 13,10\n" 269 // Key rows 270 "[ 0,0; d:0x01; :-; :+4; $b:-; d:+4; $b:-; :+4; $b:-; d:+3; $b:-; " 271 "$g:led-num; $g:led-caps; $g:led-scroll ]\n" 272 "[ 0,20; :+13; d$c:+; $b:-; d:+3; $b:-; d:+4 ]\n" 273 "[ 0,30; d$d:0x26; :+12; $d:+1; $b:-; d:+3; $b:-; :+3; " 274 "d$f:+1 ]\n" 275 "[ 0,40; d$c:0x3b; :+11; $enter:+1; 40,10:-; :+3 ]\n" 276 "[ 0,50; d24,10:0x4b; :+10; d26,10:+1; 15,10:-; d:+1; " 277 " 15,10:-; :+3; d$f:+1 ]\n" 278 "[ 0,60; d$g:0x5c; d$g:0x66; d$g:0x5d; 59,10:+1; d$g:+1; d$g:0x67+1; " 279 "d$g:0x60; $b:-; d:+3; $b:-; $c:+1; :+1 ]\n"; 280 281 _InitFrom(kDefaultLayout104); 282 #endif 283 #if 0 284 static const char* kIBMLaptop = "name = IBM Laptop International\n" 285 // Size shortcuts 286 "default-size = 18,18\n" 287 "$s = 17,10\n" 288 "$gap = 6,10\n" 289 "$sgap = 5,10\n" 290 "$backspace = 38,18\n" 291 "$tab = 28,18\n" 292 "$caps = 32,18\n" 293 "$enter = l28,36,22\n" 294 "$l-shift-ctrl = 23,18\n" 295 "$r-shift = 51,18\n" 296 "$option = 13,18\n" 297 "$space = 95,18\n" 298 // Key rows 299 "[ 0,0; $s:0x01; 148,10:-; $s:0x0e+2; $sgap:-; $s:0x1f+2; ]\n" 300 "[ 0,10; $s:0x02+3; $gap:-; $s:+4; $gap:-; $s:+4; $sgap:-; " 301 "$s:0x34+2; ]\n" 302 "[ 0,20; :0x11+12; $backspace:+1 ]\n" 303 "[ 0,38; $tab:0x26; :+12; $enter:0x47; ]\n" 304 "[ 0,56; $caps:0x3b; :+11; :0x33 ]\n" 305 "[ 0,74; $l-shift-ctrl:0x4b; :0x69; :0x4c+9; $r-shift:+1 ]\n" 306 "[ 0,92; :0x99; $l-shift-ctrl:0x5c; $option:0x66; :0x5d; $space:+1; " 307 ":+1; :0x68; :0x60; $s:0x9a; $s:0x57; $s:0x9b ]\n" 308 "[ 221,102; $s:0x61+2; ]\n"; 309 310 _InitFrom(kIBMLaptop); 311 #endif 312 fIsDefault = true; 313 } 314 315 316 void 317 KeyboardLayout::_FreeKeys() 318 { 319 free(fKeys); 320 fKeys = NULL; 321 fKeyCount = 0; 322 fKeyCapacity = 0; 323 fBounds = BRect(); 324 325 fIndicators.MakeEmpty(); 326 } 327 328 329 void 330 KeyboardLayout::_Error(const parse_state& state, const char* reason, ...) 331 { 332 va_list args; 333 va_start(args, reason); 334 335 fprintf(stderr, "Syntax error in line %ld: ", state.line); 336 vfprintf(stderr, reason, args); 337 fprintf(stderr, "\n"); 338 339 va_end(args); 340 } 341 342 343 void 344 KeyboardLayout::_AddAlternateKeyCode(Key* key, int32 modifier, int32 code) 345 { 346 if (key == NULL) 347 return; 348 349 // TODO: search for free spot 350 } 351 352 353 bool 354 KeyboardLayout::_AddKey(const Key& key) 355 { 356 TRACE(" add %ld (%g,%g)\n", key.code, key.frame.left, key.frame.top); 357 if (fKeyCount + 1 > fKeyCapacity) { 358 // enlarge array 359 int32 newCapacity = fKeyCapacity + 32; 360 Key* newKeys = (Key*)realloc(fKeys, newCapacity * sizeof(Key)); 361 if (newKeys == NULL) 362 return false; 363 364 fKeys = newKeys; 365 fKeyCapacity = newCapacity; 366 } 367 368 fKeys[fKeyCount] = key; 369 fKeyCount++; 370 371 fBounds = key.frame | fBounds; 372 373 return true; 374 } 375 376 377 void 378 KeyboardLayout::_SkipCommentsAndSpace(parse_state& state, const char*& data) 379 { 380 while (data[0] != '\0') { 381 while (isspace(data[0])) { 382 if (data[0] == '\n') 383 state.line++; 384 data++; 385 } 386 387 if (data[0] == '#') { 388 // skip comment 389 while (data[0] != '\0' && data[0] != '\n') { 390 data++; 391 } 392 } else 393 break; 394 } 395 } 396 397 398 void 399 KeyboardLayout::_Trim(BString& string, bool stripComments) 400 { 401 // Strip leading spaces 402 int32 i = 0; 403 while (isspace(string[i])) { 404 i++; 405 } 406 if (i > 0) 407 string.Remove(0, i); 408 409 // Remove comments 410 if (stripComments) { 411 i = string.FindFirst('#'); 412 if (i >= 0) 413 string.Truncate(i); 414 } 415 416 // Strip trailing spaces 417 i = string.Length() - 1; 418 while (i > 0 && isspace(string[i])) { 419 i--; 420 } 421 string.Truncate(i + 1); 422 } 423 424 425 bool 426 KeyboardLayout::_GetPair(const parse_state& state, const char*& data, 427 BString& name, BString& value) 428 { 429 // Get name 430 name = ""; 431 while (data[0] != '\0' && data[0] != '=') { 432 name += data[0]; 433 data++; 434 } 435 436 if (data[0] != '=') { 437 _Error(state, "no valid pair"); 438 return false; 439 } 440 441 // Skip sign 442 data++; 443 444 // Get value 445 value = ""; 446 while (data[0] != '\0' && data[0] != '\n') { 447 value += data[0]; 448 data++; 449 } 450 451 _Trim(name, false); 452 _Trim(value, true); 453 454 return true; 455 } 456 457 458 bool 459 KeyboardLayout::_AddKeyCodes(const parse_state& state, BPoint& rowLeftTop, 460 Key& key, const char* data, int32& lastCount) 461 { 462 if (data[0] == '-') { 463 // no key, just free space 464 int32 num = strtoul(data + 1, NULL, 0); 465 if (num < 1) 466 num = 1; 467 else if (num > 32) { 468 _Error(state, "empty key count too large"); 469 return false; 470 } 471 472 key.frame.OffsetTo(rowLeftTop); 473 rowLeftTop.x = key.frame.left + key.frame.Width() * num; 474 return true; 475 } 476 477 int32 modifier = 0; 478 479 if (isalpha(data[0])) { 480 bool led = false; 481 if (!strcmp("led-caps", data)) { 482 modifier = B_CAPS_LOCK; 483 led = true; 484 } else if (!strcmp("led-num", data)) { 485 modifier = B_NUM_LOCK; 486 led = true; 487 } else if (!strcmp("led-scroll", data)) { 488 modifier = B_SCROLL_LOCK; 489 led = true; 490 } else { 491 // TODO: get modifier (ie. "num") 492 } 493 494 if (led) { 495 key.frame.OffsetTo(rowLeftTop); 496 rowLeftTop.x = key.frame.right; 497 fBounds = key.frame | fBounds; 498 499 Indicator* indicator = new(std::nothrow) Indicator; 500 if (indicator != NULL) { 501 indicator->modifier = modifier; 502 indicator->frame = key.frame; 503 504 fIndicators.AddItem(indicator); 505 } 506 return true; 507 } 508 } 509 510 int32 first; 511 int32 last; 512 int32 num = 1; 513 514 if (data[0] == '+') { 515 num = strtoul(data + 1, NULL, 0); 516 if (num < 1) 517 num = 1; 518 else if (num > 32) { 519 _Error(state, "key count too large"); 520 return false; 521 } 522 523 if (fKeyCount > 0) 524 first = fKeys[fKeyCount - 1].code + 1; 525 else 526 first = 1; 527 528 last = first + num - 1; 529 } else { 530 char* end; 531 first = strtoul(data, &end, 0); 532 last = first; 533 534 if (end[0] == '-') { 535 last = strtoul(end + 1, NULL, 0); 536 if (first > last) { 537 _Error(state, "invalid key code specifier"); 538 return false; 539 } 540 541 num = last - first; 542 } else if (end[0] == '+') { 543 num = strtoul(end + 1, NULL, 0) + 1; 544 last = first + num - 1; 545 } else if (end[0] != '\0') { 546 _Error(state, "invalid key range"); 547 return false; 548 } 549 } 550 551 if (lastCount != 0) { 552 // update existing keys 553 if (lastCount != num) { 554 _Error(state, "modifier key mismatch"); 555 return false; 556 } 557 558 for (int32 i = fKeyCount - num; i < fKeyCount; i++, first++) { 559 Key* key = KeyAt(i); 560 561 _AddAlternateKeyCode(key, modifier, first); 562 } 563 } else { 564 // add new keys 565 for (int32 i = first; i <= last; i++) { 566 key.code = i; 567 568 // "layout" 569 key.frame.OffsetTo(rowLeftTop); 570 rowLeftTop.x = key.frame.right; 571 572 _AddKey(key); 573 } 574 575 lastCount = num; 576 } 577 578 return true; 579 } 580 581 582 bool 583 KeyboardLayout::_GetSize(const parse_state& state, const char* data, 584 float& x, float& y, float* _secondRow) 585 { 586 if (data[0] == '\0') { 587 // default size 588 x = fDefaultKeySize.width; 589 y = fDefaultKeySize.height; 590 return true; 591 } 592 593 float secondRow = 0; 594 int num = sscanf(data, "%g,%g,%g", &x, &y, &secondRow); 595 if (num < 2) { 596 _Error(state, "invalid size"); 597 return false; 598 } 599 600 if (_secondRow != NULL) 601 *_secondRow = secondRow; 602 return true; 603 } 604 605 606 bool 607 KeyboardLayout::_GetShape(const parse_state& state, const char* data, Key& key) 608 { 609 // the default 610 key.shape = kRectangleKeyShape; 611 key.dark = false; 612 613 while (isalpha(data[0])) { 614 switch (tolower(data[0])) { 615 case 'r': 616 key.shape = kRectangleKeyShape; 617 break; 618 case 'c': 619 key.shape = kCircleKeyShape; 620 break; 621 case 'l': 622 key.shape = kEnterKeyShape; 623 break; 624 case 'd': 625 key.dark = true; 626 break; 627 628 default: 629 _Error(state, "unknown shape specifier '%c'", data[0]); 630 return false; 631 } 632 633 data++; 634 } 635 636 float width, height; 637 if (!_GetSize(state, data, width, height, &key.second_row)) 638 return false; 639 640 // don't accept second row with anything but kEnterKeyShape 641 if ((key.shape != kEnterKeyShape && key.second_row != 0) 642 || (key.shape == kEnterKeyShape && key.second_row == 0)) { 643 _Error(state, "shape size mismatch"); 644 return false; 645 } 646 647 key.frame.left = 0; 648 key.frame.top = 0; 649 key.frame.right = width; 650 key.frame.bottom = height; 651 652 return true; 653 } 654 655 656 /*! Returns the term delimiter expected in a certain parse mode. */ 657 const char* 658 KeyboardLayout::_Delimiter(parse_mode mode) 659 { 660 switch (mode) { 661 default: 662 case kSize: 663 return ""; 664 case kRowStart: 665 return ";"; 666 667 case kKeyShape: 668 return ":"; 669 case kKeyCodes: 670 return ";:"; 671 } 672 } 673 674 675 bool 676 KeyboardLayout::_GetTerm(const char*& data, const char* delimiter, 677 BString& term, bool closingBracketAllowed) 678 { 679 // Get term 680 term = ""; 681 while (data[0] != '\0' && strchr(delimiter, data[0]) == NULL 682 && data[0] != '\n' && data[0] != '#' 683 && (!closingBracketAllowed || data[0] != ']')) { 684 term += data[0]; 685 data++; 686 } 687 688 if (data[0] == '\0' && delimiter[0]) 689 return false; 690 691 _Trim(term, true); 692 return true; 693 } 694 695 696 bool 697 KeyboardLayout::_SubstituteVariables(BString& term, VariableMap& variables, 698 BString& unknown) 699 { 700 while (true) { 701 int32 index = term.FindFirst('$'); 702 if (index < 0) 703 break; 704 705 // find variable name 706 707 VariableMap::iterator iterator = variables.begin(); 708 VariableMap::iterator best = variables.end(); 709 int32 bestLength = 0; 710 711 for (; iterator != variables.end(); iterator++) { 712 const BString& name = iterator->first; 713 if (!name.Compare(&term[index], name.Length()) 714 && name.Length() > bestLength) { 715 best = iterator; 716 bestLength = name.Length(); 717 } 718 } 719 720 if (best != variables.end()) { 721 // got one, replace it 722 term.Remove(index, bestLength); 723 term.Insert(best->second.String(), index); 724 } else { 725 // variable has not been found 726 unknown = &term[index]; 727 int32 length = 1; 728 while (isalpha(unknown[length])) { 729 length++; 730 } 731 unknown.Truncate(length); 732 return false; 733 } 734 } 735 736 return true; 737 } 738 739 740 bool 741 KeyboardLayout::_ParseTerm(const parse_state& state, const char*& data, 742 BString& term, VariableMap& variables) 743 { 744 if (!_GetTerm(data, _Delimiter(state.mode), term, 745 state.mode == kKeyCodes)) { 746 _Error(state, state.mode == kRowStart 747 ? "no valid row start" : "invalid term"); 748 return false; 749 } 750 751 BString unknown; 752 if (!_SubstituteVariables(term, variables, unknown)) { 753 _Error(state, "Unknown variable \"%s\"", unknown.String()); 754 return false; 755 } 756 757 return true; 758 } 759 760 761 /*! Initializes the keyboard layout from the data given. 762 The string has to be a valid keyboard layout description, otherwise 763 an error is returned. 764 */ 765 status_t 766 KeyboardLayout::_InitFrom(const char* data) 767 { 768 _FreeKeys(); 769 770 VariableMap variables; 771 BPoint rowLeftTop; 772 int32 lastKeyCount = 0; 773 Key key; 774 775 parse_state state = {kPairs, 1}; 776 777 while (data[0] != '\0') { 778 _SkipCommentsAndSpace(state, data); 779 780 if (data[0] == '[') { 781 state.mode = kRowStart; 782 783 rowLeftTop = BPoint(0, 0); 784 data++; 785 continue; 786 } else if (data[0] == '\0') 787 break; 788 789 switch (state.mode) { 790 case kPairs: 791 { 792 BString name; 793 BString value; 794 if (!_GetPair(state, data, name, value)) 795 return B_BAD_VALUE; 796 797 TRACE("<%s> = <%s>\n", name.String(), value.String()); 798 if (name == "name") 799 fName = value; 800 else if (name == "default-size") { 801 const char* valueString = value.String(); 802 parse_state tempState = {kSize, state.line}; 803 BString term; 804 if (!_ParseTerm(tempState, valueString, term, variables)) 805 return B_BAD_VALUE; 806 807 TRACE(" size = %s\n", term.String()); 808 if (!_GetSize(state, term.String(), fDefaultKeySize.width, 809 fDefaultKeySize.height)) 810 return B_BAD_VALUE; 811 } else if (name[0] == '$') 812 variables[name] = value; 813 break; 814 } 815 816 case kRowStart: 817 case kKeyShape: 818 case kKeyCodes: 819 { 820 if (data[0] == ']') { 821 if (state.mode == kKeyShape) { 822 state.mode = kPairs; 823 data++; 824 continue; 825 } 826 _Error(state, "unexpected row closing bracket"); 827 return B_BAD_VALUE; 828 } 829 830 BString term; 831 if (!_ParseTerm(state, data, term, variables)) 832 return B_BAD_VALUE; 833 834 switch (state.mode) { 835 case kRowStart: 836 if (!_GetSize(state, term.String(), rowLeftTop.x, 837 rowLeftTop.y)) 838 return B_BAD_VALUE; 839 840 TRACE("row: %s (%g:%g)\n", term.String(), rowLeftTop.x, 841 rowLeftTop.y); 842 843 state.mode = kKeyShape; 844 break; 845 case kKeyShape: 846 memset(&key, 0, sizeof(Key)); 847 if (!_GetShape(state, term.String(), key)) 848 return B_BAD_VALUE; 849 850 TRACE(" shape: %s (%g:%g:%g)\n", term.String(), 851 key.frame.Width(), key.frame.Height(), 852 key.second_row); 853 854 lastKeyCount = 0; 855 state.mode = kKeyCodes; 856 break; 857 case kKeyCodes: 858 TRACE(" raw key: %s\n", term.String()); 859 860 if (!_AddKeyCodes(state, rowLeftTop, key, term.String(), 861 lastKeyCount)) 862 return B_BAD_VALUE; 863 864 if (data[0] != ':') 865 state.mode = kKeyShape; 866 break; 867 868 default: 869 break; 870 } 871 if (data[0] != ']' && data[0] != '\0') 872 data++; 873 break; 874 } 875 876 default: 877 return B_BAD_VALUE; 878 } 879 } 880 881 return B_OK; 882 } 883 884