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