source: trunk/third/x3270/kybd.c @ 11123

Revision 11123, 60.6 KB checked in by ghudson, 27 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r11122, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Modifications Copyright 1993, 1994, 1995, 1996 by Paul Mattes.
3 * Original X11 Port Copyright 1990 by Jeff Sparkes.
4 *  Permission to use, copy, modify, and distribute this software and its
5 *  documentation for any purpose and without fee is hereby granted,
6 *  provided that the above copyright notice appear in all copies and that
7 *  both that copyright notice and this permission notice appear in
8 *  supporting documentation.
9 *
10 * Copyright 1989 by Georgia Tech Research Corporation, Atlanta, GA 30332.
11 *  All Rights Reserved.  GTRC hereby grants public use of this software.
12 *  Derivative works based on this software must incorporate this copyright
13 *  notice.
14 */
15
16/*
17 *      kybd.c
18 *              This module handles the keyboard for the 3270 emulator.
19 */
20
21#include "globals.h"
22#include <X11/Xatom.h>
23#include <X11/keysym.h>
24#include <fcntl.h>
25#include "3270ds.h"
26#include "appres.h"
27#include "ctlr.h"
28#include "cg.h"
29#include "resources.h"
30
31#include "actionsc.h"
32#include "ansic.h"
33#include "aplc.h"
34#include "ctlrc.h"
35#include "ftc.h"
36#include "keypadc.h"
37#include "kybdc.h"
38#include "macrosc.h"
39#include "popupsc.h"
40#include "printc.h"
41#include "screenc.h"
42#include "statusc.h"
43#include "tablesc.h"
44#include "telnetc.h"
45#include "trace_dsc.h"
46#include "utilc.h"
47
48/* Statics */
49#define NP      5
50static Atom     paste_atom[NP];
51static int      n_pasting = 0;
52static int      pix = 0;
53static Time     paste_time;
54static enum     { NONE, COMPOSE, FIRST } composing = NONE;
55static unsigned char pf_xlate[] = {
56        AID_PF1,  AID_PF2,  AID_PF3,  AID_PF4,  AID_PF5,  AID_PF6,
57        AID_PF7,  AID_PF8,  AID_PF9,  AID_PF10, AID_PF11, AID_PF12,
58        AID_PF13, AID_PF14, AID_PF15, AID_PF16, AID_PF17, AID_PF18,
59        AID_PF19, AID_PF20, AID_PF21, AID_PF22, AID_PF23, AID_PF24
60};
61static unsigned char pa_xlate[] = {
62        AID_PA1, AID_PA2, AID_PA3
63};
64#define PF_SZ   (sizeof(pf_xlate)/sizeof(pf_xlate[0]))
65#define PA_SZ   (sizeof(pa_xlate)/sizeof(pa_xlate[0]))
66static XtIntervalId unlock_id;
67#define UNLOCK_MS       350
68static Boolean  key_Character();
69
70static int nxk = 0;
71static struct xks {
72        KeySym key;
73        KeySym assoc;
74} *xk;
75static struct trans_list *tkm_last;
76
77static Boolean          insert = False;         /* insert mode */
78static Boolean          reverse = False;        /* reverse-input mode */
79
80/* Globals */
81unsigned int    kybdlock;
82unsigned char   aid = AID_NO;           /* current attention ID */
83struct trans_list *temp_keymaps;        /* temporary keymap list */
84
85/* Composite key mappings. */
86
87struct akeysym {
88        KeySym keysym;
89        enum keytype keytype;
90};
91static struct akeysym cc_first;
92static struct composite {
93        struct akeysym k1, k2;
94        struct akeysym translation;
95} *composites = NULL;
96static int n_composites = 0;
97
98#define ak_eq(k1, k2)   (((k1).keysym  == (k2).keysym) && \
99                         ((k1).keytype == (k2).keytype))
100
101static struct ta {
102        struct ta *next;
103        void (*fn)();
104        char *parm1;
105        char *parm2;
106} *ta_head = (struct ta *) NULL,
107  *ta_tail = (struct ta *) NULL;
108
109
110/*
111 * Put an action on the typeahead queue.
112 */
113void
114enq_ta(fn, parm1, parm2)
115void (*fn)();
116char *parm1;
117char *parm2;
118{
119        struct ta *ta;
120
121        /* If no connection, forget it. */
122        if (!CONNECTED) {
123                if (toggled(EVENT_TRACE))
124                        (void) fprintf(tracef, "  dropped (not connected)\n");
125                return;
126        }
127
128        /* If operator error, complain and drop it. */
129        if (kybdlock & KL_OERR_MASK) {
130                ring_bell();
131                if (toggled(EVENT_TRACE))
132                        (void) fprintf(tracef, "  dropped (operator error)\n");
133                return;
134        }
135
136        /* If scroll lock, complain and drop it. */
137        if (kybdlock & KL_SCROLLED) {
138                ring_bell();
139                if (toggled(EVENT_TRACE))
140                        (void) fprintf(tracef, "  dropped (scrolled)\n");
141                return;
142        }
143
144        /* If typeahead disabled, complain and drop it. */
145        if (!appres.typeahead) {
146                if (toggled(EVENT_TRACE))
147                        (void) fprintf(tracef, "  dropped (no typeahead)\n");
148                return;
149        }
150
151        ta = (struct ta *) XtMalloc(sizeof(*ta));
152        ta->next = (struct ta *) NULL;
153        ta->fn = fn;
154        ta->parm1 = ta->parm2 = CN;
155        if (parm1) {
156                ta->parm1 = XtNewString(parm1);
157                if (parm2)
158                        ta->parm2 = XtNewString(parm2);
159        }
160        if (ta_head)
161                ta_tail->next = ta;
162        else {
163                ta_head = ta;
164                status_typeahead(True);
165        }
166        ta_tail = ta;
167
168        if (toggled(EVENT_TRACE))
169                (void) fprintf(tracef, "  action queued (kybdlock 0x%x)\n",
170                    kybdlock);
171}
172
173/*
174 * Execute an action from the typeahead queue.
175 */
176Boolean
177run_ta()
178{
179        struct ta *ta;
180
181        if (kybdlock || (ta = ta_head) == (struct ta *)NULL)
182                return False;
183
184        if ((ta_head = ta->next) == (struct ta *)NULL) {
185                ta_tail = (struct ta *) NULL;
186                status_typeahead(False);
187        }
188
189        action_internal(ta->fn, IA_TYPEAHEAD, ta->parm1, ta->parm2);
190        if (ta->parm1 != CN)
191                XtFree(ta->parm1);
192        if (ta->parm2 != CN)
193                XtFree(ta->parm2);
194        XtFree((XtPointer)ta);
195
196        return True;
197}
198
199/*
200 * Flush the typeahead queue.
201 * Returns whether or not anything was flushed.
202 */
203Boolean
204flush_ta()
205{
206        struct ta *ta, *next;
207        Boolean any = False;
208
209        for (ta = ta_head; ta != (struct ta *) NULL; ta = next) {
210                if (ta->parm1)
211                        XtFree(ta->parm1);
212                if (ta->parm2)
213                        XtFree(ta->parm2);
214                next = ta->next;
215                XtFree((XtPointer)ta);
216                any = True;
217        }
218        ta_head = ta_tail = (struct ta *) NULL;
219        status_typeahead(False);
220        return any;
221}
222
223/*
224 * Translation table cache.
225 */
226XtTranslations
227lookup_tt(name, table)
228char *name;
229char *table;
230{
231        struct tt_cache {
232                char *name;
233                XtTranslations trans;
234                struct tt_cache *next;
235        };
236#       define TTN (struct tt_cache *)NULL
237        static struct tt_cache *tt_cache = TTN;
238        struct tt_cache *t;
239
240        /* Look for an old one. */
241        for (t = tt_cache; t != TTN; t = t->next)
242                if (!strcmp(name, t->name))
243                        return t->trans;
244
245        /* Allocate and translate a new one. */
246        t = (struct tt_cache *)XtMalloc(sizeof(*t));
247        t->name = XtNewString(name);
248        t->trans = XtParseTranslationTable(table);
249        t->next = tt_cache;
250        tt_cache = t;
251
252        return t->trans;
253}
254#undef TTN
255
256/* Set bits in the keyboard lock. */
257/*ARGSUSED*/
258void
259kybdlock_set(bits, cause)
260unsigned int bits;
261char *cause;
262{
263        unsigned int n;
264
265        n = kybdlock | bits;
266        if (n != kybdlock) {
267#if defined(KYBDLOCK_TRACE) /*[*/
268                if (toggled(EVENT_TRACE))
269                        (void) fprintf(tracef,
270                            "  %s: kybdlock |= 0x%04x, 0x%04x -> 0x%04x\n",
271                            cause, bits, kybdlock, n);
272#endif /*]*/
273                kybdlock = n;
274                status_kybdlock();
275        }
276}
277
278/* Clear bits in the keyboard lock. */
279/*ARGSUSED*/
280void
281kybdlock_clr(bits, cause)
282unsigned int bits;
283char *cause;
284{
285        unsigned int n;
286
287        n = kybdlock & ~bits;
288        if (n != kybdlock) {
289#if defined(KYBDLOCK_TRACE) /*[*/
290                if (toggled(EVENT_TRACE))
291                        (void) fprintf(tracef,
292                            "  %s: kybdlock &= ~0x%04x, 0x%04x -> 0x%04x\n",
293                            cause, bits, kybdlock, n);
294#endif /*]*/
295                kybdlock = n;
296                status_kybdlock();
297        }
298}
299
300/*
301 * Set or clear enter-inhibit mode.
302 */
303void
304kybd_inhibit(inhibit)
305Boolean inhibit;
306{
307        if (inhibit) {
308                kybdlock_set(KL_ENTER_INHIBIT, "kybd_inhibit");
309                if (kybdlock == KL_ENTER_INHIBIT)
310                        status_reset();
311        } else {
312                kybdlock_clr(KL_ENTER_INHIBIT, "kybd_inhibit");
313                if (!kybdlock)
314                        status_reset();
315        }
316}
317
318/*
319 * Called when a host connects or disconnects.
320 */
321void
322kybd_connect()
323{
324        if (kybdlock & KL_DEFERRED_UNLOCK)
325                XtRemoveTimeOut(unlock_id);
326        kybdlock_clr(-1, "kybd_connect");
327
328        if (CONNECTED) {
329                /* Wait for any output or a WCC(restore) from the host */
330                kybdlock_set(KL_AWAITING_FIRST, "kybd_connect");
331        } else {
332                kybdlock_set(KL_NOT_CONNECTED, "kybd_connect");
333                (void) flush_ta();
334        }
335}
336
337/*
338 * Toggle insert mode.
339 */
340static void
341insert_mode(on)
342Boolean on;
343{
344        insert = on;
345        status_insert_mode(on);
346}
347
348/*
349 * Toggle reverse mode.
350 */
351static void
352reverse_mode(on)
353Boolean on;
354{
355        reverse = on;
356        status_reverse_mode(on);
357}
358
359/*
360 * Lock the keyboard because of an operator error.
361 */
362static void
363operator_error(error_type)
364int error_type;
365{
366        if (sms_redirect())
367                popup_an_error("Keyboard locked");
368        if (appres.oerr_lock || sms_redirect()) {
369                status_oerr(error_type);
370                mcursor_locked();
371                kybdlock_set((unsigned int)error_type, "operator_error");
372                (void) flush_ta();
373        } else
374                ring_bell();
375}
376
377
378/*
379 * Handle an AID (Attention IDentifier) key.  This is the common stuff that
380 * gets executed for all AID keys (PFs, PAs, Clear and etc).
381 */
382void
383key_AID(aid_code)
384unsigned char   aid_code;
385{
386        if (IN_ANSI) {
387                register int    i;
388
389                if (aid_code == AID_ENTER) {
390                        net_sendc('\r');
391                        return;
392                }
393                for (i = 0; i < PF_SZ; i++)
394                        if (aid_code == pf_xlate[i]) {
395                                ansi_send_pf(i+1);
396                                return;
397                        }
398                for (i = 0; i < PA_SZ; i++)
399                        if (aid_code == pa_xlate[i]) {
400                                ansi_send_pa(i+1);
401                                return;
402                        }
403                return;
404        }
405        status_twait();
406        mcursor_waiting();
407        insert_mode(False);
408        kybdlock_set(KL_OIA_TWAIT | KL_OIA_LOCKED, "key_AID");
409        aid = aid_code;
410        ctlr_read_modified(aid, False);
411        ticking_start(False);
412        status_ctlr_done();
413}
414
415/*ARGSUSED*/
416void
417PF_action(w, event, params, num_params)
418Widget w;
419XEvent *event;
420String *params;
421Cardinal *num_params;
422{
423        int k;
424
425        action_debug(PF_action, event, params, num_params);
426        if (check_usage(PF_action, *num_params, 1, 1) < 0)
427                return;
428        k = atoi(params[0]);
429        if (k < 0 || k > PF_SZ) {
430                popup_an_error("%s: invalid argument", action_name(PF_action));
431                return;
432        }
433        if (kybdlock)
434                enq_ta(PF_action, params[0], CN);
435        else
436                key_AID(pf_xlate[k-1]);
437}
438
439/*ARGSUSED*/
440void
441PA_action(w, event, params, num_params)
442Widget w;
443XEvent *event;
444String *params;
445Cardinal *num_params;
446{
447        int k;
448
449        action_debug(PA_action, event, params, num_params);
450        if (check_usage(PA_action, *num_params, 1, 1) < 0)
451                return;
452        k = atoi(params[0]);
453        if (k < 0 || k > PA_SZ) {
454                popup_an_error("%s: invalid argument %d",
455                    action_name(PA_action), k);
456                return;
457        }
458        if (kybdlock)
459                enq_ta(PA_action, params[0], CN);
460        else
461                key_AID(pa_xlate[k-1]);
462}
463
464
465/*
466 * ATTN key, similar to an AID key but without the read_modified call.
467 */
468/*ARGSUSED*/
469void
470Attn_action(w, event, params, num_params)
471Widget w;
472XEvent *event;
473String *params;
474Cardinal *num_params;
475{
476        action_debug(Attn_action, event, params, num_params);
477        if (!IN_3270)
478                return;
479        if (kybdlock) {
480                enq_ta(Attn_action, CN, CN);
481                return;
482        }
483        if (appres.attn_lock) {
484                status_twait();
485                mcursor_waiting();
486                insert_mode(False);
487                kybdlock_set(KL_OIA_TWAIT | KL_OIA_LOCKED, "Attn_action");
488        }
489        net_break();
490        if (appres.attn_lock) {
491                ticking_start(False);
492                status_ctlr_done();
493        }
494}
495
496
497
498/*ARGSUSED*/
499static void
500key_Character_wrapper(w, event, params, num_params)
501Widget w;
502XEvent *event;
503String *params;
504Cardinal *num_params;
505{
506        int cgcode;
507        Boolean with_ge = False;
508
509        cgcode = atoi(params[0]);
510        if (cgcode & 0x100) {
511                with_ge = True;
512                cgcode &= 0xff;
513        }
514        if (toggled(EVENT_TRACE)) {
515                (void) fprintf(tracef, " %s -> Key(%s\"%s\")\n",
516                    ia_name[(int) ia_cause],
517                    with_ge ? "GE " : "",
518                    ctl_see((int) cg2asc[cgcode]));
519        }
520        (void) key_Character(cgcode, with_ge);
521}
522
523/*
524 * Handle an ordinary displayable character key.  Lots of stuff to handle
525 * insert-mode, protected fields and etc.
526 */
527static Boolean
528key_Character(cgcode, with_ge)
529int     cgcode;
530Boolean with_ge;
531{
532        register int    baddr, end_baddr;
533        register unsigned char  *fa;
534        Boolean no_room = False;
535
536        if (kybdlock) {
537                char code[64];
538
539                (void) sprintf(code, "%d", cgcode | (with_ge ? 0x100 : 0));
540                enq_ta(key_Character_wrapper, code, CN);
541                return False;
542        }
543        baddr = cursor_addr;
544        fa = get_field_attribute(baddr);
545        if (IS_FA(screen_buf[baddr]) || FA_IS_PROTECTED(*fa)) {
546                operator_error(KL_OERR_PROTECTED);
547                return False;
548        }
549        if (appres.numeric_lock && FA_IS_NUMERIC(*fa) &&
550            !((cgcode >= CG_0 && cgcode <= CG_9) ||
551              cgcode == CG_minus || cgcode == CG_period)) {
552                operator_error(KL_OERR_NUMERIC);
553                return False;
554        }
555        if (reverse || (insert && screen_buf[baddr])) {
556                int last_blank = -1;
557
558                /* Find next null, next fa, or last blank */
559                end_baddr = baddr;
560                if (screen_buf[end_baddr] == CG_space)
561                        last_blank = end_baddr;
562                do {
563                        INC_BA(end_baddr);
564                        if (screen_buf[end_baddr] == CG_space)
565                                last_blank = end_baddr;
566                        if (screen_buf[end_baddr] == CG_null
567                            ||  IS_FA(screen_buf[end_baddr]))
568                                break;
569                } while (end_baddr != baddr);
570
571                /* Pretend a trailing blank is a null, if desired. */
572                if (toggled(BLANK_FILL) && last_blank != -1) {
573                        INC_BA(last_blank);
574                        if (last_blank == end_baddr) {
575                                DEC_BA(end_baddr);
576                                ctlr_add(end_baddr, CG_null, 0);
577                        }
578                }
579
580                /* Check for field overflow. */
581                if (screen_buf[end_baddr] != CG_null) {
582                        if (insert) {
583                                operator_error(KL_OERR_OVERFLOW);
584                                return False;
585                        } else {        /* reverse */
586                                no_room = True;
587                        }
588                } else {
589                        /* Shift data over. */
590                        if (end_baddr > baddr) {
591                                /* At least one byte to copy, no wrap. */
592                                ctlr_bcopy(baddr, baddr+1, end_baddr - baddr,
593                                    0);
594                        } else if (end_baddr < baddr) {
595                                /* At least one byte to copy, wraps to top. */
596                                ctlr_bcopy(0, 1, end_baddr, 0);
597                                ctlr_add(0, screen_buf[(ROWS * COLS) - 1], 0);
598                                ctlr_bcopy(baddr, baddr+1,
599                                    ((ROWS * COLS) - 1) - baddr, 0);
600                        }
601                }
602
603        }
604
605        /* Replace leading nulls with blanks, if desired. */
606        if (formatted && toggled(BLANK_FILL)) {
607                int             baddr_sof = fa - screen_buf;
608                register int    baddr_fill = baddr;
609
610                DEC_BA(baddr_fill);
611                while (baddr_fill != baddr_sof) {
612
613                        /* Check for backward line wrap. */
614                        if ((baddr_fill % COLS) == COLS - 1) {
615                                Boolean aborted = True;
616                                register int baddr_scan = baddr_fill;
617
618                                /*
619                                 * Check the field within the preceeding line
620                                 * for NULLs.
621                                 */
622                                while (baddr_scan != baddr_sof) {
623                                        if (screen_buf[baddr_scan] != CG_null) {
624                                                aborted = False;
625                                                break;
626                                        }
627                                        if (!(baddr_scan % COLS))
628                                                break;
629                                        DEC_BA(baddr_scan);
630                                }
631                                if (aborted)
632                                        break;
633                        }
634
635                        if (screen_buf[baddr_fill] == CG_null)
636                                ctlr_add(baddr_fill, CG_space, 0);
637                        DEC_BA(baddr_fill);
638                }
639        }
640
641        /* Add the character. */
642        if (no_room) {
643                do {
644                        INC_BA(baddr);
645                } while (!IS_FA(screen_buf[baddr]));
646        } else {
647                ctlr_add(baddr, (unsigned char)cgcode,
648                    (unsigned char)(with_ge ? CS_GE : 0));
649                ctlr_add_fg(baddr, 0);
650                ctlr_add_gr(baddr, 0);
651                if (!reverse)
652                        INC_BA(baddr);
653        }
654
655        /* Implement auto-skip, and don't land on attribute bytes. */
656        if (cgcode != CG_dup) {
657                if (IS_FA(screen_buf[baddr]) &&
658                    FA_IS_SKIP(screen_buf[baddr]))
659                        baddr = next_unprotected(baddr);
660                else while (IS_FA(screen_buf[baddr]))
661                        INC_BA(baddr);
662
663                cursor_move(baddr);
664        }
665
666        mdt_set(fa);
667        return True;
668}
669
670/*
671 * Handle an ordinary character key, given an ASCII code.
672 */
673static void
674key_ACharacter(c, keytype, cause)
675unsigned char   c;
676enum keytype    keytype;
677enum iaction    cause;
678{
679        register int i;
680        struct akeysym ak;
681
682        ak.keysym = c;
683        ak.keytype = keytype;
684
685        switch (composing) {
686            case NONE:
687                break;
688            case COMPOSE:
689                for (i = 0; i < n_composites; i++)
690                        if (ak_eq(composites[i].k1, ak) ||
691                            ak_eq(composites[i].k2, ak))
692                                break;
693                if (i < n_composites) {
694                        cc_first.keysym = c;
695                        cc_first.keytype = keytype;
696                        composing = FIRST;
697                        status_compose(True, c, keytype);
698                } else {
699                        ring_bell();
700                        composing = NONE;
701                        status_compose(False, 0, KT_STD);
702                }
703                return;
704            case FIRST:
705                composing = NONE;
706                status_compose(False, 0, KT_STD);
707                for (i = 0; i < n_composites; i++)
708                        if ((ak_eq(composites[i].k1, cc_first) &&
709                             ak_eq(composites[i].k2, ak)) ||
710                            (ak_eq(composites[i].k1, ak) &&
711                             ak_eq(composites[i].k2, cc_first)))
712                                break;
713                if (i < n_composites) {
714                        c = composites[i].translation.keysym;
715                        keytype = composites[i].translation.keytype;
716                } else {
717                        ring_bell();
718                        return;
719                }
720                break;
721        }
722
723        if (toggled(EVENT_TRACE)) {
724                (void) fprintf(tracef, " %s -> Key(\"%s\")\n",
725                    ia_name[(int) cause], ctl_see((int) c));
726        }
727        if (IN_3270) {
728                if (c < ' ') {
729                        if (toggled(EVENT_TRACE))
730                                (void) fprintf(tracef,
731                                            "  dropped (control char)\n");
732                        return;
733                }
734                (void) key_Character((int) asc2cg[c], keytype == KT_GE);
735        } else if (IN_ANSI) {
736                net_sendc((char) c);
737        } else {
738                if (toggled(EVENT_TRACE))
739                        (void) fprintf(tracef, "  dropped (not connected)\n");
740        }
741}
742
743
744/*
745 * Simple toggles.
746 */
747/*ARGSUSED*/
748void
749AltCursor_action(w, event, params, num_params)
750Widget w;
751XEvent *event;
752String *params;
753Cardinal *num_params;
754{
755        action_debug(AltCursor_action, event, params, num_params);
756        do_toggle(ALT_CURSOR);
757}
758
759/*ARGSUSED*/
760void
761MonoCase_action(w, event, params, num_params)
762Widget w;
763XEvent *event;
764String *params;
765Cardinal *num_params;
766{
767        action_debug(MonoCase_action, event, params, num_params);
768        do_toggle(MONOCASE);
769}
770
771/*
772 * Flip the display left-to-right
773 */
774/*ARGSUSED*/
775void
776Flip_action(w, event, params, num_params)
777Widget w;
778XEvent *event;
779String *params;
780Cardinal *num_params;
781{
782        action_debug(Flip_action, event, params, num_params);
783        screen_flip();
784}
785
786
787
788/*
789 * Tab forward to next field.
790 */
791/*ARGSUSED*/
792void
793Tab_action(w, event, params, num_params)
794Widget w;
795XEvent *event;
796String *params;
797Cardinal *num_params;
798{
799        action_debug(Tab_action, event, params, num_params);
800        if (IN_ANSI) {
801                net_sendc('\t');
802                return;
803        }
804        if (kybdlock) {
805                enq_ta(Tab_action, CN, CN);
806                return;
807        }
808        cursor_move(next_unprotected(cursor_addr));
809}
810
811
812/*
813 * Tab backward to previous field.
814 */
815/*ARGSUSED*/
816void
817BackTab_action(w, event, params, num_params)
818Widget w;
819XEvent *event;
820String *params;
821Cardinal *num_params;
822{
823        register int    baddr, nbaddr;
824        int             sbaddr;
825
826        action_debug(BackTab_action, event, params, num_params);
827        if (!IN_3270)
828                return;
829        if (kybdlock) {
830                enq_ta(BackTab_action, CN, CN);
831                return;
832        }
833        baddr = cursor_addr;
834        DEC_BA(baddr);
835        if (IS_FA(screen_buf[baddr]))   /* at bof */
836                DEC_BA(baddr);
837        sbaddr = baddr;
838        while (True) {
839                nbaddr = baddr;
840                INC_BA(nbaddr);
841                if (IS_FA(screen_buf[baddr])
842                    &&  !FA_IS_PROTECTED(screen_buf[baddr])
843                    &&  !IS_FA(screen_buf[nbaddr]))
844                        break;
845                DEC_BA(baddr);
846                if (baddr == sbaddr) {
847                        cursor_move(0);
848                        return;
849                }
850        }
851        INC_BA(baddr);
852        cursor_move(baddr);
853}
854
855
856/*
857 * Deferred keyboard unlock.
858 */
859
860/*ARGSUSED*/
861static void
862defer_unlock(closure, id)
863XtPointer closure;
864XtIntervalId *id;
865{
866        kybdlock_clr(KL_DEFERRED_UNLOCK, "defer_unlock");
867        status_reset();
868        if (CONNECTED)
869                ps_process();
870}
871
872/*
873 * Reset keyboard lock.
874 */
875void
876do_reset(explicit)
877Boolean explicit;
878{
879        /*
880         * If explicit (from the keyboard) and there is typeahead or
881         * a half-composed key, simply flush it.
882         */
883        if (explicit) {
884                Boolean half_reset = False;
885
886                if (flush_ta())
887                        half_reset = True;
888                if (composing != NONE) {
889                        composing = NONE;
890                        status_compose(False, 0, KT_STD);
891                        half_reset = True;
892                }
893                if (half_reset)
894                        return;
895        }
896
897        /* Always clear insert mode. */
898        insert_mode(False);
899
900        /* Otherwise, if not connect, reset is a no-op. */
901        if (!CONNECTED)
902                return;
903
904        /*
905         * Remove any deferred keyboard unlock.  We will either unlock the
906         * keyboard now, or want to defer further into the future.
907         */
908        if (kybdlock & KL_DEFERRED_UNLOCK)
909                XtRemoveTimeOut(unlock_id);
910
911        /*
912         * If explicit (from the keyboard), unlock the keyboard now.
913         * Otherwise (from the host), schedule a deferred keyboard unlock.
914         */
915        if (explicit || ft_state != FT_NONE) {
916                kybdlock_clr(-1, "do_reset");
917        } else if (kybdlock &
918  (KL_DEFERRED_UNLOCK | KL_OIA_TWAIT | KL_OIA_LOCKED | KL_AWAITING_FIRST)) {
919                kybdlock_clr(~KL_DEFERRED_UNLOCK, "do_reset");
920                kybdlock_set(KL_DEFERRED_UNLOCK, "do_reset");
921                unlock_id = XtAppAddTimeOut(appcontext, UNLOCK_MS,
922                    defer_unlock, 0);
923        }
924
925        /* Clean up other modes. */
926        status_reset();
927        mcursor_normal();
928        composing = NONE;
929        status_compose(False, 0, KT_STD);
930}
931
932/*ARGSUSED*/
933void
934Reset_action(w, event, params, num_params)
935Widget w;
936XEvent *event;
937String *params;
938Cardinal *num_params;
939{
940        action_debug(Reset_action, event, params, num_params);
941        do_reset(True);
942}
943
944
945/*
946 * Move to first unprotected field on screen.
947 */
948/*ARGSUSED*/
949void
950Home_action(w, event, params, num_params)
951Widget w;
952XEvent *event;
953String *params;
954Cardinal *num_params;
955{
956        action_debug(Home_action, event, params, num_params);
957        if (IN_ANSI) {
958                ansi_send_home();
959                return;
960        }
961        if (kybdlock) {
962                enq_ta(Home_action, CN, CN);
963                return;
964        }
965        if (!formatted) {
966                cursor_move(0);
967                return;
968        }
969        cursor_move(next_unprotected(ROWS*COLS-1));
970}
971
972
973/*
974 * Cursor left 1 position.
975 */
976static void
977do_left()
978{
979        register int    baddr;
980
981        baddr = cursor_addr;
982        DEC_BA(baddr);
983        cursor_move(baddr);
984}
985
986/*ARGSUSED*/
987void
988Left_action(w, event, params, num_params)
989Widget w;
990XEvent *event;
991String *params;
992Cardinal *num_params;
993{
994        action_debug(Left_action, event, params, num_params);
995        if (IN_ANSI) {
996                ansi_send_left();
997                return;
998        }
999        if (kybdlock) {
1000                enq_ta(Left_action, CN, CN);
1001                return;
1002        }
1003        if (!flipped)
1004                do_left();
1005        else {
1006                register int    baddr;
1007
1008                baddr = cursor_addr;
1009                INC_BA(baddr);
1010                cursor_move(baddr);
1011        }
1012}
1013
1014
1015/*
1016 * Delete char key.
1017 * Returns "True" if succeeds, "False" otherwise.
1018 */
1019static Boolean
1020do_delete()
1021{
1022        register int    baddr, end_baddr;
1023        register unsigned char  *fa;
1024
1025        baddr = cursor_addr;
1026        fa = get_field_attribute(baddr);
1027        if (FA_IS_PROTECTED(*fa) || IS_FA(screen_buf[baddr])) {
1028                operator_error(KL_OERR_PROTECTED);
1029                return False;
1030        }
1031        /* find next fa */
1032        end_baddr = baddr;
1033        do {
1034                INC_BA(end_baddr);
1035                if (IS_FA(screen_buf[end_baddr]))
1036                        break;
1037        } while (end_baddr != baddr);
1038        DEC_BA(end_baddr);
1039        if (end_baddr > baddr) {
1040                ctlr_bcopy(baddr+1, baddr, end_baddr - baddr, 0);
1041        } else if (end_baddr != baddr) {
1042                ctlr_bcopy(baddr+1, baddr, ((ROWS * COLS) - 1) - baddr, 0);
1043                ctlr_add((ROWS * COLS) - 1, screen_buf[0], 0);
1044                ctlr_bcopy(1, 0, end_baddr, 0);
1045        }
1046        ctlr_add(end_baddr, CG_null, 0);
1047        mdt_set(fa);
1048        return True;
1049}
1050
1051/*ARGSUSED*/
1052void
1053Delete_action(w, event, params, num_params)
1054Widget w;
1055XEvent *event;
1056String *params;
1057Cardinal *num_params;
1058{
1059        action_debug(Delete_action, event, params, num_params);
1060        if (IN_ANSI) {
1061                net_sendc('\177');
1062                return;
1063        }
1064        if (kybdlock) {
1065                enq_ta(Delete_action, CN, CN);
1066                return;
1067        }
1068        if (!do_delete())
1069                return;
1070        if (reverse) {
1071                int baddr = cursor_addr;
1072
1073                DEC_BA(baddr);
1074                if (!IS_FA(screen_buf[baddr]))
1075                        cursor_move(baddr);
1076        }
1077}
1078
1079
1080/*
1081 * Backspace.
1082 */
1083/*ARGSUSED*/
1084void
1085BackSpace_action(w, event, params, num_params)
1086Widget w;
1087XEvent *event;
1088String *params;
1089Cardinal *num_params;
1090{
1091        action_debug(BackSpace_action, event, params, num_params);
1092        if (IN_ANSI) {
1093                net_send_erase();
1094        } else {
1095                if (kybdlock) {
1096                        enq_ta(BackSpace_action, CN, CN);
1097                        return;
1098                }
1099                if (reverse)
1100                        (void) do_delete();
1101                else if (!flipped)
1102                        do_left();
1103                else {
1104                        register int    baddr;
1105
1106                        baddr = cursor_addr;
1107                        INC_BA(baddr);
1108                        cursor_move(baddr);
1109                }
1110        }
1111}
1112
1113
1114/*
1115 * Destructive backspace, like Unix "erase".
1116 */
1117/*ARGSUSED*/
1118void
1119Erase_action(w, event, params, num_params)
1120Widget w;
1121XEvent *event;
1122String *params;
1123Cardinal *num_params;
1124{
1125        int     baddr;
1126        unsigned char   *fa;
1127
1128        action_debug(Erase_action, event, params, num_params);
1129        if (IN_ANSI) {
1130                net_send_erase();
1131                return;
1132        }
1133        if (kybdlock) {
1134                enq_ta(Erase_action, CN, CN);
1135                return;
1136        }
1137        baddr = cursor_addr;
1138        fa = get_field_attribute(baddr);
1139        if (fa == &screen_buf[baddr] || FA_IS_PROTECTED(*fa)) {
1140                operator_error(KL_OERR_PROTECTED);
1141                return;
1142        }
1143        if (baddr && fa == &screen_buf[baddr - 1])
1144                return;
1145        do_left();
1146        (void) do_delete();
1147}
1148
1149
1150/*
1151 * Cursor right 1 position.
1152 */
1153/*ARGSUSED*/
1154void
1155Right_action(w, event, params, num_params)
1156Widget w;
1157XEvent *event;
1158String *params;
1159Cardinal *num_params;
1160{
1161        register int    baddr;
1162
1163        action_debug(Right_action, event, params, num_params);
1164        if (IN_ANSI) {
1165                ansi_send_right();
1166                return;
1167        }
1168        if (kybdlock) {
1169                enq_ta(Right_action, CN, CN);
1170                return;
1171        }
1172        if (!flipped) {
1173                baddr = cursor_addr;
1174                INC_BA(baddr);
1175                cursor_move(baddr);
1176        } else
1177                do_left();
1178}
1179
1180
1181/*
1182 * Cursor left 2 positions.
1183 */
1184/*ARGSUSED*/
1185void
1186Left2_action(w, event, params, num_params)
1187Widget w;
1188XEvent *event;
1189String *params;
1190Cardinal *num_params;
1191{
1192        register int    baddr;
1193
1194        action_debug(Left2_action, event, params, num_params);
1195        if (IN_ANSI)
1196                return;
1197        if (kybdlock) {
1198                enq_ta(Left2_action, CN, CN);
1199                return;
1200        }
1201        baddr = cursor_addr;
1202        DEC_BA(baddr);
1203        DEC_BA(baddr);
1204        cursor_move(baddr);
1205}
1206
1207
1208/*
1209 * Cursor to previous word.
1210 */
1211/*ARGSUSED*/
1212void
1213PreviousWord_action(w, event, params, num_params)
1214Widget w;
1215XEvent *event;
1216String *params;
1217Cardinal *num_params;
1218{
1219        register int baddr;
1220        int baddr0;
1221        unsigned char  c;
1222        Boolean prot;
1223
1224        action_debug(PreviousWord_action, event, params, num_params);
1225        if (IN_ANSI)
1226                return;
1227        if (kybdlock) {
1228                enq_ta(PreviousWord_action, CN, CN);
1229                return;
1230        }
1231        if (!formatted)
1232                return;
1233
1234        baddr = cursor_addr;
1235        prot = FA_IS_PROTECTED(*get_field_attribute(baddr));
1236
1237        /* Skip to before this word, if in one now. */
1238        if (!prot) {
1239                c = screen_buf[baddr];
1240                while (!IS_FA(c) && c != CG_space && c != CG_null) {
1241                        DEC_BA(baddr);
1242                        if (baddr == cursor_addr)
1243                                return;
1244                        c = screen_buf[baddr];
1245                }
1246        }
1247        baddr0 = baddr;
1248
1249        /* Find the end of the preceding word. */
1250        do {
1251                c = screen_buf[baddr];
1252                if (IS_FA(c)) {
1253                        DEC_BA(baddr);
1254                        prot = FA_IS_PROTECTED(*get_field_attribute(baddr));
1255                        continue;
1256                }
1257                if (!prot && c != CG_space && c != CG_null)
1258                        break;
1259                DEC_BA(baddr);
1260        } while (baddr != baddr0);
1261
1262        if (baddr == baddr0)
1263                return;
1264
1265        /* Go it its front. */
1266        for (;;) {
1267                DEC_BA(baddr);
1268                c = screen_buf[baddr];
1269                if (IS_FA(c) || c == CG_space || c == CG_null) {
1270                        break;
1271                }
1272        }
1273        INC_BA(baddr);
1274        cursor_move(baddr);
1275}
1276
1277
1278/*
1279 * Cursor right 2 positions.
1280 */
1281/*ARGSUSED*/
1282void
1283Right2_action(w, event, params, num_params)
1284Widget w;
1285XEvent *event;
1286String *params;
1287Cardinal *num_params;
1288{
1289        register int    baddr;
1290
1291        action_debug(Right2_action, event, params, num_params);
1292        if (IN_ANSI)
1293                return;
1294        if (kybdlock) {
1295                enq_ta(Right2_action, CN, CN);
1296                return;
1297        }
1298        baddr = cursor_addr;
1299        INC_BA(baddr);
1300        INC_BA(baddr);
1301        cursor_move(baddr);
1302}
1303
1304
1305/* Find the next unprotected word, or -1 */
1306static int
1307nu_word(baddr)
1308int baddr;
1309{
1310        int baddr0 = baddr;
1311        unsigned char c;
1312        Boolean prot;
1313
1314        prot = FA_IS_PROTECTED(*get_field_attribute(baddr));
1315
1316        do {
1317                c = screen_buf[baddr];
1318                if (IS_FA(c))
1319                        prot = FA_IS_PROTECTED(c);
1320                else if (!prot && c != CG_space && c != CG_null)
1321                        return baddr;
1322                INC_BA(baddr);
1323        } while (baddr != baddr0);
1324
1325        return -1;
1326}
1327
1328/* Find the next word in this field, or -1 */
1329static int
1330nt_word(baddr)
1331int baddr;
1332{
1333        int baddr0 = baddr;
1334        unsigned char c;
1335        Boolean in_word = True;
1336
1337        do {
1338                c = screen_buf[baddr];
1339                if (IS_FA(c))
1340                        return -1;
1341                if (in_word) {
1342                        if (c == CG_space || c == CG_null)
1343                                in_word = False;
1344                } else {
1345                        if (c != CG_space && c != CG_null)
1346                                return baddr;
1347                }
1348                INC_BA(baddr);
1349        } while (baddr != baddr0);
1350
1351        return -1;
1352}
1353
1354
1355/*
1356 * Cursor to next unprotected word.
1357 */
1358/*ARGSUSED*/
1359void
1360NextWord_action(w, event, params, num_params)
1361Widget w;
1362XEvent *event;
1363String *params;
1364Cardinal *num_params;
1365{
1366        register int    baddr;
1367        unsigned char c;
1368
1369        action_debug(NextWord_action, event, params, num_params);
1370        if (IN_ANSI)
1371                return;
1372        if (kybdlock) {
1373                enq_ta(NextWord_action, CN, CN);
1374                return;
1375        }
1376        if (!formatted)
1377                return;
1378
1379        /* If not in an unprotected field, go to the next unprotected word. */
1380        if (IS_FA(screen_buf[cursor_addr]) ||
1381            FA_IS_PROTECTED(*get_field_attribute(cursor_addr))) {
1382                baddr = nu_word(cursor_addr);
1383                if (baddr != -1)
1384                        cursor_move(baddr);
1385                return;
1386        }
1387
1388        /* If there's another word in this field, go to it. */
1389        baddr = nt_word(cursor_addr);
1390        if (baddr != -1) {
1391                cursor_move(baddr);
1392                return;
1393        }
1394
1395        /* If in a word, go to just after its end. */
1396        c = screen_buf[cursor_addr];
1397        if (c != CG_space && c != CG_null) {
1398                baddr = cursor_addr;
1399                do {
1400                        c = screen_buf[baddr];
1401                        if (c == CG_space || c == CG_null) {
1402                                cursor_move(baddr);
1403                                return;
1404                        } else if (IS_FA(c)) {
1405                                baddr = nu_word(baddr);
1406                                if (baddr != -1)
1407                                        cursor_move(baddr);
1408                                return;
1409                        }
1410                        INC_BA(baddr);
1411                } while (baddr != cursor_addr);
1412        }
1413        /* Otherwise, go to the next unprotected word. */
1414        else {
1415                baddr = nu_word(cursor_addr);
1416                if (baddr != -1)
1417                        cursor_move(baddr);
1418        }
1419}
1420
1421
1422/*
1423 * Cursor up 1 position.
1424 */
1425/*ARGSUSED*/
1426void
1427Up_action(w, event, params, num_params)
1428Widget w;
1429XEvent *event;
1430String *params;
1431Cardinal *num_params;
1432{
1433        register int    baddr;
1434
1435        action_debug(Up_action, event, params, num_params);
1436        if (IN_ANSI) {
1437                ansi_send_up();
1438                return;
1439        }
1440        if (kybdlock) {
1441                enq_ta(Up_action, CN, CN);
1442                return;
1443        }
1444        baddr = cursor_addr - COLS;
1445        if (baddr < 0)
1446                baddr = (cursor_addr + (ROWS * COLS)) - COLS;
1447        cursor_move(baddr);
1448}
1449
1450
1451/*
1452 * Cursor down 1 position.
1453 */
1454/*ARGSUSED*/
1455void
1456Down_action(w, event, params, num_params)
1457Widget w;
1458XEvent *event;
1459String *params;
1460Cardinal *num_params;
1461{
1462        register int    baddr;
1463
1464        action_debug(Down_action, event, params, num_params);
1465        if (IN_ANSI) {
1466                ansi_send_down();
1467                return;
1468        }
1469        if (kybdlock) {
1470                enq_ta(Down_action, CN, CN);
1471                return;
1472        }
1473        baddr = (cursor_addr + COLS) % (COLS * ROWS);
1474        cursor_move(baddr);
1475}
1476
1477
1478/*
1479 * Cursor to first field on next line or any lines after that.
1480 */
1481/*ARGSUSED*/
1482void
1483Newline_action(w, event, params, num_params)
1484Widget w;
1485XEvent *event;
1486String *params;
1487Cardinal *num_params;
1488{
1489        register int    baddr;
1490        register unsigned char  *fa;
1491
1492        action_debug(Newline_action, event, params, num_params);
1493        if (IN_ANSI) {
1494                net_sendc('\n');
1495                return;
1496        }
1497        if (kybdlock) {
1498                enq_ta(Newline_action, CN, CN);
1499                return;
1500        }
1501        baddr = (cursor_addr + COLS) % (COLS * ROWS);   /* down */
1502        baddr = (baddr / COLS) * COLS;                  /* 1st col */
1503        fa = get_field_attribute(baddr);
1504        if (fa != (&screen_buf[baddr]) && !FA_IS_PROTECTED(*fa))
1505                cursor_move(baddr);
1506        else
1507                cursor_move(next_unprotected(baddr));
1508}
1509
1510
1511/*
1512 * DUP key
1513 */
1514/*ARGSUSED*/
1515void
1516Dup_action(w, event, params, num_params)
1517Widget w;
1518XEvent *event;
1519String *params;
1520Cardinal *num_params;
1521{
1522        action_debug(Dup_action, event, params, num_params);
1523        if (IN_ANSI)
1524                return;
1525        if (kybdlock) {
1526                enq_ta(Dup_action, CN, CN);
1527                return;
1528        }
1529        if (key_Character(CG_dup, False))
1530                cursor_move(next_unprotected(cursor_addr));
1531}
1532
1533
1534/*
1535 * FM key
1536 */
1537/*ARGSUSED*/
1538void
1539FieldMark_action(w, event, params, num_params)
1540Widget w;
1541XEvent *event;
1542String *params;
1543Cardinal *num_params;
1544{
1545        action_debug(FieldMark_action, event, params, num_params);
1546        if (IN_ANSI)
1547                return;
1548        if (kybdlock) {
1549                enq_ta(FieldMark_action, CN, CN);
1550                return;
1551        }
1552        (void) key_Character(CG_fm, False);
1553}
1554
1555
1556/*
1557 * Vanilla AID keys.
1558 */
1559/*ARGSUSED*/
1560void
1561Enter_action(w, event, params, num_params)
1562Widget w;
1563XEvent *event;
1564String *params;
1565Cardinal *num_params;
1566{
1567        action_debug(Enter_action, event, params, num_params);
1568        if (kybdlock)
1569                enq_ta(Enter_action, CN, CN);
1570        else
1571                key_AID(AID_ENTER);
1572}
1573
1574
1575/*ARGSUSED*/
1576void
1577SysReq_action(w, event, params, num_params)
1578Widget w;
1579XEvent *event;
1580String *params;
1581Cardinal *num_params;
1582{
1583        action_debug(SysReq_action, event, params, num_params);
1584        if (kybdlock)
1585                enq_ta(SysReq_action, CN, CN);
1586        else
1587                key_AID(AID_SYSREQ);
1588}
1589
1590
1591/*
1592 * Clear AID key
1593 */
1594/*ARGSUSED*/
1595void
1596Clear_action(w, event, params, num_params)
1597Widget w;
1598XEvent *event;
1599String *params;
1600Cardinal *num_params;
1601{
1602        action_debug(Clear_action, event, params, num_params);
1603        if (IN_ANSI) {
1604                ansi_send_clear();
1605                return;
1606        }
1607        if (kybdlock && CONNECTED) {
1608                enq_ta(Clear_action, CN, CN);
1609                return;
1610        }
1611        buffer_addr = 0;
1612        ctlr_clear(True);
1613        cursor_move(0);
1614        if (CONNECTED)
1615                key_AID(AID_CLEAR);
1616}
1617
1618
1619/*
1620 * Cursor Select key (light pen simulator).
1621 */
1622/*ARGSUSED*/
1623void
1624CursorSelect_action(w, event, params, num_params)
1625Widget w;
1626XEvent *event;
1627String *params;
1628Cardinal *num_params;
1629{
1630        register unsigned char  *fa, *sel;
1631
1632        action_debug(CursorSelect_action, event, params, num_params);
1633        if (IN_ANSI)
1634                return;
1635        if (kybdlock) {
1636                enq_ta(CursorSelect_action, CN, CN);
1637                return;
1638        }
1639        fa = get_field_attribute(cursor_addr);
1640        if (!FA_IS_SELECTABLE(*fa)) {
1641                operator_error(KL_OERR_PROTECTED);
1642                return;
1643        }
1644        sel = fa + 1;
1645        switch (*sel) {
1646            case CG_greater:            /* > */
1647                ctlr_add(cursor_addr, CG_question, 0); /* change to ? */
1648                mdt_clear(fa);
1649                break;
1650            case CG_question:           /* ? */
1651                ctlr_add(cursor_addr, CG_greater, 0);   /* change to > */
1652                mdt_set(fa);
1653                break;
1654            case CG_space:              /* space */
1655            case CG_null:               /* null */
1656                mdt_set(fa);
1657                key_AID(AID_SELECT);
1658                break;
1659            case CG_ampersand:          /* & */
1660                mdt_set(fa);
1661                key_AID(AID_ENTER);
1662                break;
1663            default:
1664                operator_error(KL_OERR_PROTECTED);
1665        }
1666}
1667
1668
1669/*
1670 * Erase End Of Field Key.
1671 */
1672/*ARGSUSED*/
1673void
1674EraseEOF_action(w, event, params, num_params)
1675Widget w;
1676XEvent *event;
1677String *params;
1678Cardinal *num_params;
1679{
1680        register int    baddr;
1681        register unsigned char  *fa;
1682
1683        action_debug(EraseEOF_action, event, params, num_params);
1684        if (IN_ANSI)
1685                return;
1686        if (kybdlock) {
1687                enq_ta(EraseEOF_action, CN, CN);
1688                return;
1689        }
1690        baddr = cursor_addr;
1691        fa = get_field_attribute(baddr);
1692        if (FA_IS_PROTECTED(*fa) || IS_FA(screen_buf[baddr])) {
1693                operator_error(KL_OERR_PROTECTED);
1694                return;
1695        }
1696        if (formatted) {        /* erase to next field attribute */
1697                do {
1698                        ctlr_add(baddr, CG_null, 0);
1699                        INC_BA(baddr);
1700                } while (!IS_FA(screen_buf[baddr]));
1701                mdt_set(fa);
1702        } else {        /* erase to end of screen */
1703                do {
1704                        ctlr_add(baddr, CG_null, 0);
1705                        INC_BA(baddr);
1706                } while (baddr != 0);
1707        }
1708}
1709
1710
1711/*
1712 * Erase all Input Key.
1713 */
1714/*ARGSUSED*/
1715void
1716EraseInput_action(w, event, params, num_params)
1717Widget w;
1718XEvent *event;
1719String *params;
1720Cardinal *num_params;
1721{
1722        register int    baddr, sbaddr;
1723        unsigned char   fa;
1724        Boolean         f;
1725
1726        action_debug(EraseInput_action, event, params, num_params);
1727        if (IN_ANSI)
1728                return;
1729        if (kybdlock) {
1730                enq_ta(EraseInput_action, CN, CN);
1731                return;
1732        }
1733        if (formatted) {
1734                /* find first field attribute */
1735                baddr = 0;
1736                do {
1737                        if (IS_FA(screen_buf[baddr]))
1738                                break;
1739                        INC_BA(baddr);
1740                } while (baddr != 0);
1741                sbaddr = baddr;
1742                f = False;
1743                do {
1744                        fa = screen_buf[baddr];
1745                        if (!FA_IS_PROTECTED(fa)) {
1746                                mdt_clear(&screen_buf[baddr]);
1747                                do {
1748                                        INC_BA(baddr);
1749                                        if (!f) {
1750                                                cursor_move(baddr);
1751                                                f = True;
1752                                        }
1753                                        if (!IS_FA(screen_buf[baddr])) {
1754                                                ctlr_add(baddr, CG_null, 0);
1755                                        }
1756                                }               while (!IS_FA(screen_buf[baddr]));
1757                        } else {        /* skip protected */
1758                                do {
1759                                        INC_BA(baddr);
1760                                } while (!IS_FA(screen_buf[baddr]));
1761                        }
1762                } while (baddr != sbaddr);
1763                if (!f)
1764                        cursor_move(0);
1765        } else {
1766                ctlr_clear(True);
1767                cursor_move(0);
1768        }
1769}
1770
1771
1772
1773/*
1774 * Delete word key.  Backspaces the cursor until it hits the front of a word,
1775 * deletes characters until it hits a blank or null, and deletes all of these
1776 * but the last.
1777 *
1778 * Which is to say, does a ^W.
1779 */
1780/*ARGSUSED*/
1781void
1782DeleteWord_action(w, event, params, num_params)
1783Widget w;
1784XEvent *event;
1785String *params;
1786Cardinal *num_params;
1787{
1788        register int    baddr, baddr2, front_baddr, back_baddr, end_baddr;
1789        register unsigned char  *fa;
1790
1791        action_debug(DeleteWord_action, event, params, num_params);
1792        if (IN_ANSI) {
1793                net_send_werase();
1794                return;
1795        }
1796        if (kybdlock) {
1797                enq_ta(DeleteWord_action, CN, CN);
1798                return;
1799        }
1800        if (!formatted)
1801                return;
1802
1803        baddr = cursor_addr;
1804        fa = get_field_attribute(baddr);
1805
1806        /* Make sure we're on a modifiable field. */
1807        if (FA_IS_PROTECTED(*fa) || IS_FA(screen_buf[baddr])) {
1808                operator_error(KL_OERR_PROTECTED);
1809                return;
1810        }
1811
1812        /* Search backwards for a non-blank character. */
1813        front_baddr = baddr;
1814        while (screen_buf[front_baddr] == CG_space ||
1815               screen_buf[front_baddr] == CG_null)
1816                DEC_BA(front_baddr);
1817
1818        /* If we ran into the edge of the field without seeing any non-blanks,
1819           there isn't any word to delete; just move the cursor. */
1820        if (IS_FA(screen_buf[front_baddr])) {
1821                cursor_move(front_baddr+1);
1822                return;
1823        }
1824
1825        /* front_baddr is now pointing at a non-blank character.  Now search
1826           for the first blank to the left of that (or the edge of the field),
1827           leaving front_baddr pointing at the the beginning of the word. */
1828        while (!IS_FA(screen_buf[front_baddr]) &&
1829               screen_buf[front_baddr] != CG_space &&
1830               screen_buf[front_baddr] != CG_null)
1831                DEC_BA(front_baddr);
1832        INC_BA(front_baddr);
1833
1834        /* Find the end of the word, searching forward for the edge of the
1835           field or a non-blank. */
1836        back_baddr = front_baddr;
1837        while (!IS_FA(screen_buf[back_baddr]) &&
1838               screen_buf[back_baddr] != CG_space &&
1839               screen_buf[back_baddr] != CG_null)
1840                INC_BA(back_baddr);
1841
1842        /* Find the start of the next word, leaving back_baddr pointing at it
1843           or at the end of the field. */
1844        while (screen_buf[back_baddr] == CG_space ||
1845               screen_buf[back_baddr] == CG_null)
1846                INC_BA(back_baddr);
1847
1848        /* Find the end of the field, leaving end_baddr pointing at the field
1849           attribute of the start of the next field. */
1850        end_baddr = back_baddr;
1851        while (!IS_FA(screen_buf[end_baddr]))
1852                INC_BA(end_baddr);
1853
1854        /* Copy any text to the right of the word we are deleting. */
1855        baddr = front_baddr;
1856        baddr2 = back_baddr;
1857        while (baddr2 != end_baddr) {
1858                ctlr_add(baddr, screen_buf[baddr2], 0);
1859                INC_BA(baddr);
1860                INC_BA(baddr2);
1861        }
1862
1863        /* Insert nulls to pad out the end of the field. */
1864        while (baddr != end_baddr) {
1865                ctlr_add(baddr, CG_null, 0);
1866                INC_BA(baddr);
1867        }
1868
1869        /* Set the MDT and move the cursor. */
1870        mdt_set(fa);
1871        cursor_move(front_baddr);
1872}
1873
1874
1875
1876/*
1877 * Delete field key.  Similar to EraseEOF, but it wipes out the entire field
1878 * rather than just to the right of the cursor, and it leaves the cursor at
1879 * the front of the field.
1880 *
1881 * Which is to say, does a ^U.
1882 */
1883/*ARGSUSED*/
1884void
1885DeleteField_action(w, event, params, num_params)
1886Widget w;
1887XEvent *event;
1888String *params;
1889Cardinal *num_params;
1890{
1891        register int    baddr;
1892        register unsigned char  *fa;
1893
1894        action_debug(DeleteField_action, event, params, num_params);
1895        if (IN_ANSI) {
1896                net_send_kill();
1897                return;
1898        }
1899        if (kybdlock) {
1900                enq_ta(DeleteField_action, CN, CN);
1901                return;
1902        }
1903        if (!formatted)
1904                return;
1905
1906        baddr = cursor_addr;
1907        fa = get_field_attribute(baddr);
1908        if (FA_IS_PROTECTED(*fa) || IS_FA(screen_buf[baddr])) {
1909                operator_error(KL_OERR_PROTECTED);
1910                return;
1911        }
1912        while (!IS_FA(screen_buf[baddr]))
1913                DEC_BA(baddr);
1914        INC_BA(baddr);
1915        cursor_move(baddr);
1916        while (!IS_FA(screen_buf[baddr])) {
1917                ctlr_add(baddr, CG_null, 0);
1918                INC_BA(baddr);
1919        }
1920        mdt_set(fa);
1921}
1922
1923
1924
1925/*
1926 * Set insert mode key.
1927 */
1928/*ARGSUSED*/
1929void
1930Insert_action(w, event, params, num_params)
1931Widget w;
1932XEvent *event;
1933String *params;
1934Cardinal *num_params;
1935{
1936        action_debug(Insert_action, event, params, num_params);
1937        if (IN_ANSI)
1938                return;
1939        if (kybdlock) {
1940                enq_ta(Insert_action, CN, CN);
1941                return;
1942        }
1943        insert_mode(True);
1944}
1945
1946
1947/*
1948 * Toggle insert mode key.
1949 */
1950/*ARGSUSED*/
1951void
1952ToggleInsert_action(w, event, params, num_params)
1953Widget w;
1954XEvent *event;
1955String *params;
1956Cardinal *num_params;
1957{
1958        action_debug(ToggleInsert_action, event, params, num_params);
1959        if (IN_ANSI)
1960                return;
1961        if (kybdlock) {
1962                enq_ta(ToggleInsert_action, CN, CN);
1963                return;
1964        }
1965        if (insert)
1966                insert_mode(False);
1967        else
1968                insert_mode(True);
1969}
1970
1971
1972/*
1973 * Toggle reverse mode key.
1974 */
1975/*ARGSUSED*/
1976void
1977ToggleReverse_action(w, event, params, num_params)
1978Widget w;
1979XEvent *event;
1980String *params;
1981Cardinal *num_params;
1982{
1983        action_debug(ToggleReverse_action, event, params, num_params);
1984        if (IN_ANSI)
1985                return;
1986        if (kybdlock) {
1987                enq_ta(ToggleReverse_action, CN, CN);
1988                return;
1989        }
1990        reverse_mode(!reverse);
1991}
1992
1993
1994/*
1995 * Move the cursor to the first blank after the last nonblank in the
1996 * field, or if the field is full, to the last character in the field.
1997 */
1998/*ARGSUSED*/
1999void
2000FieldEnd_action(w, event, params, num_params)
2001Widget w;
2002XEvent *event;
2003String *params;
2004Cardinal *num_params;
2005{
2006        int     baddr;
2007        unsigned char   *fa, c;
2008        int     last_nonblank = -1;
2009
2010        action_debug(FieldEnd_action, event, params, num_params);
2011        if (IN_ANSI)
2012                return;
2013        if (kybdlock) {
2014                enq_ta(FieldEnd_action, CN, CN);
2015                return;
2016        }
2017        if (!formatted)
2018                return;
2019        baddr = cursor_addr;
2020        fa = get_field_attribute(baddr);
2021        if (fa == &screen_buf[baddr] || FA_IS_PROTECTED(*fa))
2022                return;
2023
2024        baddr = fa - screen_buf;
2025        while (True) {
2026                INC_BA(baddr);
2027                c = screen_buf[baddr];
2028                if (IS_FA(c))
2029                        break;
2030                if (c != CG_null && c != CG_space)
2031                        last_nonblank = baddr;
2032        }
2033
2034        if (last_nonblank == -1) {
2035                baddr = fa - screen_buf;
2036                INC_BA(baddr);
2037        } else {
2038                baddr = last_nonblank;
2039                INC_BA(baddr);
2040                if (IS_FA(screen_buf[baddr]))
2041                        baddr = last_nonblank;
2042        }
2043        cursor_move(baddr);
2044}
2045
2046
2047/*
2048 * X-dependent code starts here.
2049 */
2050
2051/*
2052 * Translate a keymap (from an XQueryKeymap or a KeymapNotify event) into
2053 * a bitmap of Shift, Meta or Alt keys pressed.
2054 */
2055#define key_is_down(kc, bitmap) (kc && ((bitmap)[(kc)/8] & (1<<((kc)%8))))
2056int
2057state_from_keymap(keymap)
2058char keymap[32];
2059{
2060        static Boolean  initted = False;
2061        static KeyCode  kc_Shift_L, kc_Shift_R;
2062        static KeyCode  kc_Meta_L, kc_Meta_R;
2063        static KeyCode  kc_Alt_L, kc_Alt_R;
2064        int     pseudo_state = 0;
2065
2066        if (!initted) {
2067                kc_Shift_L = XKeysymToKeycode(display, XK_Shift_L);
2068                kc_Shift_R = XKeysymToKeycode(display, XK_Shift_R);
2069                kc_Meta_L  = XKeysymToKeycode(display, XK_Meta_L);
2070                kc_Meta_R  = XKeysymToKeycode(display, XK_Meta_R);
2071                kc_Alt_L   = XKeysymToKeycode(display, XK_Alt_L);
2072                kc_Alt_R   = XKeysymToKeycode(display, XK_Alt_R);
2073                initted = True;
2074        }
2075        if (key_is_down(kc_Shift_L, keymap) ||
2076            key_is_down(kc_Shift_R, keymap))
2077                pseudo_state |= ShiftKeyDown;
2078        if (key_is_down(kc_Meta_L, keymap) ||
2079            key_is_down(kc_Meta_R, keymap))
2080                pseudo_state |= MetaKeyDown;
2081        if (key_is_down(kc_Alt_L, keymap) ||
2082            key_is_down(kc_Alt_R, keymap))
2083                pseudo_state |= AltKeyDown;
2084        return pseudo_state;
2085}
2086#undef key_is_down
2087
2088/*
2089 * Process shift keyboard events.  The code has to look for the raw Shift keys,
2090 * rather than using the handy "state" field in the event structure.  This is
2091 * because the event state is the state _before_ the key was pressed or
2092 * released.  This isn't enough information to distinguish between "left
2093 * shift released" and "left shift released, right shift still held down"
2094 * events, for example.
2095 *
2096 * This function is also called as part of Focus event processing.
2097 */
2098/*ARGSUSED*/
2099void
2100Shift_action(w, event, params, num_params)
2101Widget w;
2102XEvent *event;
2103String *params;
2104Cardinal *num_params;
2105{
2106        char    keys[32];
2107
2108        XQueryKeymap(display, keys);
2109        shift_event(state_from_keymap(keys));
2110}
2111
2112/* Add a key to the extended association table. */
2113void
2114add_xk(key, assoc)
2115KeySym key;
2116KeySym assoc;
2117{
2118        int i;
2119
2120        for (i = 0; i < nxk; i++)
2121                if (xk[i].key == key) {
2122                        xk[i].assoc = assoc;
2123                        return;
2124                }
2125        xk = (struct xks *)XtRealloc((XtPointer)xk,
2126            (nxk + 1) * sizeof(struct xks));
2127        xk[nxk].key = key;
2128        xk[nxk].assoc = assoc;
2129        nxk++;
2130}
2131
2132/* Clear the extended association table. */
2133void
2134clear_xks()
2135{
2136        if (nxk) {
2137                XtFree((XtPointer)xk);
2138                xk = (struct xks *)NULL;
2139                nxk = 0;
2140        }
2141}
2142
2143/*
2144 * Translate a keysym name to a keysym, including APL and extended
2145 * characters.
2146 */
2147static KeySym
2148StringToKeysym(s, keytypep)
2149char *s;
2150enum keytype *keytypep;
2151{
2152        KeySym k;
2153        int is_ge;
2154
2155        if (!strncmp(s, "apl_", 4)) {
2156                k = APLStringToKeysym(s, &is_ge);
2157                if (is_ge)
2158                        *keytypep = KT_GE;
2159                else
2160                        *keytypep = KT_STD;
2161        } else {
2162                k = XStringToKeysym(s);
2163                *keytypep = KT_STD;
2164        }
2165        if (k == NoSymbol && strlen(s) == 1)
2166                k = s[0] & 0xff;
2167        if (k < ' ')
2168                k = NoSymbol;
2169        else if (k > 0xff) {
2170                int i;
2171
2172                for (i = 0; i < nxk; i++)
2173                        if (xk[i].key == k) {
2174                                k = xk[i].assoc;
2175                                break;
2176                        }
2177                if (k > 0xff)
2178                        k = NoSymbol;
2179        }
2180        return k;
2181}
2182
2183static Boolean
2184build_composites()
2185{
2186        char *c;
2187        char *cn;
2188        char *ln;
2189        char ksname[3][64];
2190        char junk[2];
2191        KeySym k[3];
2192        enum keytype a[3];
2193        int i;
2194        struct composite *cp;
2195
2196        if (!appres.compose_map) {
2197                xs_warning("%s: No %s defined", action_name(Compose_action),
2198                    ResComposeMap);
2199                return False;
2200        }
2201        cn = xs_buffer("%s.%s", ResComposeMap, appres.compose_map);
2202        if ((c = get_resource(cn)) == NULL) {
2203                xs_warning("%s: Cannot find %s \"%s\"",
2204                    action_name(Compose_action), ResComposeMap,
2205                    appres.compose_map);
2206                return False;
2207        }
2208        XtFree(cn);
2209        while ((ln = strtok(c, "\n"))) {
2210                Boolean okay = True;
2211
2212                c = NULL;
2213                if (sscanf(ln, " %63[^+ \t] + %63[^= \t] =%63s%1s",
2214                    ksname[0], ksname[1], ksname[2], junk) != 3) {
2215                        xs_warning("%s: Invalid syntax: %s",
2216                            action_name(Compose_action), ln);
2217                        continue;
2218                }
2219                for (i = 0; i < 3; i++) {
2220                        k[i] = StringToKeysym(ksname[i], &a[i]);
2221                        if (k[i] == NoSymbol) {
2222                                xs_warning("%s: Invalid KeySym: \"%s\"",
2223                                    action_name(Compose_action), ksname[i]);
2224                                okay = False;
2225                                break;
2226                        }
2227                }
2228                if (!okay)
2229                        continue;
2230                composites = (struct composite *) XtRealloc((char *)composites,
2231                    (n_composites + 1) * sizeof(struct composite));
2232                cp = composites + n_composites;
2233                cp->k1.keysym = k[0];
2234                cp->k1.keytype = a[0];
2235                cp->k2.keysym = k[1];
2236                cp->k2.keytype = a[1];
2237                cp->translation.keysym = k[2];
2238                cp->translation.keytype = a[2];
2239                n_composites++;
2240        }
2241        return True;
2242}
2243
2244/*
2245 * Called by the toolkit when the "Compose" key is pressed.  "Compose" is
2246 * implemented by pressing and releasing three keys: "Compose" and two
2247 * data keys.  For example, "Compose" "s" "s" gives the German "ssharp"
2248 * character, and "Compose" "C", "," gives a capital "C" with a cedilla
2249 * (symbol Ccedilla).
2250 *
2251 * The mechanism breaks down a little when the user presses "Compose" and
2252 * then a non-data key.  Oh well.
2253 */
2254/*ARGSUSED*/
2255void
2256Compose_action(w, event, params, num_params)
2257Widget w;
2258XEvent *event;
2259String *params;
2260Cardinal *num_params;
2261{
2262        action_debug(Compose_action, event, params, num_params);
2263
2264        if (!composites && !build_composites())
2265                return;
2266
2267        if (composing == NONE) {
2268                composing = COMPOSE;
2269                status_compose(True, 0, KT_STD);
2270        }
2271}
2272
2273/*
2274 * Called by the toolkit for any key without special actions.
2275 */
2276/*ARGSUSED*/
2277void
2278Default_action(w, event, params, num_params)
2279Widget w;
2280XEvent *event;
2281String *params;
2282Cardinal *num_params;
2283{
2284        XKeyEvent       *kevent = (XKeyEvent *)event;
2285        char            buf[32];
2286        KeySym          ks;
2287        int             ll;
2288
2289        action_debug(Default_action, event, params, num_params);
2290        ll = XLookupString(kevent, buf, 32, &ks, (XComposeStatus *) 0);
2291        if (ll == 1) {
2292                /* Remap certain control characters. */
2293                switch (buf[0]) {
2294                    case '\t':
2295                        action_internal(Tab_action, IA_DEFAULT, CN, CN);
2296                        break;
2297                    case '\177':
2298                        action_internal(Delete_action, IA_DEFAULT, CN, CN);
2299                        break;
2300                    case '\b':
2301                        action_internal(BackSpace_action, IA_DEFAULT, CN, CN);
2302                        break;
2303                    case '\r':
2304                        action_internal(Enter_action, IA_DEFAULT, CN, CN);
2305                        break;
2306                    case '\n':
2307                        action_internal(Newline_action, IA_DEFAULT, CN, CN);
2308                        break;
2309                    default:
2310                        key_ACharacter((unsigned char) buf[0], KT_STD,
2311                            IA_DEFAULT);
2312                }
2313                return;
2314        }
2315
2316        /* Pick some other reasonable defaults. */
2317        switch (ks) {
2318            case XK_Up:
2319                action_internal(Up_action, IA_DEFAULT, CN, CN);
2320                break;
2321            case XK_Down:
2322                action_internal(Down_action, IA_DEFAULT, CN, CN);
2323                break;
2324            case XK_Left:
2325                action_internal(Left_action, IA_DEFAULT, CN, CN);
2326                break;
2327            case XK_Right:
2328                action_internal(Right_action, IA_DEFAULT, CN, CN);
2329                break;
2330            case XK_Insert:
2331#if defined(XK_KP_Insert) /*[*/
2332            case XK_KP_Insert:
2333#endif /*]*/
2334                action_internal(Insert_action, IA_DEFAULT, CN, CN);
2335                break;
2336            case XK_Delete:
2337                action_internal(Delete_action, IA_DEFAULT, CN, CN);
2338                break;
2339            case XK_Home:
2340                action_internal(Home_action, IA_DEFAULT, CN, CN);
2341                break;
2342            case XK_Tab:
2343                action_internal(Tab_action, IA_DEFAULT, CN, CN);
2344                break;
2345            default:
2346                if (toggled(EVENT_TRACE))
2347                        (void) fprintf(tracef, " %s: Unknown keysym\n",
2348                            action_name(Default_action));
2349                break;
2350        }
2351}
2352
2353
2354/*
2355 * Key action.
2356 */
2357/*ARGSUSED*/
2358void
2359Key_action(w, event, params, num_params)
2360Widget w;
2361XEvent *event;
2362String *params;
2363Cardinal *num_params;
2364{
2365        int i;
2366        KeySym k;
2367        enum keytype keytype;
2368
2369        action_debug(Key_action, event, params, num_params);
2370        for (i = 0; i < *num_params; i++) {
2371                char *s = params[i];
2372
2373                k = StringToKeysym(s, &keytype);
2374                if (k == NoSymbol) {
2375                        popup_an_error("%s: Nonexistent or invalid KeySym: %s",
2376                            action_name(Key_action), s);
2377                        continue;
2378                }
2379                if (k & ~0xff) {
2380                        popup_an_error("%s: Invalid KeySym: %s",
2381                            action_name(Key_action), s);
2382                        continue;
2383                }
2384                key_ACharacter((unsigned char)(k & 0xff), keytype, IA_KEY);
2385        }
2386}
2387
2388/*
2389 * String action.
2390 */
2391/*ARGSUSED*/
2392void
2393String_action(w, event, params, num_params)
2394Widget w;
2395XEvent *event;
2396String *params;
2397Cardinal *num_params;
2398{
2399        int i;
2400        int len = 0;
2401        char *s;
2402
2403        action_debug(String_action, event, params, num_params);
2404
2405        /* Determine the total length of the strings. */
2406        for (i = 0; i < *num_params; i++)
2407                len += strlen(params[i]);
2408        if (!len)
2409                return;
2410
2411        /* Allocate a block of memory and copy them in. */
2412        s = XtMalloc(len + 1);
2413        *s = '\0';
2414        for (i = 0; i < *num_params; i++)
2415                (void) strcat(s, params[i]);
2416
2417        /* Set a pending string. */
2418        ps_set(s);
2419}
2420
2421/*
2422 * Dual-mode action for the "asciicircum" ("^") key:
2423 *  If in ANSI mode, pass through untranslated.
2424 *  If in 3270 mode, translate to "notsign".
2425 */
2426/*ARGSUSED*/
2427void
2428CircumNot_action(w, event, params, num_params)
2429Widget w;
2430XEvent *event;
2431String *params;
2432Cardinal *num_params;
2433{
2434        action_debug(CircumNot_action, event, params, num_params);
2435
2436        if (IN_3270 && composing == NONE)
2437                key_ACharacter(0xac, KT_STD, IA_KEY);
2438        else
2439                key_ACharacter('^', KT_STD, IA_KEY);
2440}
2441
2442/* PA key action for String actions */
2443static void
2444do_pa(n)
2445int n;
2446{
2447        if (n < 1 || n > PA_SZ) {
2448                popup_an_error("Unknown PA key %d", n);
2449                return;
2450        }
2451        if (kybdlock) {
2452                char nn[3];
2453
2454                (void) sprintf(nn, "%d", n);
2455                enq_ta(PA_action, nn, CN);
2456                return;
2457        }
2458        key_AID(pa_xlate[n-1]);
2459}
2460
2461/* PF key action for String actions */
2462static void
2463do_pf(n)
2464int n;
2465{
2466        if (n < 1 || n > PF_SZ) {
2467                popup_an_error("Unknown PF key %d", n);
2468                return;
2469        }
2470        if (kybdlock) {
2471                char nn[3];
2472
2473                (void) sprintf(nn, "%d", n);
2474                enq_ta(PF_action, nn, CN);
2475                return;
2476        }
2477        key_AID(pf_xlate[n-1]);
2478}
2479
2480/*
2481 * Set or clear the keyboard scroll lock.
2482 */
2483void
2484kybd_scroll_lock(lock)
2485Boolean lock;
2486{
2487        if (!IN_3270)
2488                return;
2489        if (lock)
2490                kybdlock_set(KL_SCROLLED, "kybd_scroll_lock");
2491        else
2492                kybdlock_clr(KL_SCROLLED, "kybd_scroll_lock");
2493}
2494
2495/*
2496 * Move the cursor back within the legal paste area.
2497 * Returns a Boolean indicating success.
2498 */
2499static Boolean
2500remargin(lmargin)
2501int lmargin;
2502{
2503        Boolean ever = False;
2504        int baddr, b0;
2505        unsigned char *fa;
2506
2507        baddr = cursor_addr;
2508        while (BA_TO_COL(baddr) < lmargin) {
2509                baddr = ROWCOL_TO_BA(BA_TO_ROW(baddr), lmargin);
2510                if (!ever) {
2511                        b0 = baddr;
2512                        ever = True;
2513                }
2514                fa = get_field_attribute(baddr);
2515                if (fa == &screen_buf[baddr] || FA_IS_PROTECTED(*fa)) {
2516                        baddr = next_unprotected(baddr);
2517                        if (baddr <= b0)
2518                                return False;
2519                }
2520        }
2521
2522        cursor_move(baddr);
2523        return True;
2524}
2525
2526/*
2527 * Pretend that a sequence of keys was entered at the keyboard.
2528 *
2529 * "Pasting" means that the sequence came from the X clipboard.  Returns are
2530 * ignored; newlines mean "move to beginning of next line"; tabs and formfeeds
2531 * become spaces.  Backslashes are not special, but ASCII ESC characters are
2532 * used to signify 3270 Graphic Escapes.
2533 *
2534 * "Not pasting" means that the sequence is a login string specified in the
2535 * hosts file, or a parameter to the String action.  Returns are "move to
2536 * beginning of next line"; newlines mean "Enter AID" and the termination of
2537 * processing the string.  Backslashes are processed as in C.
2538 *
2539 * Returns the number of unprocessed characters.
2540 */
2541int
2542emulate_input(s, len, pasting)
2543char *s;
2544int len;
2545Boolean pasting;
2546{
2547        char c;
2548        enum { BASE, BACKSLASH, BACKX, BACKP, BACKPA, BACKPF, OCTAL, HEX,
2549               XGE } state = BASE;
2550        int literal;
2551        int nc;
2552        enum iaction ia = pasting ? IA_PASTE : IA_STRING;
2553        int orig_addr = cursor_addr;
2554        int orig_col = BA_TO_COL(cursor_addr);
2555        static char dxl[] = "0123456789abcdef";
2556
2557        /*
2558         * In the switch statements below, "break" generally means "consume
2559         * this character," while "continue" means "rescan this character."
2560         */
2561        while (len) {
2562
2563                /*
2564                 * It isn't possible to unlock the keyboard from a string,
2565                 * so if the keyboard is locked, it's fatal
2566                 */
2567                if (kybdlock) {
2568                        if (toggled(EVENT_TRACE))
2569                                (void) fprintf(tracef,
2570                                    "  keyboard locked, string dropped\n");
2571                        return 0;
2572                }
2573
2574                if (pasting && IN_3270) {
2575
2576                        /* Check for cursor wrap to top of screen. */
2577                        if (cursor_addr < orig_addr)
2578                                return len-1;           /* wrapped */
2579
2580                        /* Jump cursor over left margin. */
2581                        if (toggled(MARGINED_PASTE) &&
2582                            BA_TO_COL(cursor_addr) < orig_col) {
2583                                if (!remargin(orig_col))
2584                                        return len-1;
2585                        }
2586                }
2587
2588                c = *s;
2589                switch (state) {
2590                    case BASE:
2591                        switch (c) {
2592                            case '\b':
2593                                action_internal(Left_action, ia, CN, CN);
2594                                continue;
2595                            case '\f':
2596                                if (pasting) {
2597                                        key_ACharacter((unsigned char) ' ',
2598                                            KT_STD, ia);
2599                                } else {
2600                                        action_internal(Clear_action, ia, CN, CN);
2601                                        if (IN_3270)
2602                                                return len-1;
2603                                        else
2604                                                break;
2605                                }
2606                            case '\n':
2607                                if (pasting)
2608                                        action_internal(Newline_action, ia, CN, CN);
2609                                else {
2610                                        action_internal(Enter_action, ia, CN, CN);
2611                                        if (IN_3270)
2612                                                return len-1;
2613                                }
2614                                break;
2615                            case '\r':  /* ignored */
2616                                break;
2617                            case '\t':
2618                                action_internal(Tab_action, ia, CN, CN);
2619                                break;
2620                            case '\\':  /* backslashes are NOT special when pasting */
2621                                if (!pasting)
2622                                        state = BACKSLASH;
2623                                else
2624                                        key_ACharacter((unsigned char) c,
2625                                            KT_STD, ia);
2626                                break;
2627                            case '\033': /* ESC is special only when pasting */
2628                                if (pasting)
2629                                        state = XGE;
2630                                break;
2631                            case '[':   /* APL left bracket */
2632                                if (pasting && appres.apl_mode)
2633                                        key_ACharacter(
2634                                            (unsigned char) XK_Yacute,
2635                                            KT_GE, ia);
2636                                else
2637                                        key_ACharacter((unsigned char) c,
2638                                            KT_STD, ia);
2639                                break;
2640                            case ']':   /* APL right bracket */
2641                                if (pasting && appres.apl_mode)
2642                                        key_ACharacter(
2643                                            (unsigned char) XK_diaeresis,
2644                                            KT_GE, ia);
2645                                else
2646                                        key_ACharacter((unsigned char) c,
2647                                            KT_STD, ia);
2648                                break;
2649                        default:
2650                                key_ACharacter((unsigned char) c, KT_STD,
2651                                    ia);
2652                                break;
2653                        }
2654                        break;
2655                    case BACKSLASH:     /* last character was a backslash */
2656                        switch (c) {
2657                            case 'a':
2658                                popup_an_error("%s: bell not supported",
2659                                    action_name(String_action));
2660                                state = BASE;
2661                                break;
2662                            case 'b':
2663                                action_internal(Left_action, ia, CN, CN);
2664                                state = BASE;
2665                                break;
2666                            case 'f':
2667                                action_internal(Clear_action, ia, CN, CN);
2668                                state = BASE;
2669                                if (IN_3270)
2670                                        return len-1;
2671                                else
2672                                        break;
2673                            case 'n':
2674                                action_internal(Enter_action, ia, CN, CN);
2675                                state = BASE;
2676                                if (IN_3270)
2677                                        return len-1;
2678                                else
2679                                        break;
2680                            case 'p':
2681                                state = BACKP;
2682                                break;
2683                            case 'r':
2684                                action_internal(Newline_action, ia, CN, CN);
2685                                state = BASE;
2686                                break;
2687                            case 't':
2688                                action_internal(Tab_action, ia, CN, CN);
2689                                state = BASE;
2690                                break;
2691                            case 'v':
2692                                popup_an_error("%s: vertical tab not supported",
2693                                    action_name(String_action));
2694                                state = BASE;
2695                                break;
2696                            case 'x':
2697                                state = BACKX;
2698                                break;
2699                            case '\\':
2700                                key_ACharacter((unsigned char) c, KT_STD, ia);
2701                                state = BASE;
2702                                break;
2703                            case '0':
2704                            case '1':
2705                            case '2':
2706                            case '3':
2707                            case '4':
2708                            case '5':
2709                            case '6':
2710                            case '7':
2711                                state = OCTAL;
2712                                literal = 0;
2713                                nc = 0;
2714                                continue;
2715                        default:
2716                                state = BASE;
2717                                continue;
2718                        }
2719                        break;
2720                    case BACKP: /* last two characters were "\p" */
2721                        switch (c) {
2722                            case 'a':
2723                                literal = 0;
2724                                nc = 0;
2725                                state = BACKPA;
2726                                break;
2727                            case 'f':
2728                                literal = 0;
2729                                nc = 0;
2730                                state = BACKPF;
2731                                break;
2732                            default:
2733                                popup_an_error("%s: unknown character after \\p",
2734                                    action_name(String_action));
2735                                state = BASE;
2736                                break;
2737                        }
2738                        break;
2739                    case BACKPF: /* last three characters were "\pf" */
2740                        if (nc < 2 && isdigit(c)) {
2741                                literal = (literal * 10) + (c - '0');
2742                                nc++;
2743                        } else if (!nc) {
2744                                popup_an_error("%s: unknown character after \\pf",
2745                                    action_name(String_action));
2746                                state = BASE;
2747                        } else {
2748                                do_pf(literal);
2749                                if (IN_3270)
2750                                        return len-1;
2751                                state = BASE;
2752                                continue;
2753                        }
2754                        break;
2755                    case BACKPA: /* last three characters were "\pa" */
2756                        if (nc < 1 && isdigit(c)) {
2757                                literal = (literal * 10) + (c - '0');
2758                                nc++;
2759                        } else if (!nc) {
2760                                popup_an_error("%s: unknown character after \\pa",
2761                                    action_name(String_action));
2762                                state = BASE;
2763                        } else {
2764                                do_pa(literal);
2765                                if (IN_3270)
2766                                        return len-1;
2767                                state = BASE;
2768                                continue;
2769                        }
2770                        break;
2771                    case BACKX: /* last two characters were "\x" */
2772                        if (isxdigit(c)) {
2773                                state = HEX;
2774                                literal = 0;
2775                                nc = 0;
2776                                continue;
2777                        } else {
2778                                popup_an_error("%s: missing hex digits after \\x",
2779                                    action_name(String_action));
2780                                state = BASE;
2781                                continue;
2782                        }
2783                    case OCTAL: /* have seen \ and one or more octal digits */
2784                        if (nc < 3 && isdigit(c) && c < '8') {
2785                                literal = (literal * 8) + (strchr(dxl, c) - dxl);
2786                                nc++;
2787                                break;
2788                        } else {
2789                                key_ACharacter((unsigned char) literal, KT_STD,
2790                                    ia);
2791                                state = BASE;
2792                                continue;
2793                        }
2794                    case HEX:   /* have seen \ and one or more hex digits */
2795                        if (nc < 2 && isxdigit(c)) {
2796                                literal = (literal * 16) + (strchr(dxl, tolower(c)) - dxl);
2797                                nc++;
2798                                break;
2799                        } else {
2800                                key_ACharacter((unsigned char) literal, KT_STD,
2801                                    ia);
2802                                state = BASE;
2803                                continue;
2804                        }
2805                    case XGE:   /* have seen ESC */
2806                        key_ACharacter((unsigned char) c, KT_GE, ia);
2807                        state = BASE;
2808                        break;
2809                }
2810                s++;
2811                len--;
2812        }
2813
2814        switch (state) {
2815            case OCTAL:
2816            case HEX:
2817                key_ACharacter((unsigned char) literal, KT_STD, ia);
2818                state = BASE;
2819                break;
2820            case BACKPF:
2821                if (nc > 0) {
2822                        do_pf(literal);
2823                        state = BASE;
2824                }
2825                break;
2826            case BACKPA:
2827                if (nc > 0) {
2828                        do_pa(literal);
2829                        state = BASE;
2830                }
2831                break;
2832            default:
2833                break;
2834        }
2835
2836        if (state != BASE)
2837                popup_an_error("%s: missing data after \\",
2838                    action_name(String_action));
2839
2840        return len;
2841}
2842
2843/*ARGSUSED*/
2844static void
2845paste_callback(w, client_data, selection, type, value, length, format)
2846Widget w;
2847XtPointer client_data;
2848Atom *selection;
2849Atom *type;
2850XtPointer value;
2851unsigned long *length;
2852int *format;
2853{
2854        char *s;
2855        unsigned long len;
2856
2857        if ((value == NULL) || (*length == 0)) {
2858                XtFree(value);
2859
2860                /* Try the next one. */
2861                if (n_pasting > pix)
2862                        XtGetSelectionValue(w, paste_atom[pix++], XA_STRING,
2863                            paste_callback, NULL, paste_time);
2864                return;
2865        }
2866
2867        s = (char *)value;
2868        len = *length;
2869        (void) emulate_input(s, (int) len, True);
2870        n_pasting = 0;
2871
2872        XtFree(value);
2873}
2874
2875void
2876insert_selection_action(w, event, params, num_params)
2877Widget w;
2878XButtonEvent *event;
2879String *params;
2880Cardinal *num_params;
2881{
2882        int     i;
2883        Atom    a;
2884
2885        action_debug(insert_selection_action, (XEvent *)event, params,
2886            num_params);
2887        n_pasting = 0;
2888        for (i = 0; i < *num_params; i++) {
2889                a = XInternAtom(display, params[i], True);
2890                if (a == None) {
2891                        popup_an_error("%s: no atom for selection",
2892                            action_name(insert_selection_action));
2893                        continue;
2894                }
2895                if (n_pasting < NP)
2896                        paste_atom[n_pasting++] = a;
2897        }
2898        pix = 0;
2899        if (n_pasting > pix) {
2900                paste_time = event->time;
2901                XtGetSelectionValue(w, paste_atom[pix++], XA_STRING,
2902                    paste_callback, NULL, paste_time);
2903        }
2904}
2905
2906/*ARGSUSED*/
2907void
2908ignore_action(w, event, params, num_params)
2909Widget w;
2910XButtonEvent *event;
2911String *params;
2912Cardinal *num_params;
2913{
2914        action_debug(ignore_action, (XEvent *)event, params, num_params);
2915}
2916
2917/*
2918 * Set or clear a temporary keymap.
2919 *
2920 *   Keymap(x)          toggle keymap "x" (add "x" to the keymap, or if "x"
2921 *                       was already added, remove it)
2922 *   Keymap()           removes the previous keymap, if any
2923 *   Keymap(None)       removes the previous keymap, if any
2924 */
2925/*ARGSUSED*/
2926void
2927Keymap_action(w, event, params, num_params)
2928Widget w;
2929XEvent *event;
2930String *params;
2931Cardinal *num_params;
2932{
2933        char *k;
2934        char *kmname, *km;
2935        XtTranslations trans;
2936        struct trans_list *t, *prev;
2937#       define TN (struct trans_list *)NULL
2938
2939        action_debug(Keymap_action, event, params, num_params);
2940
2941        if (check_usage(Keymap_action, *num_params, 0, 1) < 0)
2942                return;
2943
2944        if (*num_params == 0 || !strcmp(params[0], "None")) {
2945                struct trans_list *next;
2946
2947                /* Delete all temporary keymaps. */
2948                for (t = temp_keymaps; t != TN; t = next) {
2949                        XtFree((XtPointer)t->name);
2950                        next = t->next;
2951                        XtFree((XtPointer)t);
2952                }
2953                tkm_last = temp_keymaps = TN;
2954                screen_set_temp_keymap((XtTranslations)NULL);
2955                keypad_set_temp_keymap((XtTranslations)NULL);
2956                status_kmap(False);
2957                return;
2958        }
2959
2960        k = params[0];
2961
2962        /* Check for deleting one keymap. */
2963        for (prev = TN, t = temp_keymaps; t != TN; prev = t, t = t->next)
2964                if (!strcmp(k, t->name))
2965                        break;
2966        if (t != TN) {
2967
2968                /* Delete the keymap from the list. */
2969                if (prev != TN)
2970                        prev->next = t->next;
2971                else
2972                        temp_keymaps = t->next;
2973                if (tkm_last == t)
2974                        tkm_last = prev;
2975                XtFree((XtPointer)t->name);
2976                XtFree((XtPointer)t);
2977
2978                /* Rebuild the translation tables from the remaining ones. */
2979                screen_set_temp_keymap((XtTranslations)NULL);
2980                keypad_set_temp_keymap((XtTranslations)NULL);
2981                for (t = temp_keymaps; t != TN; t = t->next) {
2982                        trans = lookup_tt(t->name, CN);
2983                        screen_set_temp_keymap(trans);
2984                        keypad_set_temp_keymap(trans);
2985                }
2986
2987                /* Update the status line. */
2988                if (temp_keymaps == TN)
2989                        status_kmap(False);
2990                return;
2991        }
2992
2993        /* Add a keymap. */
2994
2995        /* Look up the resource. */
2996        kmname = xs_buffer("%s.%s", ResKeymap, k);
2997        km = get_resource(kmname);
2998        XtFree(kmname);
2999        if (km == CN) {
3000                popup_an_error("%s: can't find %s.%s",
3001                    action_name(Keymap_action), ResKeymap, k);
3002                return;
3003        }
3004
3005        /* Update the translation tables. */
3006        trans = lookup_tt(k, km);
3007        XtFree(km);
3008        screen_set_temp_keymap(trans);
3009        keypad_set_temp_keymap(trans);
3010
3011        /* Add it to the list. */
3012        t = (struct trans_list *)XtMalloc(sizeof(*t));
3013        t->name = XtNewString(k);
3014        t->next = TN;
3015        if (tkm_last != TN)
3016                tkm_last->next = t;
3017        else
3018                temp_keymaps = t;
3019        tkm_last = t;
3020
3021        /* Update the status line. */
3022        status_kmap(True);
3023}
3024#undef TN
3025
3026/*
3027 * Set up the cursor and input field for command input.
3028 * Returns the length of the input field, or 0 if there is no field
3029 * to set up.
3030 */
3031int
3032kybd_prime()
3033{
3034        int baddr;
3035        register unsigned char *fa;
3036        int len = 0;
3037
3038        /*
3039         * No point in trying if the screen isn't formatted, the keyboard
3040         * is locked, or we aren't in 3270 mode.
3041         */
3042        if (!formatted || kybdlock || !IN_3270)
3043                return 0;
3044
3045        fa = get_field_attribute(cursor_addr);
3046        if (IS_FA(screen_buf[cursor_addr]) || FA_IS_PROTECTED(*fa)) {
3047                /*
3048                 * The cursor is not in an unprotected field.  Find the
3049                 * next one.
3050                 */
3051                baddr = next_unprotected(cursor_addr);
3052
3053                /* If there isn't any, give up. */
3054                if (!baddr)
3055                        return 0;
3056
3057                /* Move the cursor there. */
3058        } else {
3059                /* Already in an unprotected field.  Find its start. */
3060                baddr = cursor_addr;
3061                while (!IS_FA(screen_buf[baddr])) {
3062                        DEC_BA(baddr);
3063                }
3064                INC_BA(baddr);
3065        }
3066
3067        /* Move the cursor to the beginning of the field. */
3068        cursor_move(baddr);
3069
3070        /* Erase it. */
3071        while (!IS_FA(screen_buf[baddr])) {
3072                ctlr_add(baddr, 0, 0);
3073                len++;
3074                INC_BA(baddr);
3075        }
3076
3077        /* Return the field length. */
3078        return len;
3079}
Note: See TracBrowser for help on using the repository browser.