1 #include <X11/keysym.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "xglk.h"
5 #include "xg_internal.h"
6 #include "xg_win_textgrid.h"
7 #include "xg_win_textbuf.h"
8 
9 /* The first 256 commands are the standard character set.
10    The second 256 are special keyboard keys.
11    The third 256 are option-key combinations. */
12 #define NUMCOMMANDS (768)
13 #define NUMMACROS (256)
14 #define MACRO_MASK (0xff)
15 
16 typedef struct keymap_struct {
17   cmdentry_t *keycmds[NUMCOMMANDS];
18 } keymap_t;
19 
20 typedef struct binding_struct {
21   unsigned int key;
22   char *name;
23 } binding_t;
24 
25 #define range_Printable (1)
26 #define range_Typable (2)
27 
28 static char *macrolist[NUMMACROS];
29 static int modify_mode;
30 
31 static keymap_t *global_map = NULL;
32 static keymap_t *msg_char_map = NULL;
33 static keymap_t *msg_line_map = NULL;
34 static keymap_t *win_textgrid_map = NULL;
35 static keymap_t *win_textgrid_char_map = NULL;
36 static keymap_t *win_textgrid_line_map = NULL;
37 static keymap_t *win_textbuffer_map = NULL;
38 static keymap_t *win_textbuffer_paging_map = NULL;
39 static keymap_t *win_textbuffer_char_map = NULL;
40 static keymap_t *win_textbuffer_line_map = NULL;
41 
42 static char *xkey_get_key_name(int key);
43 static cmdentry_t *xkey_find_cmd_by_name(char *str);
44 static keymap_t *new_keymap(binding_t *bindlist);
45 static void keymap_add_multiple(keymap_t *map, int range, char *func);
46 
47 static cmdentry_t mastertable[] = {
48   {xgc_insert, -1, 0, "insert-self"},
49   {xgc_getchar, -1, 0, "getchar-self"},
50   {xgc_enter, op_Enter, 0, "enter-line"},
51 
52   {xgc_movecursor, op_ForeChar, 0, "forward-char"},
53   {xgc_movecursor, op_BackChar, 0, "backward-char"},
54   {xgc_movecursor, op_ForeWord, 0, "forward-word"},
55   {xgc_movecursor, op_BackWord, 0, "backward-word"},
56   {xgc_movecursor, op_ForeLine, 0, "forward-line"},
57   {xgc_movecursor, op_BackLine, 0, "backward-line"},
58   {xgc_movecursor, op_BeginLine, 0, "beginning-of-line"},
59   {xgc_movecursor, op_EndLine, 0, "end-of-line"},
60 
61   {xgc_scroll, op_DownPage, 0, "scroll-down"},
62   {xgc_scroll, op_UpPage, 0, "scroll-up"},
63   {xgc_scroll, op_DownLine, 0, "scroll-down-line"},
64   {xgc_scroll, op_UpLine, 0, "scroll-up-line"},
65   {xgc_scroll, op_ToBottom, 0, "scroll-to-bottom"},
66   {xgc_scroll, op_ToTop, 0, "scroll-to-top"},
67 
68   {xgc_delete, op_ForeChar, 0, "delete-next-char"},
69   {xgc_delete, op_BackChar, 0, "delete-char"},
70   {xgc_delete, op_ForeWord, 0, "delete-next-word"},
71   {xgc_delete, op_BackWord, 0, "delete-word"},
72 
73   {xgc_cutbuf, op_Yank, 0, "yank-scrap"},
74   {xgc_cutbuf, op_YankReplace, 0, "yank-scrap-replace"},
75   {xgc_cutbuf, op_Copy, 0, "copy-region"},
76   {xgc_cutbuf, op_Kill, 0, "kill-line"},
77   {xgc_cutbuf, op_Wipe, 0, "kill-region"},
78   {xgc_cutbuf, op_Erase, 0, "erase-region"},
79   {xgc_cutbuf, op_Untype, 0, "kill-input"},
80 
81   {xgc_history, op_BackLine, 0, "backward-history"},
82   {xgc_history, op_ForeLine, 0, "forward-history"},
83 
84   {xgc_focus, op_ForeWin, 0, "focus-forward"},
85   {xgc_redraw, op_AllWindows, 0, "redraw-all-windows"},
86 
87   {xgc_work_meta, op_Cancel, 1, "cancel"},
88   {xgc_work_meta, op_Meta, 0, "meta"},
89   {xgc_work_meta, op_ExplainKey, 0, "explain-key"},
90 
91   {xgc_noop, -1, 0, "no-op"},
92 
93   {NULL, 0, 0, NULL}
94 };
95 
96 #define KEYSYM(ksym) (0x100 | ((ksym) & 0xff))
97 #define META(ksym)   (0x200 | ((ksym) & 0xff))
98 
99 static binding_t global_bindings[] = {
100 
101   {'\007' /* ctrl-G */, "cancel"},
102   {META('\007') /* ctrl-G */, "cancel"},
103   {KEYSYM(XK_Escape), "meta"},
104   {KEYSYM(XK_Help), "explain-key"},
105   {META('x'), "explain-key"},
106 
107   {KEYSYM(XK_Tab), "focus-forward"},
108   {'\014' /* ctrl-L */, "redraw-all-windows"},
109 
110   {0, NULL}
111 };
112 
113 static binding_t win_textbuffer_bindings[] = {
114 
115   {'\001' /* ctrl-A */, "beginning-of-line"},
116   {'\005' /* ctrl-E */, "end-of-line"},
117   {'\002' /* ctrl-B */, "backward-char"},
118   {KEYSYM(XK_Left), "backward-char"},
119   {KEYSYM(XK_KP_Left), "backward-char"},
120   {'\006' /* ctrl-F */, "forward-char"},
121   {KEYSYM(XK_Right), "forward-char"},
122   {KEYSYM(XK_KP_Right), "forward-char"},
123   {META('f'), "forward-word"},
124   {META('b'), "backward-word"},
125 
126   {'\026' /* ctrl-V */, "scroll-down"},
127   {KEYSYM(XK_Page_Down), "scroll-down"},
128   {KEYSYM(XK_KP_Page_Down), "scroll-down"},
129   {META('v'), "scroll-up"},
130   {KEYSYM(XK_Page_Up), "scroll-up"},
131   {KEYSYM(XK_KP_Page_Up), "scroll-up"},
132 
133   {META('<'), "scroll-to-top"},
134   {META('>'), "scroll-to-bottom"},
135 
136   {META('w'), "copy-region"},
137   {'\027' /* ctrl-W */, "copy-region"},
138 
139   {0, NULL}
140 };
141 
142 static binding_t win_textgrid_bindings[] = {
143 
144   {'\001' /* ctrl-A */, "beginning-of-line"},
145   {'\005' /* ctrl-E */, "end-of-line"},
146   {'\002' /* ctrl-B */, "backward-char"},
147   {KEYSYM(XK_Left), "backward-char"},
148   {KEYSYM(XK_KP_Left), "backward-char"},
149   {'\006' /* ctrl-F */, "forward-char"},
150   {KEYSYM(XK_Right), "forward-char"},
151   {KEYSYM(XK_KP_Right), "forward-char"},
152   {META('f'), "forward-word"},
153   {META('b'), "backward-word"},
154 
155   {META('w'), "copy-region"},
156   {'\027' /* ctrl-W */, "copy-region"},
157 
158   {0, NULL}
159 };
160 
161 static binding_t win_textbuffer_char_bindings[] = {
162   {0, NULL}
163 };
164 
165 static binding_t win_textgrid_char_bindings[] = {
166   {0, NULL}
167 };
168 
169 static binding_t msg_char_bindings[] = {
170   {0, NULL}
171 };
172 
173 static binding_t win_textbuffer_line_bindings[] = {
174   {KEYSYM(XK_Down), "forward-history"},
175   {KEYSYM(XK_KP_Down), "forward-history"},
176   {'\016' /* ctrl-N */, "forward-history"},
177   {KEYSYM(XK_Up), "backward-history"},
178   {KEYSYM(XK_KP_Up), "backward-history"},
179   {'\020' /* ctrl-P */, "backward-history"},
180   {'\004' /* ctrl-D */, "delete-next-char"},
181   {KEYSYM(XK_Delete), "delete-char"},
182   {KEYSYM(XK_KP_Delete), "delete-char"},
183   {KEYSYM(XK_BackSpace), "delete-char"},
184   {'\177' /* delete */, "delete-char"},
185   {'\010' /* ctrl-H */, "delete-char"},
186   {'\n' /* newline */, "enter-line"},
187   {'\r' /* return */, "enter-line"},
188   {KEYSYM(XK_Return), "enter-line"},
189   {KEYSYM(XK_KP_Enter), "enter-line"},
190 
191   {'\013' /* ctrl-K */, "kill-line"},
192   {'\025' /* ctrl-U */, "kill-input"},
193   {'\027' /* ctrl-W */, "kill-region"},
194   {'\031' /* ctrl-Y */, "yank-scrap"},
195 
196   /*### macros */
197 
198   {0, NULL}
199 };
200 
201 static binding_t win_textgrid_line_bindings[] = {
202   {KEYSYM(XK_Down), "forward-history"},
203   {KEYSYM(XK_KP_Down), "forward-history"},
204   {'\016' /* ctrl-N */, "forward-history"},
205   {KEYSYM(XK_Up), "backward-history"},
206   {KEYSYM(XK_KP_Up), "backward-history"},
207   {'\020' /* ctrl-P */, "backward-history"},
208   {'\004' /* ctrl-D */, "delete-next-char"},
209   {KEYSYM(XK_Delete), "delete-char"},
210   {KEYSYM(XK_KP_Delete), "delete-char"},
211   {KEYSYM(XK_BackSpace), "delete-char"},
212   {'\177' /* delete */, "delete-char"},
213   {'\010' /* ctrl-H */, "delete-char"},
214   {'\n' /* newline */, "enter-line"},
215   {'\r' /* return */, "enter-line"},
216   {KEYSYM(XK_Return), "enter-line"},
217   {KEYSYM(XK_KP_Enter), "enter-line"},
218 
219   {'\013' /* ctrl-K */, "kill-line"},
220   {'\025' /* ctrl-U */, "kill-input"},
221   {'\027' /* ctrl-W */, "kill-region"},
222   {'\031' /* ctrl-Y */, "yank-scrap"},
223 
224   {0, NULL}
225 };
226 
227 static binding_t msg_line_bindings[] = {
228   {'\001' /* ctrl-A */, "beginning-of-line"},
229   {'\005' /* ctrl-E */, "end-of-line"},
230   {'\002' /* ctrl-B */, "backward-char"},
231   {KEYSYM(XK_Left), "backward-char"},
232   {KEYSYM(XK_KP_Left), "backward-char"},
233   {'\006' /* ctrl-F */, "forward-char"},
234   {KEYSYM(XK_Right), "forward-char"},
235   {KEYSYM(XK_KP_Right), "forward-char"},
236   {META('f'), "forward-word"},
237   {META('b'), "backward-word"},
238 
239   {'\004' /* ctrl-D */, "delete-next-char"},
240   {KEYSYM(XK_Delete), "delete-char"},
241   {KEYSYM(XK_BackSpace), "delete-char"},
242   {'\177' /* delete */, "delete-char"},
243   {'\010' /* ctrl-H */, "delete-char"},
244   {'\n' /* newline */, "enter-line"},
245   {'\r' /* return */, "enter-line"},
246   {KEYSYM(XK_Return), "enter-line"},
247   {KEYSYM(XK_KP_Enter), "enter-line"},
248 
249   {0, NULL}
250 };
251 
252 static binding_t win_textbuffer_paging_bindings[] = {
253   {'\n' /* newline */, "scroll-down"},
254   {'\r' /* return */, "scroll-down"},
255   {KEYSYM(XK_Return), "scroll-down"},
256   {KEYSYM(XK_KP_Enter), "scroll-down"},
257 
258   {META('<'), "scroll-to-top"},
259   {META('>'), "scroll-to-bottom"},
260 
261   {0, NULL}
262 };
263 
init_xkey()264 int init_xkey()
265 {
266   int ix;
267 
268   modify_mode = op_Cancel;
269 
270   for (ix=0; ix<NUMMACROS; ix++) {
271     macrolist[ix] = NULL;
272   }
273 
274   global_map = new_keymap(global_bindings);
275   if (!global_map)
276     return FALSE;
277 
278   win_textbuffer_map = new_keymap(win_textbuffer_bindings);
279   win_textbuffer_paging_map = new_keymap(win_textbuffer_paging_bindings);
280   win_textbuffer_char_map = new_keymap(win_textbuffer_char_bindings);
281   win_textbuffer_line_map = new_keymap(win_textbuffer_line_bindings);
282   if (!win_textbuffer_map
283     || !win_textbuffer_paging_map
284     || !win_textbuffer_char_map
285     || !win_textbuffer_line_map)
286     return FALSE;
287   keymap_add_multiple(win_textbuffer_char_map, range_Typable, "getchar-self");
288   keymap_add_multiple(win_textbuffer_paging_map, range_Printable, "scroll-down");
289   keymap_add_multiple(win_textbuffer_line_map, range_Printable, "insert-self");
290 
291   win_textgrid_map = new_keymap(win_textgrid_bindings);
292   win_textgrid_char_map = new_keymap(win_textgrid_char_bindings);
293   win_textgrid_line_map = new_keymap(win_textgrid_line_bindings);
294   if (!win_textgrid_map
295     || !win_textgrid_char_map
296     || !win_textgrid_line_map)
297     return FALSE;
298   keymap_add_multiple(win_textgrid_char_map, range_Typable, "getchar-self");
299   keymap_add_multiple(win_textgrid_line_map, range_Printable, "insert-self");
300 
301   msg_char_map = new_keymap(msg_char_bindings);
302   msg_line_map = new_keymap(msg_line_bindings);
303   if (!msg_char_map || !msg_line_map)
304     return FALSE;
305   keymap_add_multiple(msg_char_map, range_Typable, "getchar-self");
306   keymap_add_multiple(msg_line_map, range_Printable, "insert-self");
307 
308   return TRUE;
309 }
310 
xkey_get_macro(int key)311 char *xkey_get_macro(int key)
312 {
313   key &= MACRO_MASK;
314   return macrolist[key];
315 }
316 
new_keymap(binding_t * bindlist)317 static keymap_t *new_keymap(binding_t *bindlist)
318 {
319   keymap_t *res;
320   int ix;
321   int keynum;
322   cmdentry_t *cmd;
323   binding_t *bx;
324   char *cx;
325 
326   res = (keymap_t *)malloc(sizeof(keymap_t));
327   if (!res)
328     return NULL;
329 
330   for (ix=0; ix<NUMCOMMANDS; ix++) {
331     res->keycmds[ix] = NULL;
332   }
333 
334   for (bx=bindlist; bx->name; bx++) {
335     ix = (bx->key);
336     cmd = xkey_find_cmd_by_name(bx->name);
337     if (cmd) {
338       keynum = ix;
339       res->keycmds[keynum] = cmd;
340     }
341   }
342 
343   return res;
344 }
345 
keymap_add_multiple(keymap_t * map,int range,char * func)346 static void keymap_add_multiple(keymap_t *map, int range, char *func)
347 {
348   cmdentry_t *cmd;
349   int ix;
350 
351   cmd = xkey_find_cmd_by_name(func);
352 
353   if (!cmd) {
354     return;
355   }
356 
357   /* ### both of these are non-ideal */
358 
359   switch (range) {
360   case range_Printable:
361     for (ix = 32; ix <= 255; ix++) {
362       if (ix >= 127 && ix < 160)
363 	continue;
364       if (!map->keycmds[ix])
365 	map->keycmds[ix] = cmd;
366     }
367     break;
368   case range_Typable:
369     for (ix = 0; ix <= 511; ix++) {
370       if (ix == KEYSYM(XK_Tab))
371 	continue;
372       if (!map->keycmds[ix])
373 	map->keycmds[ix] = cmd;
374     }
375     break;
376   }
377 }
378 
xkey_set_macro(int key,char * str,int chown)379 void xkey_set_macro(int key, char *str, int chown)
380 {
381   char *cx;
382 
383   key &= MACRO_MASK;
384 
385   if (macrolist[key]) {
386     free(macrolist[key]);
387     macrolist[key] = NULL;
388   }
389 
390   if (str) {
391     if (chown) {
392       macrolist[key] = str;
393     }
394     else {
395       cx = (char *)malloc(strlen(str)+1);
396       strcpy(cx, str);
397       macrolist[key] = cx;
398     }
399   }
400 }
401 
402 #define TEST_KEY_MAP(mp, ky, rs)  \
403   if ((mp) && ((rs) = (mp)->keycmds[ky]))  \
404     return (rs);
405 
406 /* check keymap for a single window -- or nonwindow. */
xkey_parse_key(int key,window_t * win)407 static cmdentry_t *xkey_parse_key(int key, window_t *win)
408 {
409   cmdentry_t *res;
410 
411   if (!win) {
412     TEST_KEY_MAP(global_map, key, res);
413     if (xmsg_msgmode) {
414       if (xmsg_msgmode == xmsg_mode_Char) {
415 	TEST_KEY_MAP(msg_char_map, key, res);
416       }
417       else if (xmsg_msgmode == xmsg_mode_Line) {
418 	TEST_KEY_MAP(msg_line_map, key, res);
419       }
420     }
421   }
422   else {
423     switch (win->type) {
424     case wintype_TextGrid:
425       if (!xmsg_msgmode) {
426 	if (win->char_request) {
427 	  TEST_KEY_MAP(win_textgrid_char_map, key, res);
428 	}
429 	else if (win->line_request) {
430 	  TEST_KEY_MAP(win_textgrid_line_map, key, res);
431 	}
432       }
433       TEST_KEY_MAP(win_textgrid_map, key, res);
434       break;
435     case wintype_TextBuffer:
436       if (win_textbuffer_is_paging(win)) {
437 	TEST_KEY_MAP(win_textbuffer_paging_map, key, res);
438       }
439       if (!xmsg_msgmode) {
440 	if (win->char_request) {
441 	  TEST_KEY_MAP(win_textbuffer_char_map, key, res);
442 	}
443 	else if (win->line_request) {
444 	  TEST_KEY_MAP(win_textbuffer_line_map, key, res);
445 	}
446       }
447       TEST_KEY_MAP(win_textbuffer_map, key, res);
448       break;
449     default:
450       break;
451     }
452   }
453 
454   return NULL;
455 }
456 
xkey_get_key_name(int key)457 static char *xkey_get_key_name(int key)
458 {
459   static char buf[32];
460   KeySym ksym;
461   char *prefix, *name;
462 
463   if ((key & 0xff00) == 0x100) {
464     key &= 0xff;
465     ksym = (KeySym)((XK_Home & 0xff00) | key);
466     name = XKeysymToString(ksym);
467     if (!name)
468       name = "Unknown key";
469     strcpy(buf, name);
470     return buf;
471   }
472 
473   if (key & 0xff00) {
474     key &= 0xff;
475     prefix = "meta-";
476   }
477   else {
478     prefix = "";
479   }
480 
481   if (key < 32) {
482     sprintf(buf, "%sctrl-%c", prefix, key+'A'-1);
483   }
484   else {
485     sprintf(buf, "%s%c", prefix, key);
486   }
487 
488   return buf;
489 }
490 
xkey_find_cmd_by_name(char * str)491 static cmdentry_t *xkey_find_cmd_by_name(char *str)
492 {
493   cmdentry_t *retval;
494 
495   for (retval = mastertable; retval->func; retval++) {
496     if (!strcmp(str, retval->name))
497       return retval;
498   }
499   return NULL;
500 }
501 
xkey_perform_key(int key,unsigned int state)502 void xkey_perform_key(int key, unsigned int state)
503 {
504   cmdentry_t *command = NULL;
505   window_t *cmdwin = NULL;
506   int op;
507 
508   if (modify_mode == op_Meta) {
509     modify_mode = op_Cancel;
510     if ((key & 0xFF00) == 0) {
511       key |= 0x200;
512     }
513   }
514 
515   if (gli_focuswin) {
516     command = xkey_parse_key(key, gli_focuswin);
517     cmdwin = gli_focuswin;
518   }
519   if (!command) {
520     command = xkey_parse_key(key, NULL);
521     cmdwin = NULL;
522   }
523 
524   if (modify_mode == op_ExplainKey) {
525     char buf[128];
526     char *cx, *cxmac;
527     modify_mode = op_Cancel;
528     cx = xkey_get_key_name(key);
529     cxmac = xkey_get_macro(key);
530     if (!command)
531       sprintf(buf, "Key <%s> is not bound", cx);
532     else if (!cxmac)
533       sprintf(buf, "Key <%s>: %s", cx, command->name);
534     else {
535       if (strlen(cxmac) < sizeof(buf) - 64)
536 	sprintf(buf, "Key <%s>: %s \"%s\"", cx, command->name, cxmac);
537       else {
538 	sprintf(buf, "Key <%s>: %s \"", cx, command->name);
539 	strncat(buf, cxmac, sizeof(buf) - 64);
540 	strcat(buf, "...\"");
541       }
542     }
543     xmsg_set_message(buf, FALSE);
544     return;
545   }
546 
547   if (!command && gli_rootwin) {
548     /* look for a focuswin which knows this key */
549     window_t *win = gli_focuswin;
550     cmdentry_t *altcommand = NULL;
551     do {
552       win = gli_window_fixiterate(win);
553       if (win && win->type != wintype_Pair) {
554 	altcommand = xkey_parse_key(key, win);
555 	if (altcommand)
556 	  break;
557       }
558     } while (win != gli_focuswin);
559     if (win != gli_focuswin && altcommand) {
560       command = altcommand;
561       cmdwin = win;
562       gli_set_focus(win);
563     }
564   }
565 
566   if (command) {
567     if (command->operand == (-1)) {
568       /* Translate a key or keysym to a Glk code. */
569       if (key & 0xff00) {
570 	KeySym ksym;
571 	key &= 0xff;
572 	ksym = (KeySym)((XK_Home & 0xff00) | key);
573 	switch (ksym) {
574 	case XK_Left:
575 	case XK_KP_Left:
576 	  op = keycode_Left;
577 	  break;
578 	case XK_Right:
579 	case XK_KP_Right:
580 	  op = keycode_Right;
581 	  break;
582 	case XK_Up:
583 	case XK_KP_Up:
584 	  op = keycode_Up;
585 	  break;
586 	case XK_Down:
587 	case XK_KP_Down:
588 	  op = keycode_Down;
589 	  break;
590 	case XK_Page_Up:
591 	case XK_KP_Page_Up:
592 	  op = keycode_PageUp;
593 	  break;
594 	case XK_Page_Down:
595 	case XK_KP_Page_Down:
596 	  op = keycode_PageDown;
597 	  break;
598 	case XK_Home:
599 	case XK_KP_Home:
600 	case XK_Begin:
601 	case XK_KP_Begin:
602 	  op = keycode_Home;
603 	  break;
604 	case XK_End:
605 	case XK_KP_End:
606 	  op = keycode_End;
607 	  break;
608 	case XK_Return:
609 	case XK_KP_Enter:
610 	case XK_Linefeed:
611 	  op = keycode_Return;
612 	  break;
613 	case XK_BackSpace:
614 	case XK_Delete:
615 	case XK_KP_Delete:
616 	  op = keycode_Delete;
617 	  break;
618 	case XK_Escape:
619 	  op = keycode_Escape;
620 	  break;
621 	case XK_F1:
622 	case XK_KP_F1:
623 	  op = keycode_Func1;
624 	  break;
625 	case XK_F2:
626 	case XK_KP_F2:
627 	  op = keycode_Func2;
628 	  break;
629 	case XK_F3:
630 	case XK_KP_F3:
631 	  op = keycode_Func3;
632 	  break;
633 	case XK_F4:
634 	case XK_KP_F4:
635 	  op = keycode_Func4;
636 	  break;
637 	case XK_F5:
638 	  op = keycode_Func5;
639 	  break;
640 	case XK_F6:
641 	  op = keycode_Func6;
642 	  break;
643 	case XK_F7:
644 	  op = keycode_Func7;
645 	  break;
646 	case XK_F8:
647 	  op = keycode_Func8;
648 	  break;
649 	case XK_F9:
650 	  op = keycode_Func9;
651 	  break;
652 	case XK_F10:
653 	  op = keycode_Func10;
654 	  break;
655 	case XK_F11:
656 	  op = keycode_Func11;
657 	  break;
658 	case XK_F12:
659 	  op = keycode_Func12;
660 	  break;
661 	default:
662 	  op = keycode_Unknown;
663 	  break;
664 	}
665       }
666       else {
667 	switch (key) {
668 	case '\177':
669 	  op = keycode_Delete;
670 	  break;
671 	default:
672 	  op = key;
673 	  break;
674 	}
675       }
676     }
677     else {
678       op = command->operand;
679     }
680     (*(command->func))(cmdwin, op);
681 
682   }
683   else {
684     char buf[128];
685     char *cx;
686     cx = xkey_get_key_name(key);
687     sprintf(buf, "Key <%s> not bound", cx);
688     xmsg_set_message(buf, FALSE);
689   }
690 }
691 
xkey_guess_focus()692 void xkey_guess_focus()
693 {
694   window_t *altwin;
695 
696   if (xmsg_msgmode) {
697     gli_set_focus(NULL);
698     return;
699   }
700 
701   if (gli_focuswin
702     && (gli_focuswin->line_request || gli_focuswin->char_request)) {
703     return;
704   }
705 
706   altwin = gli_focuswin;
707   do {
708     altwin = gli_window_fixiterate(altwin);
709     if (altwin
710       && (altwin->line_request || altwin->char_request)) {
711       break;
712     }
713   } while (altwin != gli_focuswin);
714 
715   gli_set_focus(altwin);
716 }
717 
xgc_work_meta(struct glk_window_struct * dummy,int op)718 void xgc_work_meta(struct glk_window_struct *dummy, int op)
719 {
720   switch (op) {
721   case op_Cancel:
722     xmsg_set_message("Cancelled.", FALSE);
723     modify_mode = op_Cancel;
724     break;
725   case op_Meta:
726     modify_mode = op_Meta;
727     break;
728   case op_ExplainKey:
729     xmsg_set_message("Type a key to explain.", FALSE);
730     modify_mode = op_ExplainKey;
731     break;
732   /*###case op_DefineMacro:
733     xmsg_set_message("Select some text, and type a macro command key to define.",
734       FALSE);
735     modify_mode = op_DefineMacro;
736     break; ###*/
737   }
738 }
739 
740