config.c (21569B)
1 /* FIXME: This really is horrible. */ 2 3 #include <stdio.h> 4 #include <ctype.h> 5 #include <string.h> 6 #include <limits.h> 7 8 #include "util.h" 9 #include "keys.h" 10 #include "memory.h" 11 #include "config.h" 12 13 static int parse_keysym(char *text, size_t len, ltk_keysym *sym_ret); 14 15 ltk_config *global_config = NULL; 16 17 enum toktype { 18 STRING, 19 SECTION, 20 EQUALS, 21 NEWLINE, 22 ERROR, 23 END 24 }; 25 26 #if 0 27 static const char * 28 toktype_str(enum toktype type) { 29 switch (type) { 30 case STRING: 31 return "string"; 32 break; 33 case SECTION: 34 return "section"; 35 break; 36 case EQUALS: 37 return "equals"; 38 break; 39 case NEWLINE: 40 return "newline"; 41 break; 42 case ERROR: 43 return "error"; 44 break; 45 case END: 46 return "end of file"; 47 break; 48 default: 49 return "unknown"; 50 } 51 } 52 #endif 53 54 struct token { 55 char *text; 56 size_t len; 57 enum toktype type; 58 size_t line; /* line in original input */ 59 size_t line_offset; /* offset from start of line */ 60 }; 61 62 struct lexstate { 63 const char *filename; 64 char *text; 65 size_t len; /* length of text */ 66 size_t cur; /* current byte position */ 67 size_t cur_line; /* current line */ 68 size_t line_start; /* byte offset of start of current line */ 69 }; 70 71 static struct token 72 next_token(struct lexstate *s) { 73 char c; 74 struct token tok; 75 int finished = 0; 76 while (1) { 77 if (s->cur >= s->len) 78 return (struct token){NULL, 0, END, s->cur_line, s->cur - s->line_start + 1}; 79 while (isspace(c = s->text[s->cur])) { 80 s->cur++; 81 if (c == '\n') { 82 struct token tok = (struct token){s->text + s->cur - 1, 1, NEWLINE, s->cur_line, s->cur - s->line_start}; 83 s->cur_line++; 84 s->line_start = s->cur; 85 return tok; 86 } 87 if (s->cur >= s->len) 88 return (struct token){NULL, 0, END, s->cur_line, s->cur - s->line_start + 1}; 89 } 90 91 switch (s->text[s->cur]) { 92 case '#': 93 s->cur++; 94 while (s->cur < s->len && s->text[s->cur] != '\n') 95 s->cur++; 96 continue; 97 case '[': 98 s->cur++; 99 tok = (struct token){s->text + s->cur, 0, SECTION, s->cur_line, s->cur - s->line_start + 1}; 100 finished = 0; 101 while (s->cur < s->len) { 102 char c = s->text[s->cur]; 103 if (c == '\n') { 104 break; 105 } else if (c == ']') { 106 s->cur++; 107 finished = 1; 108 break; 109 } else { 110 tok.len++; 111 } 112 s->cur++; 113 } 114 if (!finished) { 115 tok.text = "Unfinished section name"; 116 tok.len = strlen("Unfinished section name"); 117 tok.type = ERROR; 118 } 119 break; 120 case '=': 121 tok = (struct token){s->text + s->cur, 1, EQUALS, s->cur_line, s->cur - s->line_start + 1}; 122 s->cur++; 123 break; 124 case '"': 125 /* FIXME: error if next char is not whitespace or end */ 126 s->cur++; 127 tok = (struct token){s->text + s->cur, 0, STRING, s->cur_line, s->cur - s->line_start + 1}; 128 size_t shift = 0, bs = 0; 129 finished = 0; 130 while (s->cur < s->len) { 131 char c = s->text[s->cur]; 132 if (c == '\n') { 133 break; 134 } else if (c == '\\') { 135 shift += bs; 136 tok.len += bs; 137 bs = (bs + 1) % 2; 138 } else if (c == '"') { 139 if (bs) { 140 shift++; 141 tok.len++; 142 bs = 0; 143 } else { 144 s->cur++; 145 finished = 1; 146 break; 147 } 148 } else { 149 tok.len++; 150 } 151 s->text[s->cur - shift] = s->text[s->cur]; 152 s->cur++; 153 } 154 if (!finished) { 155 tok.text = "Unfinished string"; 156 tok.len = strlen("Unfinished string"); 157 tok.type = ERROR; 158 } 159 break; 160 default: 161 tok = (struct token){s->text + s->cur, 1, STRING, s->cur_line, s->cur - s->line_start + 1}; 162 s->cur++; 163 while (s->cur < s->len) { 164 char c = s->text[s->cur]; 165 if (isspace(c) || c == '=') { 166 break; 167 } else if (c == '"') { 168 tok.text = "Unexpected start of string"; 169 tok.len = strlen("Unexpected start of string"); 170 tok.type = ERROR; 171 tok.line_offset = s->cur - s->line_start + 1; 172 } else if (c == '[' || c == ']') { 173 tok.text = "Unexpected start or end of section name"; 174 tok.len = strlen("Unexpected start or end of section name"); 175 tok.type = ERROR; 176 tok.line_offset = s->cur - s->line_start + 1; 177 } 178 tok.len++; 179 s->cur++; 180 } 181 } 182 return tok; 183 } 184 } 185 186 #undef MIN 187 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 188 189 static int 190 parse_modmask(char *modmask_str, size_t len, ltk_mod_type *mask_ret) { 191 size_t cur = 0; 192 *mask_ret = 0; 193 while (cur < len) { 194 if (str_array_equal("shift", modmask_str + cur, MIN(5, len - cur))) { 195 cur += 5; 196 *mask_ret |= LTK_MOD_SHIFT; 197 } else if (str_array_equal("ctrl", modmask_str + cur, MIN(4, len - cur))) { 198 cur += 4; 199 *mask_ret |= LTK_MOD_CTRL; 200 } else if (str_array_equal("alt", modmask_str + cur, MIN(3, len - cur))) { 201 cur += 3; 202 *mask_ret |= LTK_MOD_ALT; 203 } else if (str_array_equal("super", modmask_str + cur, MIN(5, len - cur))) { 204 cur += 5; 205 *mask_ret |= LTK_MOD_SUPER; 206 } else if (str_array_equal("any", modmask_str + cur, MIN(3, len - cur))) { 207 cur += 3; 208 *mask_ret = UINT_MAX; 209 } else { 210 return 1; 211 } 212 if (cur < len && modmask_str[cur] != '|') 213 return 1; 214 else 215 cur++; 216 } 217 return 0; 218 } 219 220 static int 221 parse_flags(char *text, size_t len, ltk_key_binding_flags *flags_ret) { 222 if (str_array_equal("run-always", text, len)) 223 *flags_ret = LTK_KEY_BINDING_RUN_ALWAYS; 224 else 225 return 1; 226 return 0; 227 } 228 229 static int 230 parse_keypress_binding( 231 struct lexstate *s, struct token *tok, 232 ltk_keypress_binding *binding_ret, 233 char **func_name_ret, size_t *func_len_ret, 234 char **errstr) { 235 ltk_keypress_binding b = {NULL, NULL, LTK_KEY_NONE, LTK_MOD_NONE, LTK_KEY_BINDING_NOFLAGS}; 236 *tok = next_token(s); 237 char *msg = NULL; 238 if (tok->type != STRING) { 239 msg = "Invalid token type"; 240 goto error; 241 } 242 *func_name_ret = tok->text; 243 *func_len_ret = tok->len; 244 struct token prevtok; 245 int text_init = 0, rawtext_init = 0, sym_init = 0, mods_init = 0, flags_init = 0; 246 while (1) { 247 *tok = next_token(s); 248 if (tok->type == NEWLINE || tok->type == END) 249 break; 250 prevtok = *tok; 251 *tok = next_token(s); 252 if (prevtok.type != STRING) { 253 msg = "Invalid token type"; 254 *tok = prevtok; 255 goto error; 256 } else if (tok->type != STRING) { 257 msg = "Invalid token type"; 258 goto error; 259 } else if (str_array_equal("text", prevtok.text, prevtok.len)) { 260 if (rawtext_init) { 261 msg = "Rawtext already specified"; 262 goto error; 263 } else if (sym_init) { 264 msg = "Keysym already specified"; 265 goto error; 266 } else if (text_init) { 267 msg = "Duplicate text specification"; 268 goto error; 269 } 270 b.text = ltk_strndup(tok->text, tok->len); 271 text_init = 1; 272 } else if (str_array_equal("rawtext", prevtok.text, prevtok.len)) { 273 if (text_init) { 274 msg = "Text already specified"; 275 goto error; 276 } else if (sym_init) { 277 msg = "Keysym already specified"; 278 goto error; 279 } else if (rawtext_init) { 280 msg = "Duplicate rawtext specification"; 281 goto error; 282 } 283 b.rawtext = ltk_strndup(tok->text, tok->len); 284 rawtext_init = 1; 285 } else if (str_array_equal("sym", prevtok.text, prevtok.len)) { 286 if (text_init) { 287 msg = "Text already specified"; 288 goto error; 289 } else if (rawtext_init) { 290 msg = "Rawtext already specified"; 291 goto error; 292 } else if (sym_init) { 293 msg = "Duplicate keysym specification"; 294 goto error; 295 } else if (parse_keysym(tok->text, tok->len, &b.sym)) { 296 msg = "Invalid keysym specification"; 297 goto error; 298 } 299 sym_init = 1; 300 } else if (str_array_equal("mods", prevtok.text, prevtok.len)) { 301 if (mods_init) { 302 msg = "Duplicate mods specification"; 303 goto error; 304 } else if (parse_modmask(tok->text, tok->len, &b.mods)) { 305 msg = "Invalid mods specification"; 306 goto error; 307 } 308 mods_init = 1; 309 } else if (str_array_equal("flags", prevtok.text, prevtok.len)) { 310 if (flags_init) { 311 msg = "Duplicate flags specification"; 312 goto error; 313 } else if (parse_flags(tok->text, tok->len, &b.flags)) { 314 msg = "Invalid flags specification"; 315 goto error; 316 } 317 flags_init = 1; 318 } else { 319 msg = "Invalid keyword"; 320 *tok = prevtok; 321 goto error; 322 } 323 }; 324 if (!text_init && !rawtext_init && !sym_init) { 325 msg = "One of text, rawtext, and sym must be initialized"; 326 goto error; 327 } 328 *binding_ret = b; 329 return 0; 330 error: 331 free(b.text); 332 free(b.rawtext); 333 if (msg) { 334 *errstr = ltk_print_fmt( 335 "%s, line %zu, offset %zu: %s", s->filename, tok->line, tok->line_offset, msg 336 ); 337 } else { 338 *errstr = NULL; 339 } 340 return 1; 341 } 342 343 static int 344 parse_keybinding( 345 struct lexstate *s, 346 struct token *tok, 347 char *widget, 348 size_t len, 349 keypress_binding_handler press_handler, 350 keyrelease_binding_handler release_handler, 351 char **errstr) { 352 char *msg = NULL; 353 *tok = next_token(s); 354 if (tok->type == SECTION || tok->type == END) { 355 return -1; 356 } else if (tok->type == NEWLINE) { 357 return 0; /* empty line */ 358 } else if (tok->type != STRING) { 359 msg = "Invalid token type"; 360 goto error; 361 } else if (str_array_equal("bind-keypress", tok->text, tok->len)) { 362 ltk_keypress_binding b; 363 char *name; 364 size_t nlen; 365 if (parse_keypress_binding(s, tok, &b, &name, &nlen, errstr)) 366 return 1; 367 if (press_handler(widget, len, name, nlen, b)) { 368 msg = "Invalid key binding"; 369 goto error; 370 } 371 } else if (str_array_equal("bind-keyrelease", tok->text, tok->len)) { 372 ltk_keypress_binding b; 373 char *name; 374 size_t nlen; 375 if (parse_keypress_binding(s, tok, &b, &name, &nlen, errstr)) 376 return 1; 377 if (b.text || b.rawtext) { 378 free(b.text); 379 free(b.rawtext); 380 msg = "Text and rawtext may only be specified for keypress bindings"; 381 goto error; 382 } 383 if (release_handler(widget, len, name, nlen, (ltk_keyrelease_binding){b.sym, b.mods, b.flags})) { 384 msg = "Invalid key binding"; 385 goto error; 386 } 387 } else { 388 msg = "Invalid statement"; 389 goto error; 390 } 391 return 0; 392 error: 393 if (msg) { 394 *errstr = ltk_print_fmt( 395 "%s, line %zu, offset %zu: %s", s->filename, tok->line, tok->line_offset, msg 396 ); 397 } 398 return 1; 399 } 400 401 static void 402 push_lang_mapping(ltk_config *c) { 403 if (c->mappings_alloc == c->mappings_len) { 404 c->mappings_alloc = ideal_array_size(c->mappings_alloc, c->mappings_len + 1); 405 c->mappings = ltk_reallocarray(c->mappings, c->mappings_alloc, sizeof(ltk_language_mapping)); 406 } 407 c->mappings[c->mappings_len].lang = NULL; 408 c->mappings[c->mappings_len].mappings = NULL; 409 c->mappings[c->mappings_len].mappings_alloc = 0; 410 c->mappings[c->mappings_len].mappings_len = 0; 411 c->mappings_len++; 412 } 413 414 static void 415 push_text_mapping(ltk_config *c, char *text1, size_t len1, char *text2, size_t len2) { 416 if (c->mappings_len == 0) 417 return; /* I guess just fail silently... */ 418 ltk_language_mapping *m = &c->mappings[c->mappings_len - 1]; 419 if (m->mappings_alloc == m->mappings_len) { 420 m->mappings_alloc = ideal_array_size(m->mappings_alloc, m->mappings_len + 1); 421 m->mappings = ltk_reallocarray(m->mappings, m->mappings_alloc, sizeof(ltk_keytext_mapping)); 422 } 423 m->mappings[m->mappings_len].from = ltk_strndup(text1, len1); 424 m->mappings[m->mappings_len].to = ltk_strndup(text2, len2); 425 m->mappings_len++; 426 } 427 428 static void 429 destroy_config(ltk_config *c) { 430 for (size_t i = 0; i < c->mappings_len; i++) { 431 ltk_free(c->mappings[i].lang); 432 for (size_t j = 0; j < c->mappings[i].mappings_len; j++) { 433 ltk_free(c->mappings[i].mappings[j].from); 434 ltk_free(c->mappings[i].mappings[j].to); 435 } 436 ltk_free(c->mappings[i].mappings); 437 } 438 ltk_free(c->general.line_editor); 439 ltk_free(c->mappings); 440 ltk_free(c); 441 } 442 443 void 444 ltk_config_cleanup(void) { 445 if (global_config) 446 destroy_config(global_config); 447 global_config = NULL; 448 } 449 450 ltk_config * 451 ltk_config_get(void) { 452 return global_config; 453 } 454 455 int 456 ltk_config_get_language_index(char *lang, size_t *idx_ret) { 457 if (!global_config) 458 return 1; 459 for (size_t i = 0; i < global_config->mappings_len; i++) { 460 if (!strcmp(lang, global_config->mappings[i].lang)) { 461 *idx_ret = i; 462 return 0; 463 } 464 } 465 return 1; 466 } 467 468 ltk_language_mapping * 469 ltk_config_get_language_mapping(size_t idx) { 470 if (!global_config || idx >= global_config->mappings_len) 471 return NULL; 472 return &global_config->mappings[idx]; 473 } 474 475 int 476 str_array_prefix(const char *str, const char *ar, size_t len) { 477 size_t slen = strlen(str); 478 if (len < slen) 479 return 0; 480 return !strncmp(str, ar, slen); 481 } 482 483 /* WARNING: errstr must be freed! */ 484 /* FIXME: make ltk_load_file give size_t; handle errors there (copy from ledit) */ 485 static int 486 load_from_text( 487 const char *filename, 488 char *file_contents, 489 size_t len, 490 keypress_binding_handler press_handler, 491 keyrelease_binding_handler release_handler, 492 char **errstr) { 493 ltk_config *config = ltk_malloc(sizeof(ltk_config)); 494 config->mappings = NULL; 495 config->mappings_alloc = config->mappings_len = 0; 496 config->general.explicit_focus = 0; 497 config->general.all_activatable = 0; 498 config->general.line_editor = NULL; 499 500 struct lexstate s = {filename, file_contents, len, 0, 1, 0}; 501 struct token tok = next_token(&s); 502 int start_of_line = 1; 503 char *msg = NULL; 504 struct token secttok; 505 while (tok.type != END) { 506 switch (tok.type) { 507 case SECTION: 508 if (!start_of_line) { 509 msg = "Section can only start at new line"; 510 goto error; 511 } 512 secttok = tok; 513 tok = next_token(&s); 514 if (tok.type != NEWLINE && tok.type != END) { 515 msg = "Section must be alone on line"; 516 goto error; 517 } 518 /* FIXME: generalize (at least once more options are added) */ 519 if (str_array_equal("general", secttok.text, secttok.len)) { 520 struct token prev1tok, prev2tok; 521 while (1) { 522 tok = next_token(&s); 523 if (tok.type == SECTION || tok.type == END) 524 break; 525 else if (tok.type == NEWLINE) 526 continue; 527 prev2tok = tok; 528 tok = next_token(&s); 529 prev1tok = tok; 530 tok = next_token(&s); 531 if (prev2tok.type != STRING || prev1tok.type != EQUALS || tok.type != STRING) { 532 msg = "Invalid assignment statement"; 533 goto error; 534 } 535 if (str_array_equal("explicit-focus", prev2tok.text, prev2tok.len)) { 536 if (str_array_equal("true", tok.text, tok.len)) { 537 config->general.explicit_focus = 1; 538 } else if (str_array_equal("false", tok.text, tok.len)) { 539 config->general.explicit_focus = 0; 540 } else { 541 msg = "Invalid boolean setting"; 542 goto error; 543 } 544 } else if (str_array_equal("all-activatable", prev2tok.text, prev2tok.len)) { 545 if (str_array_equal("true", tok.text, tok.len)) { 546 config->general.all_activatable = 1; 547 } else if (str_array_equal("false", tok.text, tok.len)) { 548 config->general.all_activatable = 0; 549 } else { 550 msg = "Invalid boolean setting"; 551 goto error; 552 } 553 } else if (str_array_equal("line-editor", prev2tok.text, prev2tok.len)) { 554 config->general.line_editor = ltk_strndup(tok.text, tok.len); 555 } else { 556 msg = "Invalid setting"; 557 goto error; 558 } 559 tok = next_token(&s); 560 if (tok.type == END) { 561 break; 562 } else if (tok.type != NEWLINE) { 563 msg = "Invalid assignment statement"; 564 goto error; 565 } 566 start_of_line = 1; 567 } 568 } else if (str_array_prefix("key-binding:", secttok.text, secttok.len)) { 569 int ret = 0; 570 char *widget = secttok.text + strlen("key-binding:"); 571 size_t len = secttok.len - strlen("key-binding:"); 572 while (1) { 573 if ((ret = parse_keybinding(&s, &tok, widget, len, press_handler, release_handler, errstr)) > 0) { 574 goto errornomsg; 575 } else if (ret < 0) { 576 start_of_line = 1; 577 break; 578 } 579 } 580 } else if (str_array_equal("key-mapping", secttok.text, secttok.len)) { 581 int lang_init = 0; 582 push_lang_mapping(config); 583 struct token prev1tok, prev2tok; 584 while (1) { 585 tok = next_token(&s); 586 if (tok.type == SECTION || tok.type == END) 587 break; 588 else if (tok.type == NEWLINE) 589 continue; 590 prev2tok = tok; 591 tok = next_token(&s); 592 prev1tok = tok; 593 tok = next_token(&s); 594 if (prev2tok.type != STRING) { 595 msg = "Invalid statement in language mapping"; 596 goto error; 597 } 598 if (str_array_equal("language", prev2tok.text, prev2tok.len)) { 599 if (prev1tok.type != EQUALS || tok.type != STRING) { 600 msg = "Invalid language assignment"; 601 goto error; 602 } else if (lang_init) { 603 msg = "Language already set"; 604 goto error; 605 } 606 config->mappings[config->mappings_len - 1].lang = ltk_strndup(tok.text, tok.len); 607 lang_init = 1; 608 } else if (str_array_equal("map", prev2tok.text, prev2tok.len)) { 609 if (prev1tok.type != STRING || tok.type != STRING) { 610 msg = "Invalid map statement"; 611 goto error; 612 } 613 push_text_mapping(config, prev1tok.text, prev1tok.len, tok.text, tok.len); 614 } else { 615 msg = "Invalid statement in language mapping"; 616 goto error; 617 } 618 tok = next_token(&s); 619 if (tok.type == END) { 620 break; 621 } else if (tok.type != NEWLINE) { 622 msg = "Invalid statement in language mapping"; 623 goto error; 624 } 625 start_of_line = 1; 626 } 627 if (!lang_init) { 628 msg = "Language not set for language mapping"; 629 goto error; 630 } 631 } else { 632 msg = "Invalid section"; 633 goto error; 634 } 635 break; 636 case NEWLINE: 637 start_of_line = 1; 638 break; 639 default: 640 msg = "Invalid token"; 641 goto error; 642 break; 643 } 644 } 645 global_config = config; 646 return 0; 647 error: 648 if (msg) { 649 *errstr = ltk_print_fmt( 650 "%s, line %zu, offset %zu: %s", filename, tok.line, tok.line_offset, msg 651 ); 652 } 653 errornomsg: 654 destroy_config(config); 655 return 1; 656 } 657 658 int 659 ltk_config_parsefile( 660 const char *filename, 661 keypress_binding_handler press_handler, 662 keyrelease_binding_handler release_handler, 663 char **errstr) { 664 unsigned long len = 0; 665 char *ferrstr = NULL; 666 char *file_contents = ltk_read_file(filename, &len, &ferrstr); 667 if (!file_contents) { 668 *errstr = ltk_print_fmt("Unable to open file \"%s\": %s", filename, ferrstr); 669 return 1; 670 } 671 int ret = load_from_text(filename, file_contents, len, press_handler, release_handler, errstr); 672 ltk_free(file_contents); 673 return ret; 674 } 675 676 /* FIXME: update this */ 677 const char *default_config = "[general]\n" 678 "explicit-focus = true\n" 679 "all-activatable = true\n" 680 "[key-binding:widget]\n" 681 "bind-keypress move-next sym tab\n" 682 "bind-keypress move-prev sym tab mods shift\n" 683 "bind-keypress move-next text n\n" 684 "bind-keypress move-prev text p\n" 685 "bind-keypress focus-active sym return\n" 686 "bind-keypress unfocus-active sym escape\n" 687 "bind-keypress set-pressed sym return flags run-always\n" 688 "bind-keyrelease unset-pressed sym return flags run-always\n" 689 "[key-mapping]\n" 690 "language = \"English (US)\"\n"; 691 692 /* FIXME: improve this configuration */ 693 int 694 ltk_config_load_default( 695 keypress_binding_handler press_handler, 696 keyrelease_binding_handler release_handler, 697 char **errstr) { 698 char *config_copied = ltk_strdup(default_config); 699 int ret = load_from_text("<default config>", config_copied, strlen(config_copied), press_handler, release_handler, errstr); 700 free(config_copied); 701 return ret; 702 } 703 704 void 705 ltk_keypress_binding_destroy(ltk_keypress_binding b) { 706 ltk_free(b.text); 707 ltk_free(b.rawtext); 708 } 709 710 /* FIXME: which additional ones are needed here? */ 711 static struct keysym_mapping { 712 char *name; 713 ltk_keysym keysym; 714 } keysym_map[] = { 715 {"backspace", LTK_KEY_BACKSPACE}, 716 {"begin", LTK_KEY_BEGIN}, 717 {"break", LTK_KEY_BREAK}, 718 {"cancel", LTK_KEY_CANCEL}, 719 {"clear", LTK_KEY_CLEAR}, 720 {"delete", LTK_KEY_DELETE}, 721 {"down", LTK_KEY_DOWN}, 722 {"end", LTK_KEY_END}, 723 {"escape", LTK_KEY_ESCAPE}, 724 {"execute", LTK_KEY_EXECUTE}, 725 726 {"f1", LTK_KEY_F1}, 727 {"f10", LTK_KEY_F10}, 728 {"f11", LTK_KEY_F11}, 729 {"f12", LTK_KEY_F12}, 730 {"f2", LTK_KEY_F2}, 731 {"f3", LTK_KEY_F3}, 732 {"f4", LTK_KEY_F4}, 733 {"f5", LTK_KEY_F5}, 734 {"f6", LTK_KEY_F6}, 735 {"f7", LTK_KEY_F7}, 736 {"f8", LTK_KEY_F8}, 737 {"f9", LTK_KEY_F9}, 738 739 {"find", LTK_KEY_FIND}, 740 {"help", LTK_KEY_HELP}, 741 {"home", LTK_KEY_HOME}, 742 {"insert", LTK_KEY_INSERT}, 743 744 {"kp-0", LTK_KEY_KP_0}, 745 {"kp-1", LTK_KEY_KP_1}, 746 {"kp-2", LTK_KEY_KP_2}, 747 {"kp-3", LTK_KEY_KP_3}, 748 {"kp-4", LTK_KEY_KP_4}, 749 {"kp-5", LTK_KEY_KP_5}, 750 {"kp-6", LTK_KEY_KP_6}, 751 {"kp-7", LTK_KEY_KP_7}, 752 {"kp-8", LTK_KEY_KP_8}, 753 {"kp-9", LTK_KEY_KP_9}, 754 {"kp-add", LTK_KEY_KP_ADD}, 755 {"kp-begin", LTK_KEY_KP_BEGIN}, 756 {"kp-decimal", LTK_KEY_KP_DECIMAL}, 757 {"kp-delete", LTK_KEY_KP_DELETE}, 758 {"kp-divide", LTK_KEY_KP_DIVIDE}, 759 {"kp-down", LTK_KEY_KP_DOWN}, 760 {"kp-end", LTK_KEY_KP_END}, 761 {"kp-enter", LTK_KEY_KP_ENTER}, 762 {"kp-equal", LTK_KEY_KP_EQUAL}, 763 {"kp-home", LTK_KEY_KP_HOME}, 764 {"kp-insert", LTK_KEY_KP_INSERT}, 765 {"kp-left", LTK_KEY_KP_LEFT}, 766 {"kp-multiply", LTK_KEY_KP_MULTIPLY}, 767 {"kp-next", LTK_KEY_KP_NEXT}, 768 {"kp-page-down", LTK_KEY_KP_PAGE_DOWN}, 769 {"kp-page-up", LTK_KEY_KP_PAGE_UP}, 770 {"kp-prior", LTK_KEY_KP_PRIOR}, 771 {"kp-right", LTK_KEY_KP_RIGHT}, 772 {"kp-separator", LTK_KEY_KP_SEPARATOR}, 773 {"kp-space", LTK_KEY_KP_SPACE}, 774 {"kp-subtract", LTK_KEY_KP_SUBTRACT}, 775 {"kp-tab", LTK_KEY_KP_TAB}, 776 {"kp-up", LTK_KEY_KP_UP}, 777 778 {"left", LTK_KEY_LEFT}, 779 {"linefeed", LTK_KEY_LINEFEED}, 780 {"menu", LTK_KEY_MENU}, 781 {"mode-switch", LTK_KEY_MODE_SWITCH}, 782 {"next", LTK_KEY_NEXT}, 783 {"num-lock", LTK_KEY_NUM_LOCK}, 784 {"page-down", LTK_KEY_PAGE_DOWN}, 785 {"page-up", LTK_KEY_PAGE_UP}, 786 {"pause", LTK_KEY_PAUSE}, 787 {"print", LTK_KEY_PRINT}, 788 {"prior", LTK_KEY_PRIOR}, 789 790 {"redo", LTK_KEY_REDO}, 791 {"return", LTK_KEY_RETURN}, 792 {"right", LTK_KEY_RIGHT}, 793 {"script-switch", LTK_KEY_SCRIPT_SWITCH}, 794 {"scroll-lock", LTK_KEY_SCROLL_LOCK}, 795 {"select", LTK_KEY_SELECT}, 796 {"space", LTK_KEY_SPACE}, 797 {"sysreq", LTK_KEY_SYS_REQ}, 798 {"tab", LTK_KEY_TAB}, 799 {"up", LTK_KEY_UP}, 800 {"undo", LTK_KEY_UNDO}, 801 }; 802 803 GEN_CB_MAP_HELPERS(keysym_map, struct keysym_mapping, name) 804 805 static int 806 parse_keysym(char *keysym_str, size_t len, ltk_keysym *sym) { 807 struct keysym_mapping *km = keysym_map_get_entry(keysym_str, len); 808 if (!km) 809 return 1; 810 *sym = km->keysym; 811 return 0; 812 }