tconfig.c - ltk - Socket-based GUI for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tconfig.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 }