1 /*
2 * Copyright (c) 1993-2009, 2013-2020 Paul Mattes.
3 * Copyright (c) 1990, Jeff Sparkes.
4 * Copyright (c) 1989, Georgia Tech Research Corporation (GTRC), Atlanta, GA
5 * 30332.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the names of Paul Mattes, Jeff Sparkes, GTRC nor the names of
16 * their contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES, JEFF SPARKES AND GTRC "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL PAUL MATTES, JEFF SPARKES OR GTRC BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * xkybd.c
34 * Xt-specific keyboard functions.
35 */
36
37 #include "globals.h"
38 #include "xglobals.h"
39
40 #include <X11/Xatom.h>
41
42 #define XK_3270
43 #define XK_APL
44 #include <X11/keysym.h>
45
46 #include "resources.h"
47
48 #include "actions.h"
49 #include "apl.h"
50 #include "idle.h"
51 #include "keymap.h"
52 #include "keysym2ucs.h"
53 #include "kybd.h"
54 #include "names.h"
55 #include "popups.h"
56 #include "screen.h"
57 #include "selectc.h"
58 #include "task.h"
59 #include "toggles.h"
60 #include "trace.h"
61 #include "unicodec.h"
62 #include "xactions.h"
63 #include "xscreen.h"
64 #include "xselectc.h"
65
66 /*
67 * Handle an ordinary character key, given its NULL-terminated multibyte
68 * representation.
69 */
70 static void
key_ACharacter(char * mb,enum keytype keytype,enum iaction cause)71 key_ACharacter(char *mb, enum keytype keytype, enum iaction cause)
72 {
73 ucs4_t ucs4;
74 int consumed;
75 enum me_fail error;
76
77 reset_idle_timer();
78
79 /* Convert the multibyte string to UCS4. */
80 ucs4 = multibyte_to_unicode(mb, strlen(mb), &consumed, &error);
81 if (ucs4 == 0) {
82 vtrace(" %s -> Key(?)\n", ia_name[(int) cause]);
83 vtrace(" dropped (invalid multibyte sequence)\n");
84 return;
85 }
86
87 key_UCharacter(ucs4, keytype, cause, false);
88 }
89
90 static bool
AltCursor_action(ia_t ia,unsigned argc,const char ** argv)91 AltCursor_action(ia_t ia, unsigned argc, const char **argv)
92 {
93 action_debug(AnAltCursor, ia, argc, argv);
94 if (check_argc(AnAltCursor, argc, 0, 0) < 0) {
95 return false;
96 }
97 reset_idle_timer();
98 do_toggle(ALT_CURSOR);
99 return true;
100 }
101
102 /*
103 * Cursor Select mouse action (light pen simulator).
104 */
105 void
MouseSelect_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)106 MouseSelect_xaction(Widget w, XEvent *event, String *params,
107 Cardinal *num_params)
108 {
109 xaction_debug(MouseSelect_xaction, event, params, num_params);
110 if (xcheck_usage(MouseSelect_xaction, *num_params, 0, 0) < 0) {
111 return;
112 }
113 if (w != *screen) {
114 return;
115 }
116 reset_idle_timer();
117 if (kybdlock) {
118 return;
119 }
120 if (IN_NVT) {
121 return;
122 }
123 lightpen_select(mouse_baddr(w, event));
124 }
125
126 /*
127 * MoveCursor Xt action. Depending on arguments, this is either a move to the
128 * mouse cursor position, or to an absolute location.
129 */
130 void
MoveCursor_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)131 MoveCursor_xaction(Widget w, XEvent *event, String *params,
132 Cardinal *num_params)
133 {
134 xaction_debug(MoveCursor_xaction, event, params, num_params);
135
136 /* With arguments, this isn't a mouse call. */
137 if (*num_params != 0) {
138 if (*num_params > 2) {
139 popup_an_error(AnMoveCursor "() takes 0, 1 or 2 arguments");
140 return;
141 }
142 run_action(AnMoveCursor, IA_KEYMAP, params[0], params[1]);
143 return;
144 }
145
146 /* If it is a mouse call, it only applies to the screen. */
147 if (w != *screen) {
148 return;
149 }
150
151 /* If the screen is locked, do nothing. */
152 if (kybdlock) {
153 return;
154 }
155
156 /* Move the cursor to where the mouse is. */
157 reset_idle_timer();
158 cursor_move(mouse_baddr(w, event));
159 }
160
161 /*
162 * Run a KeyPress through XIM.
163 * Returns true if there is further processing to do, false otherwise.
164 */
165 static bool
xim_lookup(XKeyEvent * event)166 xim_lookup(XKeyEvent *event)
167 {
168 static char *buf = NULL;
169 static int buf_len = 0, rlen;
170 KeySym k;
171 Status status;
172 int i;
173 bool rv = false;
174 #define BASE_BUFSIZE 50
175
176 if (ic == NULL) {
177 return true;
178 }
179
180 if (buf == NULL) {
181 buf_len = BASE_BUFSIZE;
182 buf = Malloc(buf_len);
183 }
184
185 for (;;) {
186 memset(buf, '\0', buf_len);
187 rlen = XmbLookupString(ic, event, buf, buf_len - 1, &k, &status);
188 if (status != XBufferOverflow) {
189 break;
190 }
191 buf_len += BASE_BUFSIZE;
192 buf = Realloc(buf, buf_len);
193 }
194
195 switch (status) {
196 case XLookupNone:
197 rv = false;
198 break;
199 case XLookupKeySym:
200 rv = true;
201 break;
202 case XLookupChars:
203 vtrace("%d XIM char%s:", rlen, (rlen != 1)? "s": "");
204 for (i = 0; i < rlen; i++) {
205 vtrace(" %02x", buf[i] & 0xff);
206 }
207 vtrace("\n");
208 buf[rlen] = '\0';
209 key_ACharacter(buf, KT_STD, ia_cause);
210 rv = false;
211 break;
212 case XLookupBoth:
213 rv = true;
214 break;
215 }
216 return rv;
217 }
218
219 /*
220 * X-dependent code starts here.
221 */
222
223 /*
224 * Translate a keymap (from an XQueryKeymap or a KeymapNotify event) into
225 * a bitmap of Shift, Meta or Alt keys pressed.
226 */
227 #define key_is_down(kc, bitmap) (kc && ((bitmap)[(kc)/8] & (1<<((kc)%8))))
228 int
state_from_keymap(char keymap[32])229 state_from_keymap(char keymap[32])
230 {
231 static bool initted = false;
232 static KeyCode kc_Shift_L, kc_Shift_R;
233 static KeyCode kc_Meta_L, kc_Meta_R;
234 static KeyCode kc_Alt_L, kc_Alt_R;
235 int pseudo_state = 0;
236
237 if (!initted) {
238 kc_Shift_L = XKeysymToKeycode(display, XK_Shift_L);
239 kc_Shift_R = XKeysymToKeycode(display, XK_Shift_R);
240 kc_Meta_L = XKeysymToKeycode(display, XK_Meta_L);
241 kc_Meta_R = XKeysymToKeycode(display, XK_Meta_R);
242 kc_Alt_L = XKeysymToKeycode(display, XK_Alt_L);
243 kc_Alt_R = XKeysymToKeycode(display, XK_Alt_R);
244 initted = true;
245 }
246 if (key_is_down(kc_Shift_L, keymap) || key_is_down(kc_Shift_R, keymap)) {
247 pseudo_state |= ShiftKeyDown;
248 }
249 if (key_is_down(kc_Meta_L, keymap) || key_is_down(kc_Meta_R, keymap)) {
250 pseudo_state |= MetaKeyDown;
251 }
252 if (key_is_down(kc_Alt_L, keymap) || key_is_down(kc_Alt_R, keymap)) {
253 pseudo_state |= AltKeyDown;
254 }
255 return pseudo_state;
256 }
257 #undef key_is_down
258
259 /*
260 * Process shift keyboard events. The code has to look for the raw Shift keys,
261 * rather than using the handy "state" field in the event structure. This is
262 * because the event state is the state _before_ the key was pressed or
263 * released. This isn't enough information to distinguish between "left
264 * shift released" and "left shift released, right shift still held down"
265 * events, for example.
266 *
267 * This function is also called as part of Focus event processing.
268 */
269 void
PA_Shift_xaction(Widget w _is_unused,XEvent * event _is_unused,String * params _is_unused,Cardinal * num_params _is_unused)270 PA_Shift_xaction(Widget w _is_unused, XEvent *event _is_unused,
271 String *params _is_unused, Cardinal *num_params _is_unused)
272 {
273 char keys[32];
274
275 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
276 xaction_debug(PA_Shift_xaction, event, params, num_params);
277 #endif /*]*/
278 XQueryKeymap(display, keys);
279 shift_event(state_from_keymap(keys));
280 }
281
282 /*
283 * Called by the toolkit for any key without special actions.
284 */
285 void
Default_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)286 Default_xaction(Widget w _is_unused, XEvent *event, String *params,
287 Cardinal *num_params)
288 {
289 XKeyEvent *kevent = (XKeyEvent *)event;
290 char buf[32];
291 KeySym ks;
292 int ll;
293
294 xaction_debug(Default_xaction, event, params, num_params);
295 if (xcheck_usage(Default_xaction, *num_params, 0, 0) < 0) {
296 return;
297 }
298 switch (event->type) {
299 case KeyPress:
300 if (!xim_lookup((XKeyEvent *)event)) {
301 return;
302 }
303 ll = XLookupString(kevent, buf, 32, &ks, NULL);
304 buf[ll] = '\0';
305 if (ll > 1) {
306 key_ACharacter(buf, KT_STD, IA_DEFAULT);
307 return;
308 }
309 if (ll == 1) {
310 /* Remap certain control characters. */
311 if (!IN_NVT) switch (buf[0]) {
312 case '\t':
313 run_action(AnTab, IA_DEFAULT, NULL, NULL);
314 break;
315 case '\177':
316 run_action(AnDelete, IA_DEFAULT, NULL, NULL);
317 break;
318 case '\b':
319 run_action(AnErase, IA_DEFAULT, NULL, NULL);
320 break;
321 case '\r':
322 run_action(AnEnter, IA_DEFAULT, NULL, NULL);
323 break;
324 case '\n':
325 run_action(AnNewline, IA_DEFAULT, NULL, NULL);
326 break;
327 default:
328 key_ACharacter(buf, KT_STD, IA_DEFAULT);
329 break;
330 } else {
331 key_ACharacter(buf, KT_STD, IA_DEFAULT);
332 }
333 return;
334 }
335
336 /* Pick some other reasonable defaults. */
337 switch (ks) {
338 case XK_Up:
339 run_action(AnUp, IA_DEFAULT, NULL, NULL);
340 break;
341 case XK_Down:
342 run_action(AnDown, IA_DEFAULT, NULL, NULL);
343 break;
344 case XK_Left:
345 run_action(AnLeft, IA_DEFAULT, NULL, NULL);
346 break;
347 case XK_Right:
348 run_action(AnRight, IA_DEFAULT, NULL, NULL);
349 break;
350 case XK_Insert:
351 #if defined(XK_KP_Insert) /*[*/
352 case XK_KP_Insert:
353 #endif /*]*/
354 run_action(AnInsert, IA_DEFAULT, NULL, NULL);
355 break;
356 case XK_Delete:
357 run_action(AnDelete, IA_DEFAULT, NULL, NULL);
358 break;
359 case XK_Home:
360 run_action(AnHome, IA_DEFAULT, NULL, NULL);
361 break;
362 case XK_Tab:
363 run_action(AnTab, IA_DEFAULT, NULL, NULL);
364 break;
365 #if defined(XK_ISO_Left_Tab) /*[*/
366 case XK_ISO_Left_Tab:
367 run_action(AnBackTab, IA_DEFAULT, NULL, NULL);
368 break;
369 #endif /*]*/
370 case XK_Clear:
371 run_action(AnClear, IA_DEFAULT, NULL, NULL);
372 break;
373 case XK_Sys_Req:
374 run_action(AnSysReq, IA_DEFAULT, NULL, NULL);
375 break;
376 #if defined(XK_EuroSign) /*[*/
377 case XK_EuroSign:
378 run_action(AnKey, IA_DEFAULT, "currency", NULL);
379 break;
380 #endif /*]*/
381
382 #if defined(XK_3270_Duplicate) /*[*/
383 /* Funky 3270 keysyms. */
384 case XK_3270_Duplicate:
385 run_action(AnDup, IA_DEFAULT, NULL, NULL);
386 break;
387 case XK_3270_FieldMark:
388 run_action(AnFieldMark, IA_DEFAULT, NULL, NULL);
389 break;
390 case XK_3270_Right2:
391 run_action(AnRight2, IA_DEFAULT, NULL, NULL);
392 break;
393 case XK_3270_Left2:
394 run_action(AnLeft2, IA_DEFAULT, NULL, NULL);
395 break;
396 case XK_3270_BackTab:
397 run_action(AnBackTab, IA_DEFAULT, NULL, NULL);
398 break;
399 case XK_3270_EraseEOF:
400 run_action(AnEraseEOF, IA_DEFAULT, NULL, NULL);
401 break;
402 case XK_3270_EraseInput:
403 run_action(AnEraseInput, IA_DEFAULT, NULL, NULL);
404 break;
405 case XK_3270_Reset:
406 run_action(AnReset, IA_DEFAULT, NULL, NULL);
407 break;
408 case XK_3270_PA1:
409 run_action(AnPA, IA_DEFAULT, "1", NULL);
410 break;
411 case XK_3270_PA2:
412 run_action(AnPA, IA_DEFAULT, "2", NULL);
413 break;
414 case XK_3270_PA3:
415 run_action(AnPA, IA_DEFAULT, "3", NULL);
416 break;
417 case XK_3270_Attn:
418 run_action(AnAttn, IA_DEFAULT, NULL, NULL);
419 break;
420 case XK_3270_AltCursor:
421 run_action(AnToggle, IA_DEFAULT, ResAltCursor, NULL);
422 break;
423 case XK_3270_CursorSelect:
424 run_action(AnCursorSelect, IA_DEFAULT, NULL, NULL);
425 break;
426 case XK_3270_Enter:
427 run_action(AnEnter, IA_DEFAULT, NULL, NULL);
428 break;
429 #endif /*]*/
430
431 /* Funky APL keysyms. */
432 case XK_downcaret:
433 run_action(AnKey, IA_DEFAULT, "apl_downcaret", NULL);
434 break;
435 case XK_upcaret:
436 run_action(AnKey, IA_DEFAULT, "apl_upcaret", NULL);
437 break;
438 case XK_overbar:
439 run_action(AnKey, IA_DEFAULT, "apl_overbar", NULL);
440 break;
441 case XK_downtack:
442 run_action(AnKey, IA_DEFAULT, "apl_downtack", NULL);
443 break;
444 case XK_upshoe:
445 run_action(AnKey, IA_DEFAULT, "apl_upshoe", NULL);
446 break;
447 case XK_downstile:
448 run_action(AnKey, IA_DEFAULT, "apl_downstile", NULL);
449 break;
450 case XK_underbar:
451 run_action(AnKey, IA_DEFAULT, "apl_underbar", NULL);
452 break;
453 case XK_jot:
454 run_action(AnKey, IA_DEFAULT, "apl_jot", NULL);
455 break;
456 case XK_quad:
457 run_action(AnKey, IA_DEFAULT, "apl_quad", NULL);
458 break;
459 case XK_uptack:
460 run_action(AnKey, IA_DEFAULT, "apl_uptack", NULL);
461 break;
462 case XK_circle:
463 run_action(AnKey, IA_DEFAULT, "apl_circle", NULL);
464 break;
465 case XK_upstile:
466 run_action(AnKey, IA_DEFAULT, "apl_upstile", NULL);
467 break;
468 case XK_downshoe:
469 run_action(AnKey, IA_DEFAULT, "apl_downshoe", NULL);
470 break;
471 case XK_rightshoe:
472 run_action(AnKey, IA_DEFAULT, "apl_rightshoe", NULL);
473 break;
474 case XK_leftshoe:
475 run_action(AnKey, IA_DEFAULT, "apl_leftshoe", NULL);
476 break;
477 case XK_lefttack:
478 run_action(AnKey, IA_DEFAULT, "apl_lefttack", NULL);
479 break;
480 case XK_righttack:
481 run_action(AnKey, IA_DEFAULT, "apl_righttack", NULL);
482 break;
483
484 default:
485 if (ks >= XK_F1 && ks <= XK_F24) {
486 snprintf(buf, sizeof(buf), "%ld", ks - XK_F1 + 1);
487 run_action(AnPF, IA_DEFAULT, buf, NULL);
488 } else {
489 ucs4_t ucs4;
490
491 ucs4 = keysym2ucs(ks);
492 if (ucs4 != (ucs4_t)-1) {
493 key_UCharacter(ucs4, KT_STD, IA_DEFAULT, false);
494 } else {
495 vtrace(" Default: dropped (unknown keysym)\n");
496 }
497 }
498 break;
499 }
500 break;
501
502 case ButtonPress:
503 case ButtonRelease:
504 vtrace(" Default: dropped (no action configured)\n");
505 break;
506 default:
507 vtrace(" Default: dropped (unknown event type)\n");
508 break;
509 }
510 }
511
512 /*
513 * Set or clear a temporary keymap.
514 *
515 * TemporaryKeymap(x) toggle keymap "x" (add "x" to the keymap, or if
516 * "x" was already added, remove it)
517 * TemporaryKeymap() removes the previous keymap, if any
518 * TemporaryKeymap(None) removes the previous keymap, if any
519 */
520 static bool
Keymap_action(ia_t ia,unsigned argc,const char ** argv)521 Keymap_action(ia_t ia, unsigned argc, const char **argv)
522 {
523 action_debug(AnKeymap, ia, argc, argv);
524 if (check_argc(AnKeymap, argc, 0, 1) < 0) {
525 return false;
526 }
527
528 reset_idle_timer();
529
530 if (argc == 0 || !strcmp(argv[0], KwNone)) {
531 temporary_keymap(NULL);
532 return true;
533 }
534
535 if (!temporary_keymap(argv[0])) {
536 popup_an_error(AnKeymap "(): Can't find %s %s", ResKeymap, argv[0]);
537 return false;
538 }
539 return true;
540 }
541
542 /**
543 * X keyboard module registration.
544 */
545 void
xkybd_register(void)546 xkybd_register(void)
547 {
548 static action_table_t xkybd_actions[] = {
549 { AnAltCursor, AltCursor_action, ACTION_KE },
550 { AnKeymap, Keymap_action, ACTION_KE },
551 { AnTemporaryKeymap, Keymap_action, ACTION_KE }
552 };
553
554 /* Register the actions. */
555 register_actions(xkybd_actions, array_count(xkybd_actions));
556 }
557