/* * Modifications Copyright 1993, 1994, 1995, 1996, 1999, * 2000, 2001, 2002, 2004, 2005 by Paul Mattes. * Original X11 Port Copyright 1990 by Jeff Sparkes. * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * Copyright 1989 by Georgia Tech Research Corporation, Atlanta, GA 30332. * All Rights Reserved. GTRC hereby grants public use of this software. * Derivative works based on this software must incorporate this copyright * notice. * * x3270, c3270, s3270 and tcl3270 are distributed in the hope that they will * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE * for more details. */ /* * kybd.c * This module handles the keyboard for the 3270 emulator. */ #include "globals.h" #if defined(X3270_DISPLAY) /*[*/ #include #endif #define XK_3270 #if defined(X3270_APL) /*[*/ #define XK_APL #endif /*]*/ #include #include #include "3270ds.h" #include "appres.h" #include "ctlr.h" #include "resources.h" #include "actionsc.h" #include "ansic.h" #include "aplc.h" #include "ctlrc.h" #include "ftc.h" #include "hostc.h" #include "idlec.h" #include "keymapc.h" #include "keypadc.h" #include "kybdc.h" #include "macrosc.h" #include "popupsc.h" #include "printc.h" #include "screenc.h" #if defined(X3270_DISPLAY) /*[*/ #include "selectc.h" #endif /*]*/ #include "statusc.h" #include "tablesc.h" #include "telnetc.h" #include "togglesc.h" #include "trace_dsc.h" #include "utilc.h" #if defined(X3270_DBCS) /*[*/ #include "widec.h" #endif /*]*/ /* Statics */ static enum { NONE, COMPOSE, FIRST } composing = NONE; static unsigned char pf_xlate[] = { AID_PF1, AID_PF2, AID_PF3, AID_PF4, AID_PF5, AID_PF6, AID_PF7, AID_PF8, AID_PF9, AID_PF10, AID_PF11, AID_PF12, AID_PF13, AID_PF14, AID_PF15, AID_PF16, AID_PF17, AID_PF18, AID_PF19, AID_PF20, AID_PF21, AID_PF22, AID_PF23, AID_PF24 }; static unsigned char pa_xlate[] = { AID_PA1, AID_PA2, AID_PA3 }; #define PF_SZ (sizeof(pf_xlate)/sizeof(pf_xlate[0])) #define PA_SZ (sizeof(pa_xlate)/sizeof(pa_xlate[0])) static unsigned long unlock_id; #define UNLOCK_MS 350 static Boolean key_Character(int code, Boolean with_ge, Boolean pasting); static Boolean flush_ta(void); static void key_AID(unsigned char aid_code); static void kybdlock_set(unsigned int bits, const char *cause); static KeySym MyStringToKeysym(char *s, enum keytype *keytypep); #if defined(X3270_DBCS) /*[*/ static Boolean key_WCharacter(unsigned char code[]); #endif /*]*/ static int nxk = 0; static struct xks { KeySym key; KeySym assoc; } *xk; static Boolean insert = False; /* insert mode */ static Boolean reverse = False; /* reverse-input mode */ /* Globals */ unsigned int kybdlock = KL_NOT_CONNECTED; unsigned char aid = AID_NO; /* current attention ID */ /* Composite key mappings. */ struct akeysym { KeySym keysym; enum keytype keytype; }; static struct akeysym cc_first; static struct composite { struct akeysym k1, k2; struct akeysym translation; } *composites = NULL; static int n_composites = 0; #define ak_eq(k1, k2) (((k1).keysym == (k2).keysym) && \ ((k1).keytype == (k2).keytype)) static struct ta { struct ta *next; XtActionProc fn; char *parm1; char *parm2; } *ta_head = (struct ta *) NULL, *ta_tail = (struct ta *) NULL; static char dxl[] = "0123456789abcdef"; #define FROM_HEX(c) (strchr(dxl, tolower(c)) - dxl) extern Widget *screen; #define KYBDLOCK_IS_OERR (kybdlock && !(kybdlock & ~KL_OERR_MASK)) /* * Put an action on the typeahead queue. */ static void enq_ta(XtActionProc fn, char *parm1, char *parm2) { struct ta *ta; /* If no connection, forget it. */ if (!CONNECTED) { trace_event(" dropped (not connected)\n"); return; } /* If operator error, complain and drop it. */ if (kybdlock & KL_OERR_MASK) { ring_bell(); trace_event(" dropped (operator error)\n"); return; } /* If scroll lock, complain and drop it. */ if (kybdlock & KL_SCROLLED) { ring_bell(); trace_event(" dropped (scrolled)\n"); return; } /* If typeahead disabled, complain and drop it. */ if (!appres.typeahead) { trace_event(" dropped (no typeahead)\n"); return; } ta = (struct ta *) Malloc(sizeof(*ta)); ta->next = (struct ta *) NULL; ta->fn = fn; ta->parm1 = ta->parm2 = CN; if (parm1) { ta->parm1 = NewString(parm1); if (parm2) ta->parm2 = NewString(parm2); } if (ta_head) ta_tail->next = ta; else { ta_head = ta; status_typeahead(True); } ta_tail = ta; trace_event(" action queued (kybdlock 0x%x)\n", kybdlock); } /* * Execute an action from the typeahead queue. */ Boolean run_ta(void) { struct ta *ta; if (kybdlock || (ta = ta_head) == (struct ta *)NULL) return False; if ((ta_head = ta->next) == (struct ta *)NULL) { ta_tail = (struct ta *)NULL; status_typeahead(False); } action_internal(ta->fn, IA_TYPEAHEAD, ta->parm1, ta->parm2); Free(ta->parm1); Free(ta->parm2); Free(ta); return True; } /* * Flush the typeahead queue. * Returns whether or not anything was flushed. */ static Boolean flush_ta(void) { struct ta *ta, *next; Boolean any = False; for (ta = ta_head; ta != (struct ta *) NULL; ta = next) { Free(ta->parm1); Free(ta->parm2); next = ta->next; Free(ta); any = True; } ta_head = ta_tail = (struct ta *) NULL; status_typeahead(False); return any; } /* Set bits in the keyboard lock. */ static void kybdlock_set(unsigned int bits, const char *cause unused) { unsigned int n; n = kybdlock | bits; if (n != kybdlock) { #if defined(KYBDLOCK_TRACE) /*[*/ trace_event(" %s: kybdlock |= 0x%04x, 0x%04x -> 0x%04x\n", cause, bits, kybdlock, n); #endif /*]*/ kybdlock = n; status_kybdlock(); } } /* Clear bits in the keyboard lock. */ void kybdlock_clr(unsigned int bits, const char *cause unused) { unsigned int n; n = kybdlock & ~bits; if (n != kybdlock) { #if defined(KYBDLOCK_TRACE) /*[*/ trace_event(" %s: kybdlock &= ~0x%04x, 0x%04x -> 0x%04x\n", cause, bits, kybdlock, n); #endif /*]*/ kybdlock = n; status_kybdlock(); } } /* * Set or clear enter-inhibit mode. */ void kybd_inhibit(Boolean inhibit) { if (inhibit) { kybdlock_set(KL_ENTER_INHIBIT, "kybd_inhibit"); if (kybdlock == KL_ENTER_INHIBIT) status_reset(); } else { kybdlock_clr(KL_ENTER_INHIBIT, "kybd_inhibit"); if (!kybdlock) status_reset(); } } /* * Called when a host connects or disconnects. */ static void kybd_connect(Boolean connected) { if (kybdlock & KL_DEFERRED_UNLOCK) RemoveTimeOut(unlock_id); kybdlock_clr(-1, "kybd_connect"); if (connected) { /* Wait for any output or a WCC(restore) from the host */ kybdlock_set(KL_AWAITING_FIRST, "kybd_connect"); } else { kybdlock_set(KL_NOT_CONNECTED, "kybd_connect"); (void) flush_ta(); } } /* * Called when we switch between 3270 and ANSI modes. */ static void kybd_in3270(Boolean in3270 unused) { if (kybdlock & KL_DEFERRED_UNLOCK) RemoveTimeOut(unlock_id); kybdlock_clr(~KL_AWAITING_FIRST, "kybd_in3270"); /* There might be a macro pending. */ if (CONNECTED) ps_process(); } /* * Called to initialize the keyboard logic. */ void kybd_init(void) { /* Register interest in connect and disconnect events. */ register_schange(ST_CONNECT, kybd_connect); register_schange(ST_3270_MODE, kybd_in3270); } /* * Toggle insert mode. */ static void insert_mode(Boolean on) { insert = on; status_insert_mode(on); } /* * Toggle reverse mode. */ static void reverse_mode(Boolean on) { reverse = on; status_reverse_mode(on); } /* * Lock the keyboard because of an operator error. */ static void operator_error(int error_type) { if (sms_redirect()) popup_an_error("Keyboard locked"); if (appres.oerr_lock || sms_redirect()) { status_oerr(error_type); mcursor_locked(); kybdlock_set((unsigned int)error_type, "operator_error"); (void) flush_ta(); } else { ring_bell(); } } /* * Handle an AID (Attention IDentifier) key. This is the common stuff that * gets executed for all AID keys (PFs, PAs, Clear and etc). */ static void key_AID(unsigned char aid_code) { #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { register unsigned i; if (aid_code == AID_ENTER) { net_sendc('\r'); return; } for (i = 0; i < PF_SZ; i++) if (aid_code == pf_xlate[i]) { ansi_send_pf(i+1); return; } for (i = 0; i < PA_SZ; i++) if (aid_code == pa_xlate[i]) { ansi_send_pa(i+1); return; } return; } #endif /*]*/ if (IN_SSCP) { if (kybdlock & KL_OIA_MINUS) return; if (aid_code != AID_ENTER && aid_code != AID_CLEAR) { status_minus(); kybdlock_set(KL_OIA_MINUS, "key_AID"); return; } } if (IN_SSCP && aid_code == AID_ENTER) { /* Act as if the host had written our input. */ buffer_addr = cursor_addr; } if (!IN_SSCP || aid_code != AID_CLEAR) { status_twait(); mcursor_waiting(); insert_mode(False); kybdlock_set(KL_OIA_TWAIT | KL_OIA_LOCKED, "key_AID"); } aid = aid_code; ctlr_read_modified(aid, False); ticking_start(False); status_ctlr_done(); } void PF_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { unsigned k; action_debug(PF_action, event, params, num_params); if (check_usage(PF_action, *num_params, 1, 1) < 0) return; k = atoi(params[0]); if (k < 1 || k > PF_SZ) { popup_an_error("%s: Invalid argument '%s'", action_name(PF_action), params[0]); cancel_if_idle_command(); return; } reset_idle_timer(); if (kybdlock & KL_OIA_MINUS) return; else if (kybdlock) enq_ta(PF_action, params[0], CN); else key_AID(pf_xlate[k-1]); } void PA_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { unsigned k; action_debug(PA_action, event, params, num_params); if (check_usage(PA_action, *num_params, 1, 1) < 0) return; k = atoi(params[0]); if (k < 1 || k > PA_SZ) { popup_an_error("%s: Invalid argument '%s'", action_name(PA_action), params[0]); cancel_if_idle_command(); return; } reset_idle_timer(); if (kybdlock & KL_OIA_MINUS) return; else if (kybdlock) enq_ta(PA_action, params[0], CN); else key_AID(pa_xlate[k-1]); } /* * ATTN key, per RFC 2355. Sends IP, regardless. */ void Attn_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Attn_action, event, params, num_params); if (!IN_3270) return; reset_idle_timer(); net_interrupt(); } /* * IAC IP, which works for 5250 System Request and interrupts the program * on an AS/400, even when the keyboard is locked. * * This is now the same as the Attn action. */ void Interrupt_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Interrupt_action, event, params, num_params); if (!IN_3270) return; reset_idle_timer(); net_interrupt(); } /* * Prepare for an insert of 'count' bytes. * Returns True if the insert is legal, False otherwise. */ static Boolean ins_prep(int faddr, int baddr, int count) { int next_faddr; int xaddr; int need; int ntb; int tb_start = -1; int copy_len; /* Find the end of the field. */ if (faddr == -1) { /* Unformatted. Use the end of the line. */ next_faddr = (((baddr / COLS) + 1) * COLS) % (ROWS*COLS); } else { next_faddr = faddr; INC_BA(next_faddr); while (next_faddr != faddr && !ea_buf[next_faddr].fa) { INC_BA(next_faddr); } } /* Are there enough NULLs or trailing blanks available? */ xaddr = baddr; need = count; ntb = 0; while (need && (xaddr != next_faddr)) { if (ea_buf[xaddr].cc == EBC_null) need--; else if (toggled(BLANK_FILL) && (ea_buf[xaddr].cc == EBC_space)) { if (tb_start == -1) tb_start = xaddr; ntb++; } else { tb_start = -1; ntb = 0; } INC_BA(xaddr); } #if defined(_ST) /*[*/ printf("need %d at %d, tb_start at %d\n", count, baddr, tb_start); #endif /*]*/ if (need - ntb > 0) { operator_error(KL_OERR_OVERFLOW); return False; } /* * Shift the buffer to the right until we've consumed the available * (and needed) NULLs. */ need = count; xaddr = baddr; while (need && (xaddr != next_faddr)) { int n_nulls = 0; int first_null = -1; while (need && ((ea_buf[xaddr].cc == EBC_null) || (tb_start >= 0 && xaddr >= tb_start))) { need--; n_nulls++; if (first_null == -1) first_null = xaddr; INC_BA(xaddr); } if (n_nulls) { int to; /* Shift right n_nulls worth. */ copy_len = first_null - baddr; if (copy_len < 0) copy_len += ROWS*COLS; to = (baddr + n_nulls) % (ROWS*COLS); #if defined(_ST) /*[*/ printf("found %d NULLs at %d\n", n_nulls, first_null); printf("copying %d from %d to %d\n", copy_len, to, first_null); #endif /*]*/ if (copy_len) ctlr_wrapping_memmove(to, baddr, copy_len); } INC_BA(xaddr); } return True; } #define GE_WFLAG 0x100 #define PASTE_WFLAG 0x200 static void key_Character_wrapper(Widget w unused, XEvent *event unused, String *params, Cardinal *num_params unused) { int code; Boolean with_ge = False; Boolean pasting = False; code = atoi(params[0]); if (code & GE_WFLAG) { with_ge = True; code &= ~GE_WFLAG; } if (code & PASTE_WFLAG) { pasting = True; code &= ~PASTE_WFLAG; } trace_event(" %s -> Key(%s\"%s\")\n", ia_name[(int) ia_cause], with_ge ? "GE " : "", ctl_see((int) ebc2asc[code])); (void) key_Character(code, with_ge, pasting); } /* * Handle an ordinary displayable character key. Lots of stuff to handle * insert-mode, protected fields and etc. */ static Boolean key_Character(int code, Boolean with_ge, Boolean pasting) { register int baddr, faddr, xaddr; register unsigned char fa; enum dbcs_why why; if (kybdlock) { char codename[64]; (void) sprintf(codename, "%d", code | (with_ge ? GE_WFLAG : 0) | (pasting ? PASTE_WFLAG : 0)); enq_ta(key_Character_wrapper, codename, CN); return False; } baddr = cursor_addr; faddr = find_field_attribute(baddr); fa = get_field_attribute(baddr); if (ea_buf[baddr].fa || FA_IS_PROTECTED(fa)) { operator_error(KL_OERR_PROTECTED); return False; } if (appres.numeric_lock && FA_IS_NUMERIC(fa) && !((code >= EBC_0 && code <= EBC_9) || code == EBC_minus || code == EBC_period)) { operator_error(KL_OERR_NUMERIC); return False; } /* Can't put an SBCS in a DBCS field. */ if (ea_buf[faddr].cs == CS_DBCS) { operator_error(KL_OERR_DBCS); return False; } /* If it's an SI (end of DBCS subfield), move over one position. */ if (ea_buf[baddr].cc == EBC_si) { INC_BA(baddr); if (baddr == faddr) { operator_error(KL_OERR_OVERFLOW); return False; } } /* Add the character. */ if (ea_buf[baddr].cc == EBC_so) { if (insert) { if (!ins_prep(faddr, baddr, 1)) return False; } else { Boolean was_si = False; /* * Overwriting an SO (start of DBCS subfield). * If it's followed by an SI, replace the SO/SI * pair with x/space. If not, replace it and * the following DBCS character with * x/space/SO. */ xaddr = baddr; INC_BA(xaddr); was_si = (ea_buf[xaddr].cc == EBC_si); ctlr_add(xaddr, EBC_space, CS_BASE); ctlr_add_fg(xaddr, 0); #if defined(X3270_ANSI) /*[*/ ctlr_add_bg(xaddr, 0); #endif /*]*/ if (!was_si) { INC_BA(xaddr); ctlr_add(xaddr, EBC_so, CS_BASE); ctlr_add_fg(xaddr, 0); #if defined(X3270_ANSI) /*[*/ ctlr_add_bg(xaddr, 0); #endif /*]*/ } } } else switch (ctlr_lookleft_state(baddr, &why)) { case DBCS_RIGHT: DEC_BA(baddr); /* fall through... */ case DBCS_LEFT: if (why == DBCS_ATTRIBUTE) { if (insert) { if (!ins_prep(faddr, baddr, 1)) return False; } else { /* * Replace single DBCS char with * x/space. */ xaddr = baddr; INC_BA(xaddr); ctlr_add(xaddr, EBC_space, CS_BASE); ctlr_add_fg(xaddr, 0); ctlr_add_gr(xaddr, 0); } } else { Boolean was_si; if (insert) { /* * Inserting SBCS into a DBCS subfield. * If this is the first position, we * can just insert one character in * front of the SO. Otherwise, we'll * need room for SI (to end subfield), * the character, and SO (to begin the * subfield again). */ xaddr = baddr; DEC_BA(xaddr); if (ea_buf[xaddr].cc == EBC_so) { DEC_BA(baddr); if (!ins_prep(faddr, baddr, 1)) return False; } else { if (!ins_prep(faddr, baddr, 3)) return False; xaddr = baddr; ctlr_add(xaddr, EBC_si, CS_BASE); ctlr_add_fg(xaddr, 0); ctlr_add_gr(xaddr, 0); INC_BA(xaddr); INC_BA(baddr); INC_BA(xaddr); ctlr_add(xaddr, EBC_so, CS_BASE); ctlr_add_fg(xaddr, 0); ctlr_add_gr(xaddr, 0); } } else { /* Overwriting part of a subfield. */ xaddr = baddr; ctlr_add(xaddr, EBC_si, CS_BASE); ctlr_add_fg(xaddr, 0); ctlr_add_gr(xaddr, 0); INC_BA(xaddr); INC_BA(baddr); INC_BA(xaddr); was_si = (ea_buf[xaddr].cc == EBC_si); ctlr_add(xaddr, EBC_space, CS_BASE); ctlr_add_fg(xaddr, 0); ctlr_add_gr(xaddr, 0); if (!was_si) { INC_BA(xaddr); ctlr_add(xaddr, EBC_so, CS_BASE); ctlr_add_fg(xaddr, 0); ctlr_add_gr(xaddr, 0); } } } break; default: case DBCS_NONE: if (insert && !ins_prep(faddr, baddr, 1)) return False; break; } ctlr_add(baddr, (unsigned char)code, (unsigned char)(with_ge ? CS_GE : 0)); ctlr_add_fg(baddr, 0); ctlr_add_gr(baddr, 0); INC_BA(baddr); /* Replace leading nulls with blanks, if desired. */ if (formatted && toggled(BLANK_FILL)) { register int baddr_fill = baddr; DEC_BA(baddr_fill); while (baddr_fill != faddr) { /* Check for backward line wrap. */ if ((baddr_fill % COLS) == COLS - 1) { Boolean aborted = True; register int baddr_scan = baddr_fill; /* * Check the field within the preceeding line * for NULLs. */ while (baddr_scan != faddr) { if (ea_buf[baddr_scan].cc != EBC_null) { aborted = False; break; } if (!(baddr_scan % COLS)) break; DEC_BA(baddr_scan); } if (aborted) break; } if (ea_buf[baddr_fill].cc == EBC_null) ctlr_add(baddr_fill, EBC_space, 0); DEC_BA(baddr_fill); } } mdt_set(cursor_addr); /* * Implement auto-skip, and don't land on attribute bytes. * This happens for all pasted data (even DUP), and for all * keyboard-generated data except DUP. */ if (pasting || (code != EBC_dup)) { while (ea_buf[baddr].fa) { if (FA_IS_SKIP(ea_buf[baddr].fa)) baddr = next_unprotected(baddr); else INC_BA(baddr); } cursor_move(baddr); } (void) ctlr_dbcs_postprocess(); return True; } #if defined(X3270_DBCS) /*[*/ static void key_WCharacter_wrapper(Widget w unused, XEvent *event unused, String *params, Cardinal *num_params unused) { int code; unsigned char codebuf[2]; code = atoi(params[0]); trace_event(" %s -> Key(0x%04x)\n", ia_name[(int) ia_cause], code); codebuf[0] = (code >> 8) & 0xff; codebuf[1] = code & 0xff; (void) key_WCharacter(codebuf); } /* * Handle a DBCS character. */ static Boolean key_WCharacter(unsigned char code[]) { int baddr; register unsigned char fa; int faddr; enum dbcs_state d; int xaddr; Boolean done = False; extern unsigned char reply_mode; /* XXX */ if (kybdlock) { char codename[64]; (void) sprintf(codename, "%d", (code[0] << 8) | code[1]); enq_ta(key_WCharacter_wrapper, codename, CN); return False; } /* In DBCS mode? */ if (!dbcs) { trace_event("DBCS character received when not in DBCS mode, " "ignoring.\n"); return True; } #if defined(X3270_ANSI) /*[*/ /* In ANSI mode? */ if (IN_ANSI) { char mb[16]; dbcs_to_mb(code[0], code[1], mb); net_sends(mb); return True; } #endif /*]*/ baddr = cursor_addr; fa = get_field_attribute(baddr); faddr = find_field_attribute(baddr); /* Protected? */ if (ea_buf[baddr].fa || FA_IS_PROTECTED(fa)) { operator_error(KL_OERR_PROTECTED); return False; } /* Numeric? */ if (appres.numeric_lock && FA_IS_NUMERIC(fa)) { operator_error(KL_OERR_NUMERIC); return False; } /* Check for insert mode. */ /* * Figure our what to do based on the DBCS state of the buffer. * Leaves baddr pointing to the next unmodified position. */ switch (d = ctlr_dbcs_state(baddr)) { case DBCS_RIGHT: case DBCS_RIGHT_WRAP: /* Back up one position and process it as a LEFT. */ DEC_BA(baddr); /* fall through... */ case DBCS_LEFT: case DBCS_LEFT_WRAP: /* Overwrite the existing character. */ if (insert) { if (!ins_prep(faddr, baddr, 2)) { return False; } } ctlr_add(baddr, code[0], ea_buf[baddr].cs); INC_BA(baddr); ctlr_add(baddr, code[1], ea_buf[baddr].cs); INC_BA(baddr); done = True; break; case DBCS_SB: /* Back up one position and process it as an SI. */ DEC_BA(baddr); /* fall through... */ case DBCS_SI: /* Make sure there's room to extend the subfield. */ /* XXX: Make sure we don't create SI, SI. */ if (insert) { if (!ins_prep(faddr, baddr, 2)) { return False; } } else { xaddr = baddr; INC_BA(xaddr); if (ea_buf[xaddr].fa) break; INC_BA(xaddr); if (ea_buf[xaddr].fa) break; } ctlr_add(baddr, code[0], ea_buf[baddr].cs); INC_BA(baddr); ctlr_add(baddr, code[1], ea_buf[baddr].cs); INC_BA(baddr); ctlr_add(baddr, EBC_si, ea_buf[baddr].cs); done = True; break; case DBCS_DEAD: break; case DBCS_NONE: /* Can we add SO/SI to this field? */ if (ea_buf[faddr].ic) { /* Is there room? */ if (insert) { if (!ins_prep(faddr, baddr, 4)) { return False; } } else { xaddr = baddr; INC_BA(xaddr); /* C0 */ if (ea_buf[xaddr].fa) break; INC_BA(xaddr); /* C1 */ if (ea_buf[xaddr].fa) break; INC_BA(xaddr); /* SI */ if (ea_buf[xaddr].fa) break; } /* Yes, add it. */ ctlr_add(baddr, EBC_so, ea_buf[baddr].cs); INC_BA(baddr); ctlr_add(baddr, code[0], ea_buf[baddr].cs); INC_BA(baddr); ctlr_add(baddr, code[1], ea_buf[baddr].cs); INC_BA(baddr); ctlr_add(baddr, EBC_si, ea_buf[baddr].cs); done = True; } else if (reply_mode == SF_SRM_CHAR) { /* Use the character attribute. */ if (insert) { if (!ins_prep(faddr, baddr, 2)) { return False; } } else { xaddr = baddr; INC_BA(xaddr); if (ea_buf[xaddr].fa) break; } ctlr_add(baddr, code[0], CS_DBCS); INC_BA(baddr); ctlr_add(baddr, code[1], CS_DBCS); INC_BA(baddr); done = True; } break; } if (done) { /* Implement blank fill mode. */ if (toggled(BLANK_FILL)) { xaddr = faddr; INC_BA(xaddr); while (xaddr != baddr) { if (ea_buf[xaddr].cc == EBC_null) ctlr_add(xaddr, EBC_space, CS_BASE); else break; INC_BA(xaddr); } } mdt_set(cursor_addr); /* Implement auto-skip. */ while (ea_buf[baddr].fa) { if (FA_IS_SKIP(ea_buf[baddr].fa)) baddr = next_unprotected(baddr); else INC_BA(baddr); } cursor_move(baddr); (void) ctlr_dbcs_postprocess(); return True; } else { operator_error(KL_OERR_DBCS); return False; } } #endif /*]*/ /* * Handle an ordinary character key, given an ASCII code. */ static void key_ACharacter(unsigned char c, enum keytype keytype, enum iaction cause) { register int i; struct akeysym ak; ak.keysym = c; ak.keytype = keytype; switch (composing) { case NONE: break; case COMPOSE: for (i = 0; i < n_composites; i++) if (ak_eq(composites[i].k1, ak) || ak_eq(composites[i].k2, ak)) break; if (i < n_composites) { cc_first.keysym = c; cc_first.keytype = keytype; composing = FIRST; status_compose(True, c, keytype); } else { ring_bell(); composing = NONE; status_compose(False, 0, KT_STD); } return; case FIRST: composing = NONE; status_compose(False, 0, KT_STD); for (i = 0; i < n_composites; i++) if ((ak_eq(composites[i].k1, cc_first) && ak_eq(composites[i].k2, ak)) || (ak_eq(composites[i].k1, ak) && ak_eq(composites[i].k2, cc_first))) break; if (i < n_composites) { c = composites[i].translation.keysym; keytype = composites[i].translation.keytype; } else { ring_bell(); return; } break; } trace_event(" %s -> Key(\"%s\")\n", ia_name[(int) cause], ctl_see((int) c)); if (IN_3270) { if (c < ' ') { trace_event(" dropped (control char)\n"); return; } (void) key_Character((int) asc2ebc[c], keytype == KT_GE, False); } #if defined(X3270_ANSI) /*[*/ else if (IN_ANSI) { net_sendc((char) c); } #endif /*]*/ else { trace_event(" dropped (not connected)\n"); } } /* * Simple toggles. */ #if defined(X3270_DISPLAY) /*[*/ void AltCursor_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(AltCursor_action, event, params, num_params); reset_idle_timer(); do_toggle(ALT_CURSOR); } #endif /*]*/ void MonoCase_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(MonoCase_action, event, params, num_params); reset_idle_timer(); do_toggle(MONOCASE); } /* * Flip the display left-to-right */ void Flip_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Flip_action, event, params, num_params); reset_idle_timer(); screen_flip(); } /* * Tab forward to next field. */ void Tab_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Tab_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (KYBDLOCK_IS_OERR) { kybdlock_clr(KL_OERR_MASK, "Tab"); status_reset(); } else { enq_ta(Tab_action, CN, CN); return; } } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { net_sendc('\t'); return; } #endif /*]*/ cursor_move(next_unprotected(cursor_addr)); } /* * Tab backward to previous field. */ void BackTab_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr, nbaddr; int sbaddr; action_debug(BackTab_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (KYBDLOCK_IS_OERR) { kybdlock_clr(KL_OERR_MASK, "BackTab"); status_reset(); } else { enq_ta(BackTab_action, CN, CN); return; } } if (!IN_3270) return; baddr = cursor_addr; DEC_BA(baddr); if (ea_buf[baddr].fa) /* at bof */ DEC_BA(baddr); sbaddr = baddr; while (True) { nbaddr = baddr; INC_BA(nbaddr); if (ea_buf[baddr].fa && !FA_IS_PROTECTED(ea_buf[baddr].fa) && !ea_buf[nbaddr].fa) break; DEC_BA(baddr); if (baddr == sbaddr) { cursor_move(0); return; } } INC_BA(baddr); cursor_move(baddr); } /* * Deferred keyboard unlock. */ static void defer_unlock(void) { kybdlock_clr(KL_DEFERRED_UNLOCK, "defer_unlock"); status_reset(); if (CONNECTED) ps_process(); } /* * Reset keyboard lock. */ void do_reset(Boolean explicit) { /* * If explicit (from the keyboard) and there is typeahead or * a half-composed key, simply flush it. */ if (explicit #if defined(X3270_FT) /*[*/ || ft_state != FT_NONE #endif /*]*/ ) { Boolean half_reset = False; if (flush_ta()) half_reset = True; if (composing != NONE) { composing = NONE; status_compose(False, 0, KT_STD); half_reset = True; } if (half_reset) return; } /* Always clear insert mode. */ insert_mode(False); /* Otherwise, if not connect, reset is a no-op. */ if (!CONNECTED) return; /* * Remove any deferred keyboard unlock. We will either unlock the * keyboard now, or want to defer further into the future. */ if (kybdlock & KL_DEFERRED_UNLOCK) RemoveTimeOut(unlock_id); /* * If explicit (from the keyboard), unlock the keyboard now. * Otherwise (from the host), schedule a deferred keyboard unlock. */ if (explicit #if defined(X3270_FT) /*[*/ || ft_state != FT_NONE #endif /*]*/ || (!appres.unlock_delay && !sms_in_macro())) { kybdlock_clr(-1, "do_reset"); } else if (kybdlock & (KL_DEFERRED_UNLOCK | KL_OIA_TWAIT | KL_OIA_LOCKED | KL_AWAITING_FIRST)) { kybdlock_clr(~KL_DEFERRED_UNLOCK, "do_reset"); kybdlock_set(KL_DEFERRED_UNLOCK, "do_reset"); unlock_id = AddTimeOut(UNLOCK_MS, defer_unlock); } /* Clean up other modes. */ status_reset(); mcursor_normal(); composing = NONE; status_compose(False, 0, KT_STD); } void Reset_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Reset_action, event, params, num_params); reset_idle_timer(); do_reset(True); } /* * Move to first unprotected field on screen. */ void Home_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Home_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(Home_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { ansi_send_home(); return; } #endif /*]*/ if (!formatted) { cursor_move(0); return; } cursor_move(next_unprotected(ROWS*COLS-1)); } /* * Cursor left 1 position. */ static void do_left(void) { register int baddr; enum dbcs_state d; baddr = cursor_addr; DEC_BA(baddr); d = ctlr_dbcs_state(baddr); if (IS_LEFT(d)) DEC_BA(baddr); cursor_move(baddr); } void Left_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Left_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (KYBDLOCK_IS_OERR) { kybdlock_clr(KL_OERR_MASK, "Left"); status_reset(); } else { enq_ta(Left_action, CN, CN); return; } } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { ansi_send_left(); return; } #endif /*]*/ if (!flipped) do_left(); else { register int baddr; baddr = cursor_addr; INC_BA(baddr); /* XXX: DBCS? */ cursor_move(baddr); } } /* * Delete char key. * Returns "True" if succeeds, "False" otherwise. */ static Boolean do_delete(void) { register int baddr, end_baddr; int xaddr; register unsigned char fa; int ndel; register int i; baddr = cursor_addr; /* Can't delete a field attribute. */ fa = get_field_attribute(baddr); if (FA_IS_PROTECTED(fa) || ea_buf[baddr].fa) { operator_error(KL_OERR_PROTECTED); return False; } if (ea_buf[baddr].cc == EBC_so || ea_buf[baddr].cc == EBC_si) { /* * Can't delete SO or SI, unless it's adjacent to its * opposite. */ xaddr = baddr; INC_BA(xaddr); if (ea_buf[xaddr].cc == SOSI(ea_buf[baddr].cc)) { ndel = 2; } else { operator_error(KL_OERR_PROTECTED); return False; } } else if (IS_DBCS(ea_buf[baddr].db)) { if (IS_RIGHT(ea_buf[baddr].db)) DEC_BA(baddr); ndel = 2; } else ndel = 1; /* find next fa */ if (formatted) { end_baddr = baddr; do { INC_BA(end_baddr); if (ea_buf[end_baddr].fa) break; } while (end_baddr != baddr); DEC_BA(end_baddr); } else { if ((baddr % COLS) == COLS - ndel) return True; end_baddr = baddr + (COLS - (baddr % COLS)) - 1; } /* Shift the remainder of the field left. */ if (end_baddr > baddr) { ctlr_bcopy(baddr + ndel, baddr, end_baddr - (baddr + ndel) + 1, 0); } else if (end_baddr != baddr) { /* XXX: Need to verify this. */ ctlr_bcopy(baddr + ndel, baddr, ((ROWS * COLS) - 1) - (baddr + ndel) + 1, 0); ctlr_bcopy(0, (ROWS * COLS) - ndel, ndel, 0); ctlr_bcopy(ndel, 0, end_baddr - ndel + 1, 0); } /* NULL fill at the end. */ for (i = 0; i < ndel; i++) ctlr_add(end_baddr - i, EBC_null, 0); /* Set the MDT for this field. */ mdt_set(cursor_addr); /* Patch up the DBCS state for display. */ (void) ctlr_dbcs_postprocess(); return True; } void Delete_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Delete_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(Delete_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { net_sendc('\177'); return; } #endif /*]*/ if (!do_delete()) return; if (reverse) { int baddr = cursor_addr; DEC_BA(baddr); if (!ea_buf[baddr].fa) cursor_move(baddr); } } /* * 3270-style backspace. */ void BackSpace_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(BackSpace_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(BackSpace_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { net_send_erase(); return; } #endif /*]*/ if (reverse) (void) do_delete(); else if (!flipped) do_left(); else { register int baddr; baddr = cursor_addr; DEC_BA(baddr); cursor_move(baddr); } } /* * Destructive backspace, like Unix "erase". */ static void do_erase(void) { int baddr, faddr; enum dbcs_state d; baddr = cursor_addr; faddr = find_field_attribute(baddr); if (faddr == baddr || FA_IS_PROTECTED(ea_buf[baddr].fa)) { operator_error(KL_OERR_PROTECTED); return; } if (baddr && faddr == baddr - 1) return; do_left(); /* * If we are now on an SI, move left again. */ if (ea_buf[cursor_addr].cc == EBC_si) { baddr = cursor_addr; DEC_BA(baddr); cursor_move(baddr); } /* * If we landed on the right-hand side of a DBCS character, move to the * left-hand side. * This ensures that if this is the end of a DBCS subfield, we will * land on the SI, instead of on the character following. */ d = ctlr_dbcs_state(cursor_addr); if (IS_RIGHT(d)) { baddr = cursor_addr; DEC_BA(baddr); cursor_move(baddr); } /* * Try to delete this character. */ if (!do_delete()) return; /* * If we've just erased the last character of a DBCS subfield, erase * the SO/SI pair as well. */ baddr = cursor_addr; DEC_BA(baddr); if (ea_buf[baddr].cc == EBC_so && ea_buf[cursor_addr].cc == EBC_si) { cursor_move(baddr); (void) do_delete(); } } void Erase_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Erase_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(Erase_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { net_send_erase(); return; } #endif /*]*/ do_erase(); } /* * Cursor right 1 position. */ void Right_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; enum dbcs_state d; action_debug(Right_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (KYBDLOCK_IS_OERR) { kybdlock_clr(KL_OERR_MASK, "Right"); status_reset(); } else { enq_ta(Right_action, CN, CN); return; } } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { ansi_send_right(); return; } #endif /*]*/ if (!flipped) { baddr = cursor_addr; INC_BA(baddr); d = ctlr_dbcs_state(baddr); if (IS_RIGHT(d)) INC_BA(baddr); cursor_move(baddr); } else do_left(); } /* * Cursor left 2 positions. */ void Left2_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; enum dbcs_state d; action_debug(Left2_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (KYBDLOCK_IS_OERR) { kybdlock_clr(KL_OERR_MASK, "Left2"); status_reset(); } else { enq_ta(Left2_action, CN, CN); return; } } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ baddr = cursor_addr; DEC_BA(baddr); d = ctlr_dbcs_state(baddr); if (IS_LEFT(d)) DEC_BA(baddr); DEC_BA(baddr); d = ctlr_dbcs_state(baddr); if (IS_LEFT(d)) DEC_BA(baddr); cursor_move(baddr); } /* * Cursor to previous word. */ void PreviousWord_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; int baddr0; unsigned char c; Boolean prot; action_debug(PreviousWord_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(PreviousWord_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ if (!formatted) return; baddr = cursor_addr; prot = FA_IS_PROTECTED(get_field_attribute(baddr)); /* Skip to before this word, if in one now. */ if (!prot) { c = ea_buf[baddr].cc; while (!ea_buf[baddr].fa && c != EBC_space && c != EBC_null) { DEC_BA(baddr); if (baddr == cursor_addr) return; c = ea_buf[baddr].cc; } } baddr0 = baddr; /* Find the end of the preceding word. */ do { c = ea_buf[baddr].cc; if (ea_buf[baddr].fa) { DEC_BA(baddr); prot = FA_IS_PROTECTED(get_field_attribute(baddr)); continue; } if (!prot && c != EBC_space && c != EBC_null) break; DEC_BA(baddr); } while (baddr != baddr0); if (baddr == baddr0) return; /* Go it its front. */ for (;;) { DEC_BA(baddr); c = ea_buf[baddr].cc; if (ea_buf[baddr].fa || c == EBC_space || c == EBC_null) { break; } } INC_BA(baddr); cursor_move(baddr); } /* * Cursor right 2 positions. */ void Right2_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; enum dbcs_state d; action_debug(Right2_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (KYBDLOCK_IS_OERR) { kybdlock_clr(KL_OERR_MASK, "Right2"); status_reset(); } else { enq_ta(Right2_action, CN, CN); return; } } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ baddr = cursor_addr; INC_BA(baddr); d = ctlr_dbcs_state(baddr); if (IS_RIGHT(d)) INC_BA(baddr); INC_BA(baddr); d = ctlr_dbcs_state(baddr); if (IS_RIGHT(d)) INC_BA(baddr); cursor_move(baddr); } /* Find the next unprotected word, or -1 */ static int nu_word(int baddr) { int baddr0 = baddr; unsigned char c; Boolean prot; prot = FA_IS_PROTECTED(get_field_attribute(baddr)); do { c = ea_buf[baddr].cc; if (ea_buf[baddr].fa) prot = FA_IS_PROTECTED(ea_buf[baddr].fa); else if (!prot && c != EBC_space && c != EBC_null) return baddr; INC_BA(baddr); } while (baddr != baddr0); return -1; } /* Find the next word in this field, or -1 */ static int nt_word(int baddr) { int baddr0 = baddr; unsigned char c; Boolean in_word = True; do { c = ea_buf[baddr].cc; if (ea_buf[baddr].fa) return -1; if (in_word) { if (c == EBC_space || c == EBC_null) in_word = False; } else { if (c != EBC_space && c != EBC_null) return baddr; } INC_BA(baddr); } while (baddr != baddr0); return -1; } /* * Cursor to next unprotected word. */ void NextWord_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; unsigned char c; action_debug(NextWord_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(NextWord_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ if (!formatted) return; /* If not in an unprotected field, go to the next unprotected word. */ if (ea_buf[cursor_addr].fa || FA_IS_PROTECTED(get_field_attribute(cursor_addr))) { baddr = nu_word(cursor_addr); if (baddr != -1) cursor_move(baddr); return; } /* If there's another word in this field, go to it. */ baddr = nt_word(cursor_addr); if (baddr != -1) { cursor_move(baddr); return; } /* If in a word, go to just after its end. */ c = ea_buf[cursor_addr].cc; if (c != EBC_space && c != EBC_null) { baddr = cursor_addr; do { c = ea_buf[baddr].cc; if (c == EBC_space || c == EBC_null) { cursor_move(baddr); return; } else if (ea_buf[baddr].fa) { baddr = nu_word(baddr); if (baddr != -1) cursor_move(baddr); return; } INC_BA(baddr); } while (baddr != cursor_addr); } /* Otherwise, go to the next unprotected word. */ else { baddr = nu_word(cursor_addr); if (baddr != -1) cursor_move(baddr); } } /* * Cursor up 1 position. */ void Up_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; action_debug(Up_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (KYBDLOCK_IS_OERR) { kybdlock_clr(KL_OERR_MASK, "Up"); status_reset(); } else { enq_ta(Up_action, CN, CN); return; } } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { ansi_send_up(); return; } #endif /*]*/ baddr = cursor_addr - COLS; if (baddr < 0) baddr = (cursor_addr + (ROWS * COLS)) - COLS; cursor_move(baddr); } /* * Cursor down 1 position. */ void Down_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; action_debug(Down_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (KYBDLOCK_IS_OERR) { kybdlock_clr(KL_OERR_MASK, "Down"); status_reset(); } else { enq_ta(Down_action, CN, CN); return; } } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { ansi_send_down(); return; } #endif /*]*/ baddr = (cursor_addr + COLS) % (COLS * ROWS); cursor_move(baddr); } /* * Cursor to first field on next line or any lines after that. */ void Newline_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr, faddr; register unsigned char fa; action_debug(Newline_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(Newline_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { net_sendc('\n'); return; } #endif /*]*/ baddr = (cursor_addr + COLS) % (COLS * ROWS); /* down */ baddr = (baddr / COLS) * COLS; /* 1st col */ faddr = find_field_attribute(baddr); fa = ea_buf[faddr].fa; if (faddr != baddr && !FA_IS_PROTECTED(fa)) cursor_move(baddr); else cursor_move(next_unprotected(baddr)); } /* * DUP key */ void Dup_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Dup_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(Dup_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ if (key_Character(EBC_dup, False, False)) cursor_move(next_unprotected(cursor_addr)); } /* * FM key */ void FieldMark_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(FieldMark_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(FieldMark_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ (void) key_Character(EBC_fm, False, False); } /* * Vanilla AID keys. */ void Enter_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Enter_action, event, params, num_params); reset_idle_timer(); if (kybdlock & KL_OIA_MINUS) return; else if (kybdlock) enq_ta(Enter_action, CN, CN); else key_AID(AID_ENTER); } void SysReq_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(SysReq_action, event, params, num_params); reset_idle_timer(); if (IN_ANSI) return; #if defined(X3270_TN3270E) /*[*/ if (IN_E) { net_abort(); } else #endif /*]*/ { if (kybdlock & KL_OIA_MINUS) return; else if (kybdlock) enq_ta(SysReq_action, CN, CN); else key_AID(AID_SYSREQ); } } /* * Clear AID key */ void Clear_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Clear_action, event, params, num_params); reset_idle_timer(); if (kybdlock & KL_OIA_MINUS) return; if (kybdlock && CONNECTED) { enq_ta(Clear_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { ansi_send_clear(); return; } #endif /*]*/ buffer_addr = 0; ctlr_clear(True); cursor_move(0); if (CONNECTED) key_AID(AID_CLEAR); } /* * Cursor Select key (light pen simulator). */ static void lightpen_select(int baddr) { int faddr; register unsigned char fa; int designator; #if defined(X3270_DBCS) /*[*/ int designator2; #endif /*]*/ faddr = find_field_attribute(baddr); fa = ea_buf[faddr].fa; if (!FA_IS_SELECTABLE(fa)) { ring_bell(); return; } designator = faddr; INC_BA(designator); #if defined(X3270_DBCS) /*[*/ if (dbcs) { if (ea_buf[baddr].cs == CS_DBCS) { designator2 = designator; INC_BA(designator2); if ((ea_buf[designator].db != DBCS_LEFT && ea_buf[designator].db != DBCS_LEFT_WRAP) && (ea_buf[designator2].db != DBCS_RIGHT && ea_buf[designator2].db != DBCS_RIGHT_WRAP)) { ring_bell(); return; } if (ea_buf[designator].cc == 0x42 && ea_buf[designator2].cc == EBC_greater) { ctlr_add(designator2, EBC_question, CS_DBCS); mdt_clear(faddr); } else if (ea_buf[designator].cc == 0x42 && ea_buf[designator2].cc == EBC_question) { ctlr_add(designator2, EBC_greater, CS_DBCS); mdt_clear(faddr); } else if ((ea_buf[designator].cc == EBC_space && ea_buf[designator2].cc == EBC_space) || (ea_buf[designator].cc == EBC_null && ea_buf[designator2].cc == EBC_null)) { ctlr_add(designator2, EBC_greater, CS_DBCS); mdt_set(faddr); key_AID(AID_SELECT); } else if (ea_buf[designator].cc == 0x42 && ea_buf[designator2].cc == EBC_ampersand) { mdt_set(faddr); key_AID(AID_ENTER); } else { ring_bell(); } return; } } #endif /*]*/ switch (ea_buf[designator].cc) { case EBC_greater: /* > */ ctlr_add(designator, EBC_question, 0); /* change to ? */ mdt_clear(faddr); break; case EBC_question: /* ? */ ctlr_add(designator, EBC_greater, 0); /* change to > */ mdt_set(faddr); break; case EBC_space: /* space */ case EBC_null: /* null */ mdt_set(faddr); key_AID(AID_SELECT); break; case EBC_ampersand: /* & */ mdt_set(faddr); key_AID(AID_ENTER); break; default: ring_bell(); break; } } /* * Cursor Select key (light pen simulator) -- at the current cursor location. */ void CursorSelect_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(CursorSelect_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(CursorSelect_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ lightpen_select(cursor_addr); } #if defined(X3270_DISPLAY) /*[*/ /* * Cursor Select mouse action (light pen simulator). */ void MouseSelect_action(Widget w, XEvent *event, String *params, Cardinal *num_params) { action_debug(MouseSelect_action, event, params, num_params); if (w != *screen) return; reset_idle_timer(); if (kybdlock) return; #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ lightpen_select(mouse_baddr(w, event)); } #endif /*]*/ /* * Erase End Of Field Key. */ void EraseEOF_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; register unsigned char fa; enum dbcs_state d; enum dbcs_why why; action_debug(EraseEOF_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(EraseEOF_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ baddr = cursor_addr; fa = get_field_attribute(baddr); if (FA_IS_PROTECTED(fa) || ea_buf[baddr].fa) { operator_error(KL_OERR_PROTECTED); return; } if (formatted) { /* erase to next field attribute */ do { ctlr_add(baddr, EBC_null, 0); INC_BA(baddr); } while (!ea_buf[baddr].fa); mdt_set(cursor_addr); } else { /* erase to end of screen */ do { ctlr_add(baddr, EBC_null, 0); INC_BA(baddr); } while (baddr != 0); } /* If the cursor was in a DBCS subfield, re-create the SI. */ d = ctlr_lookleft_state(cursor_addr, &why); if (IS_DBCS(d) && why == DBCS_SUBFIELD) { if (d == DBCS_RIGHT) { baddr = cursor_addr; DEC_BA(baddr); ea_buf[baddr].cc = EBC_si; } else ea_buf[cursor_addr].cc = EBC_si; } (void) ctlr_dbcs_postprocess(); } /* * Erase all Input Key. */ void EraseInput_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr, sbaddr; unsigned char fa; Boolean f; action_debug(EraseInput_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(EraseInput_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ if (formatted) { /* find first field attribute */ baddr = 0; do { if (ea_buf[baddr].fa) break; INC_BA(baddr); } while (baddr != 0); sbaddr = baddr; f = False; do { fa = ea_buf[baddr].fa; if (!FA_IS_PROTECTED(fa)) { mdt_clear(baddr); do { INC_BA(baddr); if (!f) { cursor_move(baddr); f = True; } if (!ea_buf[baddr].fa) { ctlr_add(baddr, EBC_null, 0); } } while (!ea_buf[baddr].fa); } else { /* skip protected */ do { INC_BA(baddr); } while (!ea_buf[baddr].fa); } } while (baddr != sbaddr); if (!f) cursor_move(0); } else { ctlr_clear(True); cursor_move(0); } } /* * Delete word key. Backspaces the cursor until it hits the front of a word, * deletes characters until it hits a blank or null, and deletes all of these * but the last. * * Which is to say, does a ^W. */ void DeleteWord_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; register unsigned char fa; action_debug(DeleteWord_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(DeleteWord_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { net_send_werase(); return; } #endif /*]*/ if (!formatted) return; baddr = cursor_addr; fa = get_field_attribute(baddr); /* Make sure we're on a modifiable field. */ if (FA_IS_PROTECTED(fa) || ea_buf[baddr].fa) { operator_error(KL_OERR_PROTECTED); return; } /* Backspace over any spaces to the left of the cursor. */ for (;;) { baddr = cursor_addr; DEC_BA(baddr); if (ea_buf[baddr].fa) return; if (ea_buf[baddr].cc == EBC_null || ea_buf[baddr].cc == EBC_space) do_erase(); else break; } /* Backspace until the character to the left of the cursor is blank. */ for (;;) { baddr = cursor_addr; DEC_BA(baddr); if (ea_buf[baddr].fa) return; if (ea_buf[baddr].cc == EBC_null || ea_buf[baddr].cc == EBC_space) break; else do_erase(); } } /* * Delete field key. Similar to EraseEOF, but it wipes out the entire field * rather than just to the right of the cursor, and it leaves the cursor at * the front of the field. * * Which is to say, does a ^U. */ void DeleteField_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { register int baddr; register unsigned char fa; action_debug(DeleteField_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(DeleteField_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) { net_send_kill(); return; } #endif /*]*/ if (!formatted) return; baddr = cursor_addr; fa = get_field_attribute(baddr); if (FA_IS_PROTECTED(fa) || ea_buf[baddr].fa) { operator_error(KL_OERR_PROTECTED); return; } while (!ea_buf[baddr].fa) DEC_BA(baddr); INC_BA(baddr); mdt_set(cursor_addr); cursor_move(baddr); while (!ea_buf[baddr].fa) { ctlr_add(baddr, EBC_null, 0); INC_BA(baddr); } } /* * Set insert mode key. */ void Insert_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Insert_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(Insert_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ insert_mode(True); } /* * Toggle insert mode key. */ void ToggleInsert_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(ToggleInsert_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(ToggleInsert_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ if (insert) insert_mode(False); else insert_mode(True); } /* * Toggle reverse mode key. */ void ToggleReverse_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(ToggleReverse_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(ToggleReverse_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ reverse_mode(!reverse); } /* * Move the cursor to the first blank after the last nonblank in the * field, or if the field is full, to the last character in the field. */ void FieldEnd_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { int baddr, faddr; unsigned char fa, c; int last_nonblank = -1; action_debug(FieldEnd_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { enq_ta(FieldEnd_action, CN, CN); return; } #if defined(X3270_ANSI) /*[*/ if (IN_ANSI) return; #endif /*]*/ if (!formatted) return; baddr = cursor_addr; faddr = find_field_attribute(baddr); fa = ea_buf[faddr].fa; if (faddr == baddr || FA_IS_PROTECTED(fa)) return; baddr = faddr; while (True) { INC_BA(baddr); c = ea_buf[baddr].cc; if (ea_buf[baddr].fa) break; if (c != EBC_null && c != EBC_space) last_nonblank = baddr; } if (last_nonblank == -1) { baddr = faddr; INC_BA(baddr); } else { baddr = last_nonblank; INC_BA(baddr); if (ea_buf[baddr].fa) baddr = last_nonblank; } cursor_move(baddr); } /* * MoveCursor action. Depending on arguments, this is either a move to the * mouse cursor position, or to an absolute location. */ void MoveCursor_action(Widget w, XEvent *event, String *params, Cardinal *num_params) { register int baddr; int row, col; action_debug(MoveCursor_action, event, params, num_params); reset_idle_timer(); if (kybdlock) { if (*num_params == 2) enq_ta(MoveCursor_action, params[0], params[1]); return; } switch (*num_params) { #if defined(X3270_DISPLAY) /*[*/ case 0: /* mouse click, presumably */ if (w != *screen) return; cursor_move(mouse_baddr(w, event)); break; #endif /*]*/ case 2: /* probably a macro call */ row = atoi(params[0]); col = atoi(params[1]); if (!IN_3270) { row--; col--; } if (row < 0) row = 0; if (col < 0) col = 0; baddr = ((row * COLS) + col) % (ROWS * COLS); cursor_move(baddr); break; default: /* couln't say */ popup_an_error("%s requires 0 or 2 arguments", action_name(MoveCursor_action)); cancel_if_idle_command(); break; } } #if defined(X3270_DBCS) && defined(X3270_DISPLAY) /*[*/ /* * Run a KeyPress through XIM. * Returns True if there is further processing to do, False otherwise. */ static Boolean xim_lookup(XKeyEvent *event) { static char *buf = NULL; static UChar *Ubuf = NULL; static int buf_len = 0, rlen; KeySym k; Status status; extern XIC ic; int i; Boolean rv = False; int wlen; #define BASE_BUFSIZE 50 if (ic == NULL) return True; if (buf == NULL) { buf_len = BASE_BUFSIZE; buf = Malloc(buf_len); Ubuf = (UChar *)Malloc(buf_len * sizeof(UChar)); } for (;;) { memset(buf, '\0', buf_len); rlen = XmbLookupString(ic, event, buf, buf_len - 1, &k, &status); if (status != XBufferOverflow) break; buf_len += BASE_BUFSIZE; buf = Realloc(buf, buf_len); Ubuf = (UChar *)Realloc(Ubuf, buf_len * sizeof(UChar)); } switch (status) { case XLookupNone: rv = False; break; case XLookupKeySym: rv = True; break; case XLookupChars: trace_event("%d XIM char%s:", rlen, (rlen != 1)? "s": ""); for (i = 0; i < rlen; i++) { trace_event(" %02x", buf[i] & 0xff); } trace_event("\n"); wlen = mb_to_unicode(buf, rlen, Ubuf, buf_len, NULL); if (wlen < 0) trace_event(" conversion failed\n"); for (i = 0; i < wlen; i++) { unsigned char asc; unsigned char ebc[2]; if (dbcs_map8(Ubuf[i], &asc)) { trace_event(" U+%04x -> " "EBCDIC SBCS X'%02x'\n", Ubuf[i] & 0xffff, asc2ebc[asc]); key_ACharacter(asc, KT_STD, ia_cause); } else if (dbcs_map16(Ubuf[i], ebc)) { trace_event(" U+%04x -> " "EBCDIC DBCS X'%02x%02x'\n", Ubuf[i] & 0xffff, ebc[0], ebc[1]); key_WCharacter(ebc); } else trace_event(" Cannot convert U+%04x to " "EBCDIC\n", Ubuf[i] & 0xffff); } rv = False; break; case XLookupBoth: rv = True; break; } return rv; } #endif /*]*/ /* * Key action. */ void Key_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { Cardinal i; KeySym k; enum keytype keytype; action_debug(Key_action, event, params, num_params); reset_idle_timer(); for (i = 0; i < *num_params; i++) { char *s = params[i]; k = MyStringToKeysym(s, &keytype); if (k == NoSymbol) { popup_an_error("%s: Nonexistent or invalid KeySym: %s", action_name(Key_action), s); cancel_if_idle_command(); continue; } if (k & ~0xff) { popup_an_error("%s: Invalid KeySym: %s", action_name(Key_action), s); cancel_if_idle_command(); continue; } key_ACharacter((unsigned char)(k & 0xff), keytype, IA_KEY); } } /* * String action. */ void String_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { Cardinal i; int len = 0; char *s; action_debug(String_action, event, params, num_params); reset_idle_timer(); /* Determine the total length of the strings. */ for (i = 0; i < *num_params; i++) len += strlen(params[i]); if (!len) return; /* Allocate a block of memory and copy them in. */ s = Malloc(len + 1); *s = '\0'; for (i = 0; i < *num_params; i++) (void) strcat(s, params[i]); /* Set a pending string. */ ps_set(s, False); } /* * HexString action. */ void HexString_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { Cardinal i; int len = 0; char *s; char *t; action_debug(HexString_action, event, params, num_params); reset_idle_timer(); /* Determine the total length of the strings. */ for (i = 0; i < *num_params; i++) { t = params[i]; if (!strncmp(t, "0x", 2) || !strncmp(t, "0X", 2)) t += 2; len += strlen(t); } if (!len) return; /* Allocate a block of memory and copy them in. */ s = Malloc(len + 1); *s = '\0'; for (i = 0; i < *num_params; i++) { t = params[i]; if (!strncmp(t, "0x", 2) || !strncmp(t, "0X", 2)) t += 2; (void) strcat(s, t); } /* Set a pending string. */ ps_set(s, True); } /* * Dual-mode action for the "asciicircum" ("^") key: * If in ANSI mode, pass through untranslated. * If in 3270 mode, translate to "notsign". * This action is obsoleted by the use of 3270-mode and NVT-mode keymaps, but * is still defined here for backwards compatibility with old keymaps. */ void CircumNot_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(CircumNot_action, event, params, num_params); reset_idle_timer(); if (IN_3270 && composing == NONE) key_ACharacter(0xac, KT_STD, IA_KEY); else key_ACharacter('^', KT_STD, IA_KEY); } /* PA key action for String actions */ static void do_pa(unsigned n) { if (n < 1 || n > PA_SZ) { popup_an_error("Unknown PA key %d", n); cancel_if_idle_command(); return; } if (kybdlock) { char nn[3]; (void) sprintf(nn, "%d", n); enq_ta(PA_action, nn, CN); return; } key_AID(pa_xlate[n-1]); } /* PF key action for String actions */ static void do_pf(unsigned n) { if (n < 1 || n > PF_SZ) { popup_an_error("Unknown PF key %d", n); cancel_if_idle_command(); return; } if (kybdlock) { char nn[3]; (void) sprintf(nn, "%d", n); enq_ta(PF_action, nn, CN); return; } key_AID(pf_xlate[n-1]); } /* * Set or clear the keyboard scroll lock. */ void kybd_scroll_lock(Boolean lock) { if (!IN_3270) return; if (lock) kybdlock_set(KL_SCROLLED, "kybd_scroll_lock"); else kybdlock_clr(KL_SCROLLED, "kybd_scroll_lock"); } /* * Move the cursor back within the legal paste area. * Returns a Boolean indicating success. */ static Boolean remargin(int lmargin) { Boolean ever = False; int baddr, b0 = 0; int faddr; unsigned char fa; baddr = cursor_addr; while (BA_TO_COL(baddr) < lmargin) { baddr = ROWCOL_TO_BA(BA_TO_ROW(baddr), lmargin); if (!ever) { b0 = baddr; ever = True; } faddr = find_field_attribute(baddr); fa = ea_buf[faddr].fa; if (faddr == baddr || FA_IS_PROTECTED(fa)) { baddr = next_unprotected(baddr); if (baddr <= b0) return False; } } cursor_move(baddr); return True; } /* * Pretend that a sequence of keys was entered at the keyboard. * * "Pasting" means that the sequence came from the X clipboard. Returns are * ignored; newlines mean "move to beginning of next line"; tabs and formfeeds * become spaces. Backslashes are not special, but ASCII ESC characters are * used to signify 3270 Graphic Escapes. * * "Not pasting" means that the sequence is a login string specified in the * hosts file, or a parameter to the String action. Returns are "move to * beginning of next line"; newlines mean "Enter AID" and the termination of * processing the string. Backslashes are processed as in C. * * Returns the number of unprocessed characters. */ int emulate_input(char *s, int len, Boolean pasting) { enum { BASE, BACKSLASH, BACKX, BACKP, BACKPA, BACKPF, OCTAL, HEX, XGE } state = BASE; int literal = 0; int nc = 0; enum iaction ia = pasting ? IA_PASTE : IA_STRING; int orig_addr = cursor_addr; int orig_col = BA_TO_COL(cursor_addr); #if defined(X3270_DBCS) /*[*/ unsigned char ebc[2]; unsigned char cx; static UChar *w_ibuf = NULL; static size_t w_ibuf_len = 0; UChar c; UChar *ws; #else /*][*/ char c; char *ws; #endif /*]*/ /* * Convert from a multi-byte string to a Unicode string. */ #if defined(X3270_DBCS) /*[*/ if (len > w_ibuf_len) { w_ibuf_len = len; w_ibuf = (UChar *)Realloc(w_ibuf, w_ibuf_len * sizeof(UChar)); } len = mb_to_unicode(s, len, w_ibuf, w_ibuf_len, NULL); if (len < 0) { return 0; /* failed */ } ws = w_ibuf; #else /*][*/ ws = s; #endif /*]*/ /* * In the switch statements below, "break" generally means "consume * this character," while "continue" means "rescan this character." */ while (len) { /* * It isn't possible to unlock the keyboard from a string, * so if the keyboard is locked, it's fatal */ if (kybdlock) { trace_event(" keyboard locked, string dropped\n"); return 0; } if (pasting && IN_3270) { /* Check for cursor wrap to top of screen. */ if (cursor_addr < orig_addr) return len-1; /* wrapped */ /* Jump cursor over left margin. */ if (toggled(MARGINED_PASTE) && BA_TO_COL(cursor_addr) < orig_col) { if (!remargin(orig_col)) return len-1; } } c = *ws; switch (state) { case BASE: switch (c) { case '\b': action_internal(Left_action, ia, CN, CN); continue; case '\f': if (pasting) { key_ACharacter((unsigned char) ' ', KT_STD, ia); } else { action_internal(Clear_action, ia, CN, CN); if (IN_3270) return len-1; } break; case '\n': if (pasting) action_internal(Newline_action, ia, CN, CN); else { action_internal(Enter_action, ia, CN, CN); if (IN_3270) return len-1; } break; case '\r': /* ignored */ break; case '\t': action_internal(Tab_action, ia, CN, CN); break; case '\\': /* backslashes are NOT special when pasting */ if (!pasting) state = BACKSLASH; else key_ACharacter((unsigned char) c, KT_STD, ia); break; case '\033': /* ESC is special only when pasting */ if (pasting) state = XGE; break; case '[': /* APL left bracket */ if (pasting && appres.apl_mode) key_ACharacter( (unsigned char) XK_Yacute, KT_GE, ia); else key_ACharacter((unsigned char) c, KT_STD, ia); break; case ']': /* APL right bracket */ if (pasting && appres.apl_mode) key_ACharacter( (unsigned char) XK_diaeresis, KT_GE, ia); else key_ACharacter((unsigned char) c, KT_STD, ia); break; default: #if defined(X3270_DBCS) /*[*/ /* * Try mapping it to the 8-bit character set, * otherwise to the 16-bit character set. */ if (dbcs_map8(c, &cx)) { key_ACharacter((unsigned char)cx, KT_STD, ia_cause); break; } else if (dbcs_map16(c, ebc)) { key_WCharacter(ebc); break; } else { trace_event("Cannot convert U+%04x to " "EBCDIC\n", c & 0xffff); break; } #endif /*]*/ key_ACharacter((unsigned char) c, KT_STD, ia); break; } break; case BACKSLASH: /* last character was a backslash */ switch (c) { case 'a': popup_an_error("%s: Bell not supported", action_name(String_action)); cancel_if_idle_command(); state = BASE; break; case 'b': action_internal(Left_action, ia, CN, CN); state = BASE; break; case 'f': action_internal(Clear_action, ia, CN, CN); state = BASE; if (IN_3270) return len-1; else break; case 'n': action_internal(Enter_action, ia, CN, CN); state = BASE; if (IN_3270) return len-1; else break; case 'p': state = BACKP; break; case 'r': action_internal(Newline_action, ia, CN, CN); state = BASE; break; case 't': action_internal(Tab_action, ia, CN, CN); state = BASE; break; case 'T': action_internal(BackTab_action, ia, CN, CN); state = BASE; break; case 'v': popup_an_error("%s: Vertical tab not supported", action_name(String_action)); cancel_if_idle_command(); state = BASE; break; case 'x': state = BACKX; break; case '\\': key_ACharacter((unsigned char) c, KT_STD, ia); state = BASE; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': state = OCTAL; literal = 0; nc = 0; continue; default: state = BASE; continue; } break; case BACKP: /* last two characters were "\p" */ switch (c) { case 'a': literal = 0; nc = 0; state = BACKPA; break; case 'f': literal = 0; nc = 0; state = BACKPF; break; default: popup_an_error("%s: Unknown character after \\p", action_name(String_action)); cancel_if_idle_command(); state = BASE; break; } break; case BACKPF: /* last three characters were "\pf" */ if (nc < 2 && isdigit(c)) { literal = (literal * 10) + (c - '0'); nc++; } else if (!nc) { popup_an_error("%s: Unknown character after \\pf", action_name(String_action)); cancel_if_idle_command(); state = BASE; } else { do_pf(literal); if (IN_3270) return len-1; state = BASE; continue; } break; case BACKPA: /* last three characters were "\pa" */ if (nc < 1 && isdigit(c)) { literal = (literal * 10) + (c - '0'); nc++; } else if (!nc) { popup_an_error("%s: Unknown character after \\pa", action_name(String_action)); cancel_if_idle_command(); state = BASE; } else { do_pa(literal); if (IN_3270) return len-1; state = BASE; continue; } break; case BACKX: /* last two characters were "\x" */ if (isxdigit(c)) { state = HEX; literal = 0; nc = 0; continue; } else { popup_an_error("%s: Missing hex digits after \\x", action_name(String_action)); cancel_if_idle_command(); state = BASE; continue; } case OCTAL: /* have seen \ and one or more octal digits */ if (nc < 3 && isdigit(c) && c < '8') { literal = (literal * 8) + FROM_HEX(c); nc++; break; } else { key_ACharacter((unsigned char) literal, KT_STD, ia); state = BASE; continue; } case HEX: /* have seen \ and one or more hex digits */ if (nc < 2 && isxdigit(c)) { literal = (literal * 16) + FROM_HEX(c); nc++; break; } else { key_ACharacter((unsigned char) literal, KT_STD, ia); state = BASE; continue; } case XGE: /* have seen ESC */ switch (c) { case ';': /* FM */ key_Character(EBC_fm, False, True); break; case '*': /* DUP */ key_Character(EBC_dup, False, True); break; default: key_ACharacter((unsigned char) c, KT_GE, ia); break; } state = BASE; break; } ws++; len--; } switch (state) { case BASE: if (toggled(MARGINED_PASTE) && BA_TO_COL(cursor_addr) < orig_col) { (void) remargin(orig_col); } break; case OCTAL: case HEX: key_ACharacter((unsigned char) literal, KT_STD, ia); state = BASE; if (toggled(MARGINED_PASTE) && BA_TO_COL(cursor_addr) < orig_col) { (void) remargin(orig_col); } break; case BACKPF: if (nc > 0) { do_pf(literal); state = BASE; } break; case BACKPA: if (nc > 0) { do_pa(literal); state = BASE; } break; default: popup_an_error("%s: Missing data after \\", action_name(String_action)); cancel_if_idle_command(); break; } return len; } /* * Pretend that a sequence of hexadecimal characters was entered at the * keyboard. The input is a sequence of hexadecimal bytes, 2 characters * per byte. If connected in ANSI mode, these are treated as ASCII * characters; if in 3270 mode, they are considered EBCDIC. * * Graphic Escapes are handled as \E. */ void hex_input(char *s) { char *t; Boolean escaped; #if defined(X3270_ANSI) /*[*/ unsigned char *xbuf = (unsigned char *)NULL; unsigned char *tbuf = (unsigned char *)NULL; int nbytes = 0; #endif /*]*/ /* Validate the string. */ if (strlen(s) % 2) { popup_an_error("%s: Odd number of characters in specification", action_name(HexString_action)); cancel_if_idle_command(); return; } t = s; escaped = False; while (*t) { if (isxdigit(*t) && isxdigit(*(t + 1))) { escaped = False; #if defined(X3270_ANSI) /*[*/ nbytes++; #endif /*]*/ } else if (!strncmp(t, "\\E", 2) || !strncmp(t, "\\e", 2)) { if (escaped) { popup_an_error("%s: Double \\E", action_name(HexString_action)); cancel_if_idle_command(); return; } if (!IN_3270) { popup_an_error("%s: \\E in ANSI mode", action_name(HexString_action)); cancel_if_idle_command(); return; } escaped = True; } else { popup_an_error("%s: Illegal character in specification", action_name(HexString_action)); cancel_if_idle_command(); return; } t += 2; } if (escaped) { popup_an_error("%s: Nothing follows \\E", action_name(HexString_action)); cancel_if_idle_command(); return; } #if defined(X3270_ANSI) /*[*/ /* Allocate a temporary buffer. */ if (!IN_3270 && nbytes) tbuf = xbuf = (unsigned char *)Malloc(nbytes); #endif /*]*/ /* Pump it in. */ t = s; escaped = False; while (*t) { if (isxdigit(*t) && isxdigit(*(t + 1))) { unsigned c; c = (FROM_HEX(*t) * 16) + FROM_HEX(*(t + 1)); if (IN_3270) key_Character(c, escaped, True); #if defined(X3270_ANSI) /*[*/ else *tbuf++ = (unsigned char)c; #endif /*]*/ escaped = False; } else if (!strncmp(t, "\\E", 2) || !strncmp(t, "\\e", 2)) { escaped = True; } t += 2; } #if defined(X3270_ANSI) /*[*/ if (!IN_3270 && nbytes) { net_hexansi_out(xbuf, nbytes); Free(xbuf); } #endif /*]*/ } void ignore_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(ignore_action, event, params, num_params); reset_idle_timer(); } #if defined(X3270_FT) /*[*/ /* * Set up the cursor and input field for command input. * Returns the length of the input field, or 0 if there is no field * to set up. */ int kybd_prime(void) { int baddr; register unsigned char fa; int len = 0; /* * No point in trying if the screen isn't formatted, the keyboard * is locked, or we aren't in 3270 mode. */ if (!formatted || kybdlock || !IN_3270) return 0; fa = get_field_attribute(cursor_addr); if (ea_buf[cursor_addr].fa || FA_IS_PROTECTED(fa)) { /* * The cursor is not in an unprotected field. Find the * next one. */ baddr = next_unprotected(cursor_addr); /* If there isn't any, give up. */ if (!baddr) return 0; /* Move the cursor there. */ } else { /* Already in an unprotected field. Find its start. */ baddr = cursor_addr; while (!ea_buf[baddr].fa) { DEC_BA(baddr); } INC_BA(baddr); } /* Move the cursor to the beginning of the field. */ cursor_move(baddr); /* Erase it. */ while (!ea_buf[baddr].fa) { ctlr_add(baddr, 0, 0); len++; INC_BA(baddr); } /* Return the field length. */ return len; } #endif /*]*/ /* * Translate a keysym name to a keysym, including APL and extended * characters. */ static KeySym MyStringToKeysym(char *s, enum keytype *keytypep) { KeySym k; int cc; char *ptr; #if defined(X3270_APL) /*[*/ if (!strncmp(s, "apl_", 4)) { int is_ge; k = APLStringToKeysym(s, &is_ge); if (is_ge) *keytypep = KT_GE; else *keytypep = KT_STD; } else #endif /*]*/ { k = StringToKeysym(s); *keytypep = KT_STD; } if (k == NoSymbol && !strcasecmp(s, "euro")) k = 0xa4; if (k == NoSymbol && strlen(s) == 1) k = s[0] & 0xff; if (k < ' ') k = NoSymbol; else if (k > 0xff) { int i; for (i = 0; i < nxk; i++) if (xk[i].key == k) { k = xk[i].assoc; break; } if (k > 0xff) k &= 0xff; } /* Allow arbitrary values, e.g., 0x03 for ^C. */ if (k == NoSymbol && (cc = strtoul(s, &ptr, 0)) > 0 && cc < 0xff && ptr != s && *ptr == '\0') k = cc; return k; } /* Add a key to the extended association table. */ void add_xk(KeySym key, KeySym assoc) { int i; for (i = 0; i < nxk; i++) if (xk[i].key == key) { xk[i].assoc = assoc; return; } xk = (struct xks *)Realloc(xk, (nxk + 1) * sizeof(struct xks)); xk[nxk].key = key; xk[nxk].assoc = assoc; nxk++; } /* Clear the extended association table. */ void clear_xks(void) { if (nxk) { Free(xk); xk = (struct xks *)NULL; nxk = 0; } } #if defined(X3270_DISPLAY) /*[*/ /* * X-dependent code starts here. */ /* * Translate a keymap (from an XQueryKeymap or a KeymapNotify event) into * a bitmap of Shift, Meta or Alt keys pressed. */ #define key_is_down(kc, bitmap) (kc && ((bitmap)[(kc)/8] & (1<<((kc)%8)))) int state_from_keymap(char keymap[32]) { static Boolean initted = False; static KeyCode kc_Shift_L, kc_Shift_R; static KeyCode kc_Meta_L, kc_Meta_R; static KeyCode kc_Alt_L, kc_Alt_R; int pseudo_state = 0; if (!initted) { kc_Shift_L = XKeysymToKeycode(display, XK_Shift_L); kc_Shift_R = XKeysymToKeycode(display, XK_Shift_R); kc_Meta_L = XKeysymToKeycode(display, XK_Meta_L); kc_Meta_R = XKeysymToKeycode(display, XK_Meta_R); kc_Alt_L = XKeysymToKeycode(display, XK_Alt_L); kc_Alt_R = XKeysymToKeycode(display, XK_Alt_R); initted = True; } if (key_is_down(kc_Shift_L, keymap) || key_is_down(kc_Shift_R, keymap)) pseudo_state |= ShiftKeyDown; if (key_is_down(kc_Meta_L, keymap) || key_is_down(kc_Meta_R, keymap)) pseudo_state |= MetaKeyDown; if (key_is_down(kc_Alt_L, keymap) || key_is_down(kc_Alt_R, keymap)) pseudo_state |= AltKeyDown; return pseudo_state; } #undef key_is_down /* * Process shift keyboard events. The code has to look for the raw Shift keys, * rather than using the handy "state" field in the event structure. This is * because the event state is the state _before_ the key was pressed or * released. This isn't enough information to distinguish between "left * shift released" and "left shift released, right shift still held down" * events, for example. * * This function is also called as part of Focus event processing. */ void PA_Shift_action(Widget w unused, XEvent *event unused, String *params unused, Cardinal *num_params unused) { char keys[32]; #if defined(INTERNAL_ACTION_DEBUG) /*[*/ action_debug(PA_Shift_action, event, params, num_params); #endif /*]*/ XQueryKeymap(display, keys); shift_event(state_from_keymap(keys)); } #endif /*]*/ #if defined(X3270_DISPLAY) || defined(C3270) /*[*/ static Boolean build_composites(void) { char *c, *c0, *c1; char *ln; char ksname[3][64]; char junk[2]; KeySym k[3]; enum keytype a[3]; int i; struct composite *cp; if (appres.compose_map == CN) { popup_an_error("%s: No %s defined", action_name(Compose_action), ResComposeMap); return False; } c0 = get_fresource("%s.%s", ResComposeMap, appres.compose_map); if (c0 == CN) { popup_an_error("%s: Cannot find %s \"%s\"", action_name(Compose_action), ResComposeMap, appres.compose_map); return False; } c1 = c = NewString(c0); /* will be modified by strtok */ while ((ln = strtok(c, "\n"))) { Boolean okay = True; c = NULL; if (sscanf(ln, " %63[^+ \t] + %63[^= \t] =%63s%1s", ksname[0], ksname[1], ksname[2], junk) != 3) { popup_an_error("%s: Invalid syntax: %s", action_name(Compose_action), ln); continue; } for (i = 0; i < 3; i++) { k[i] = MyStringToKeysym(ksname[i], &a[i]); if (k[i] == NoSymbol) { popup_an_error("%s: Invalid KeySym: \"%s\"", action_name(Compose_action), ksname[i]); okay = False; break; } } if (!okay) continue; composites = (struct composite *) Realloc((char *)composites, (n_composites + 1) * sizeof(struct composite)); cp = composites + n_composites; cp->k1.keysym = k[0]; cp->k1.keytype = a[0]; cp->k2.keysym = k[1]; cp->k2.keytype = a[1]; cp->translation.keysym = k[2]; cp->translation.keytype = a[2]; n_composites++; } Free(c1); return True; } /* * Called by the toolkit when the "Compose" key is pressed. "Compose" is * implemented by pressing and releasing three keys: "Compose" and two * data keys. For example, "Compose" "s" "s" gives the German "ssharp" * character, and "Compose" "C", "," gives a capital "C" with a cedilla * (symbol Ccedilla). * * The mechanism breaks down a little when the user presses "Compose" and * then a non-data key. Oh well. */ void Compose_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(Compose_action, event, params, num_params); reset_idle_timer(); if (!composites && !build_composites()) return; if (composing == NONE) { composing = COMPOSE; status_compose(True, 0, KT_STD); } } #endif /*]*/ #if defined(X3270_DISPLAY) /*[*/ /* * Called by the toolkit for any key without special actions. */ void Default_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { XKeyEvent *kevent = (XKeyEvent *)event; char buf[32]; KeySym ks; int ll; action_debug(Default_action, event, params, num_params); switch (event->type) { case KeyPress: #if defined(X3270_DBCS) /*[*/ if (!xim_lookup((XKeyEvent *)event)) return; #endif /*]*/ ll = XLookupString(kevent, buf, 32, &ks, (XComposeStatus *) 0); if (ll == 1) { /* Add Meta; XLookupString won't. */ if (event_is_meta(kevent->state)) buf[0] |= 0x80; /* Remap certain control characters. */ if (!IN_ANSI) switch (buf[0]) { case '\t': action_internal(Tab_action, IA_DEFAULT, CN, CN); break; case '\177': action_internal(Delete_action, IA_DEFAULT, CN, CN); break; case '\b': action_internal(Erase_action, IA_DEFAULT, CN, CN); break; case '\r': action_internal(Enter_action, IA_DEFAULT, CN, CN); break; case '\n': action_internal(Newline_action, IA_DEFAULT, CN, CN); break; default: key_ACharacter((unsigned char) buf[0], KT_STD, IA_DEFAULT); break; } else { key_ACharacter((unsigned char) buf[0], KT_STD, IA_DEFAULT); } return; } /* Pick some other reasonable defaults. */ switch (ks) { case XK_Up: action_internal(Up_action, IA_DEFAULT, CN, CN); break; case XK_Down: action_internal(Down_action, IA_DEFAULT, CN, CN); break; case XK_Left: action_internal(Left_action, IA_DEFAULT, CN, CN); break; case XK_Right: action_internal(Right_action, IA_DEFAULT, CN, CN); break; case XK_Insert: #if defined(XK_KP_Insert) /*[*/ case XK_KP_Insert: #endif /*]*/ action_internal(Insert_action, IA_DEFAULT, CN, CN); break; case XK_Delete: action_internal(Delete_action, IA_DEFAULT, CN, CN); break; case XK_Home: action_internal(Home_action, IA_DEFAULT, CN, CN); break; case XK_Tab: action_internal(Tab_action, IA_DEFAULT, CN, CN); break; #if defined(XK_ISO_Left_Tab) /*[*/ case XK_ISO_Left_Tab: action_internal(BackTab_action, IA_DEFAULT, CN, CN); break; #endif /*]*/ case XK_Clear: action_internal(Clear_action, IA_DEFAULT, CN, CN); break; case XK_Sys_Req: action_internal(SysReq_action, IA_DEFAULT, CN, CN); break; #if defined(XK_EuroSign) /*[*/ case XK_EuroSign: action_internal(Key_action, IA_DEFAULT, "currency", CN); break; #endif /*]*/ #if defined(XK_3270_Duplicate) /*[*/ /* Funky 3270 keysyms. */ case XK_3270_Duplicate: action_internal(Dup_action, IA_DEFAULT, CN, CN); break; case XK_3270_FieldMark: action_internal(FieldMark_action, IA_DEFAULT, CN, CN); break; case XK_3270_Right2: action_internal(Right2_action, IA_DEFAULT, CN, CN); break; case XK_3270_Left2: action_internal(Left2_action, IA_DEFAULT, CN, CN); break; case XK_3270_BackTab: action_internal(BackTab_action, IA_DEFAULT, CN, CN); break; case XK_3270_EraseEOF: action_internal(EraseEOF_action, IA_DEFAULT, CN, CN); break; case XK_3270_EraseInput: action_internal(EraseInput_action, IA_DEFAULT, CN, CN); break; case XK_3270_Reset: action_internal(Reset_action, IA_DEFAULT, CN, CN); break; case XK_3270_PA1: action_internal(PA_action, IA_DEFAULT, "1", CN); break; case XK_3270_PA2: action_internal(PA_action, IA_DEFAULT, "2", CN); break; case XK_3270_PA3: action_internal(PA_action, IA_DEFAULT, "3", CN); break; case XK_3270_Attn: action_internal(Attn_action, IA_DEFAULT, CN, CN); break; case XK_3270_AltCursor: action_internal(AltCursor_action, IA_DEFAULT, CN, CN); break; case XK_3270_CursorSelect: action_internal(CursorSelect_action, IA_DEFAULT, CN, CN); break; case XK_3270_Enter: action_internal(Enter_action, IA_DEFAULT, CN, CN); break; #endif /*]*/ #if defined(X3270_APL) /*[*/ /* Funky APL keysyms. */ case XK_downcaret: action_internal(Key_action, IA_DEFAULT, "apl_downcaret", CN); break; case XK_upcaret: action_internal(Key_action, IA_DEFAULT, "apl_upcaret", CN); break; case XK_overbar: action_internal(Key_action, IA_DEFAULT, "apl_overbar", CN); break; case XK_downtack: action_internal(Key_action, IA_DEFAULT, "apl_downtack", CN); break; case XK_upshoe: action_internal(Key_action, IA_DEFAULT, "apl_upshoe", CN); break; case XK_downstile: action_internal(Key_action, IA_DEFAULT, "apl_downstile", CN); break; case XK_underbar: action_internal(Key_action, IA_DEFAULT, "apl_underbar", CN); break; case XK_jot: action_internal(Key_action, IA_DEFAULT, "apl_jot", CN); break; case XK_quad: action_internal(Key_action, IA_DEFAULT, "apl_quad", CN); break; case XK_uptack: action_internal(Key_action, IA_DEFAULT, "apl_uptack", CN); break; case XK_circle: action_internal(Key_action, IA_DEFAULT, "apl_circle", CN); break; case XK_upstile: action_internal(Key_action, IA_DEFAULT, "apl_upstile", CN); break; case XK_downshoe: action_internal(Key_action, IA_DEFAULT, "apl_downshoe", CN); break; case XK_rightshoe: action_internal(Key_action, IA_DEFAULT, "apl_rightshoe", CN); break; case XK_leftshoe: action_internal(Key_action, IA_DEFAULT, "apl_leftshoe", CN); break; case XK_lefttack: action_internal(Key_action, IA_DEFAULT, "apl_lefttack", CN); break; case XK_righttack: action_internal(Key_action, IA_DEFAULT, "apl_righttack", CN); break; #endif /*]*/ default: if (ks >= XK_F1 && ks <= XK_F24) { (void) sprintf(buf, "%ld", ks - XK_F1 + 1); action_internal(PF_action, IA_DEFAULT, buf, CN); #if defined(XK_CYRILLIC) /*[*/ } else if (ks == XK_Cyrillic_io || ks == XK_Cyrillic_IO || (ks >= XK_Cyrillic_yu && ks <= XK_Cyrillic_HARDSIGN)) { /* Map XK to KOI8-R. */ (void) sprintf(buf, "%ld", ks & 0xff); action_internal(Key_action, IA_DEFAULT, buf, CN); #endif /*]*/ } else trace_event(" %s: dropped (unknown keysym)\n", action_name(Default_action)); break; } break; case ButtonPress: case ButtonRelease: trace_event(" %s: dropped (no action configured)\n", action_name(Default_action)); break; default: trace_event(" %s: dropped (unknown event type)\n", action_name(Default_action)); break; } } /* * Set or clear a temporary keymap. * * TemporaryKeymap(x) toggle keymap "x" (add "x" to the keymap, or if * "x" was already added, remove it) * TemporaryKeymap() removes the previous keymap, if any * TemporaryKeymap(None) removes the previous keymap, if any */ void TemporaryKeymap_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params) { action_debug(TemporaryKeymap_action, event, params, num_params); reset_idle_timer(); if (check_usage(TemporaryKeymap_action, *num_params, 0, 1) < 0) return; if (*num_params == 0 || !strcmp(params[0], "None")) { (void) temporary_keymap(CN); return; } if (temporary_keymap(params[0]) < 0) { popup_an_error("%s: Can't find %s %s", action_name(TemporaryKeymap_action), ResKeymap, params[0]); cancel_if_idle_command(); } } #endif /*]*/