1 /*
2  * Copyright (c) 2000-2020 Paul Mattes.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the names of Paul Mattes nor the names of his contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  *	screen.c
30  *		A curses-based 3270 Terminal Emulator
31  *		Screen drawing
32  */
33 
34 #include <wctype.h> /* for wint_t */
35 #include "globals.h"
36 #include <signal.h>
37 #include "appres.h"
38 #include "3270ds.h"
39 #include "resources.h"
40 #include "ctlr.h"
41 #include "toggles.h"
42 
43 #include "actions.h"
44 #include "codepage.h"
45 #include "ctlrc.h"
46 #include "cmenubar.h"
47 #include "cstatus.h"
48 #include "glue.h"
49 #include "host.h"
50 #include "keymap.h"
51 #include "kybd.h"
52 #include "lazya.h"
53 #include "names.h"
54 #include "popups.h"
55 #include "screen.h"
56 #include "see.h"
57 #include "status.h"
58 #include "task.h"
59 #include "telnet.h"
60 #include "toupper.h"
61 #include "trace.h"
62 #include "unicodec.h"
63 #include "utf8.h"
64 #include "utils.h"
65 #include "xio.h"
66 #include "xscroll.h"
67 
68 #include "cscreen.h"
69 
70 /*
71  * The usual x3270 COLS variable (current number of columns in the 3270
72  * display) is called cCOLS in c3270, to avoid a conflict with curses COLS (the
73  * number of columns on the physical termal). For c3270, globals.h #defines
74  * COLS as cCOLS, so common code can use COLS transparently -- everywhere but
75  * here. In this module, we #undef COLS, after #including globals.h but before
76  * #including curses.h, and we use (curses) COLS and (c3270) cCOLS explicitly.
77  */
78 #undef COLS
79 
80 #if defined(HAVE_NCURSESW_NCURSES_H) /*[*/
81 # include <ncursesw/ncurses.h>
82 #elif defined(HAVE_NCURSES_NCURSES_H) /*][*/
83 # include <ncurses/ncurses.h>
84 #elif defined(HAVE_NCURSES_H) /*][*/
85 # include <ncurses.h>
86 #else /*][*/
87 # include <curses.h>
88 #endif /*]*/
89 #if defined(HAVE_NCURSESW_TERM_H) /*[*/
90 # include <ncursesw/term.h>
91 #elif defined(HAVE_NCURSES_TERM_H) /*][*/
92 # include <ncurses/term.h>
93 #elif defined(HAVE_TERM_H) /*][*/
94 # include <term.h>
95 #endif /*]*/
96 
97 /* Curses' 'COLS' becomes cursesCOLS, to remove any ambiguity. */
98 #define cursesCOLS	COLS
99 #define cursesLINES	LINES
100 
101 #define STATUS_SCROLL_START_MS	1500
102 #define STATUS_SCROLL_MS	100
103 #define STATUS_PUSH_MS		5000
104 
105 #define CM (60*10)	/* csec per minute */
106 
107 #if !defined(HAVE_TIPARM) /*[*/
108 #define tiparm		tparm
109 #endif /*]*/
110 
111 typedef int curses_color;
112 typedef int curses_attr;
113 typedef int host_color_ix;
114 typedef int color_pair;
115 
116 static color_pair cp[16][16][2];
117 
118 static curses_color cmap8[16] = {
119     COLOR_BLACK,	/* neutral black */
120     COLOR_BLUE,		/* blue */
121     COLOR_RED,		/* red */
122     COLOR_MAGENTA,	/* pink */
123     COLOR_GREEN,	/* green */
124     COLOR_CYAN,		/* turquoise */
125     COLOR_YELLOW,	/* yellow */
126     COLOR_WHITE,	/* neutral white */
127 
128     COLOR_BLACK,	/* black */ /* alas, this may be gray */
129     COLOR_BLUE,		/* deep blue */
130     COLOR_YELLOW,	/* orange */
131     COLOR_MAGENTA,	/* purple */
132     COLOR_GREEN,	/* pale green */
133     COLOR_CYAN,		/* pale turquoise */
134     COLOR_BLACK,	/* gray */
135     COLOR_WHITE		/* white */
136 };
137 
138 static curses_color cmap8_rv[16] = {
139     COLOR_WHITE,	/* neutral black (reversed) */
140     COLOR_BLUE,		/* blue */
141     COLOR_RED,		/* red */
142     COLOR_MAGENTA,	/* pink */
143     COLOR_GREEN,	/* green */
144     COLOR_CYAN,		/* turquoise */
145     COLOR_YELLOW,	/* yellow */
146     COLOR_BLACK,	/* neutral white (reversed) */
147 
148     COLOR_BLACK,	/* black */ /* alas, this may be gray */
149     COLOR_BLUE,		/* deep blue */
150     COLOR_YELLOW,	/* orange */
151     COLOR_MAGENTA,	/* purple */
152     COLOR_GREEN,	/* pale green */
153     COLOR_CYAN,		/* pale turquoise */
154     COLOR_BLACK,	/* gray */
155     COLOR_WHITE		/* white */
156 };
157 
158 static curses_color cmap16[16] = {
159     COLOR_BLACK,	/* neutral black */
160     8 + COLOR_BLUE,	/* blue */
161     COLOR_RED,		/* red */
162     8 + COLOR_MAGENTA,	/* pink */
163     8 + COLOR_GREEN,	/* green */
164     8 + COLOR_CYAN,	/* turquoise */
165     8 + COLOR_YELLOW,	/* yellow */
166     8 + COLOR_WHITE,	/* neutral white */
167 
168     COLOR_BLACK,	/* black */ /* alas, this may be gray */
169     COLOR_BLUE,		/* deep blue */
170     8 + COLOR_RED,	/* orange */
171     COLOR_MAGENTA,	/* purple */
172     COLOR_GREEN,	/* pale green */
173     COLOR_CYAN,		/* pale turquoise */
174     COLOR_WHITE,	/* gray */
175     8 + COLOR_WHITE	/* white */
176 };
177 
178 static curses_color cmap16_rv[16] = {
179     8 + COLOR_WHITE,	/* neutral black (reversed) */
180     COLOR_BLUE,		/* blue */
181     COLOR_RED,		/* red */
182     8 + COLOR_MAGENTA,	/* pink */
183     COLOR_GREEN,	/* green */
184     COLOR_CYAN,		/* turquoise */
185     COLOR_YELLOW,	/* yellow */
186     COLOR_BLACK,	/* neutral white (reversed) */
187 
188     COLOR_BLACK,	/* black */ /* alas, this may be gray */
189     COLOR_BLUE,		/* deep blue */
190     8 + COLOR_RED,	/* orange */
191     COLOR_MAGENTA,	/* purple */
192     8 + COLOR_GREEN,	/* pale green */
193     8 + COLOR_CYAN,	/* pale turquoise */
194     8 + COLOR_WHITE,	/* gray */
195     8 + COLOR_WHITE	/* white */
196 };
197 
198 static curses_color *cmap = cmap8;
199 static curses_attr cattrmap[16] = {
200     A_NORMAL, A_NORMAL, A_NORMAL, A_NORMAL,
201     A_NORMAL, A_NORMAL, A_NORMAL, A_NORMAL,
202     A_NORMAL, A_NORMAL, A_NORMAL, A_NORMAL,
203     A_NORMAL, A_NORMAL, A_NORMAL, A_NORMAL
204 };
205 static int defcolor_offset = 0;
206 
207 static curses_color field_colors8[4] = {
208     COLOR_GREEN,	/* default */
209     COLOR_RED,		/* intensified */
210     COLOR_BLUE,		/* protected */
211     COLOR_WHITE		/* protected, intensified */
212 };
213 
214 static curses_color field_colors8_rv[4] = {
215     COLOR_GREEN,	/* default */
216     COLOR_RED,		/* intensified */
217     COLOR_BLUE,		/* protected */
218     COLOR_BLACK		/* protected, intensified */
219 };
220 
221 static curses_color field_colors16[4] = {
222     8 + COLOR_GREEN,	/* default */
223     COLOR_RED,		/* intensified */
224     8 + COLOR_BLUE,	/* protected */
225     8 + COLOR_WHITE	/* protected, intensified */
226 };
227 
228 static curses_color field_colors16_rv[4] = {
229     COLOR_GREEN,	/* default */
230     COLOR_RED,		/* intensified */
231     COLOR_BLUE,		/* protected */
232     COLOR_BLACK		/* protected, intensified */
233 };
234 
235 static curses_color *field_colors = field_colors8;
236 
237 static curses_attr field_cattrmap[4] = {
238     A_NORMAL, A_NORMAL, A_NORMAL, A_NORMAL
239 };
240 
241 static curses_color bg_color = COLOR_BLACK;
242 
243 static curses_attr defattr = A_NORMAL;
244 static curses_attr xhattr = A_NORMAL;
245 static ioid_t input_id = NULL_IOID;
246 
247 static int rmargin;
248 
249 bool escaped = true;
250 bool initscr_done = false;
251 
252 enum ts { TS_AUTO, TS_ON, TS_OFF };
253 enum ts me_mode = TS_AUTO;
254 enum ts ab_mode = TS_AUTO;
255 
256 #if defined(C3270_80_132) /*[*/
257 struct screen_spec {
258     int rows, cols;
259     char *mode_switch;
260 } screen_spec;
261 struct screen_spec altscreen_spec, defscreen_spec;
262 static SCREEN *def_screen = NULL, *alt_screen = NULL;
263 static SCREEN *cur_screen = NULL;
264 static void parse_screen_spec(const char *str, struct screen_spec *spec);
265 #endif /*]*/
266 
267 static struct {
268     char *name;
269     curses_color index;
270 } cc_name[] = {
271     { "black",			COLOR_BLACK },
272     { "red",			COLOR_RED },
273     { "green",			COLOR_GREEN },
274     { "yellow",			COLOR_YELLOW },
275     { "blue",			COLOR_BLUE },
276     { "magenta",    		COLOR_MAGENTA },
277     { "cyan",			COLOR_CYAN },
278     { "white",			COLOR_WHITE },
279     { "intensified-black",	8 + COLOR_BLACK },
280     { "intensified-red",	8 + COLOR_RED },
281     { "intensified-green",	8 + COLOR_GREEN },
282     { "intensified-yellow",	8 + COLOR_YELLOW },
283     { "intensified-blue",	8 + COLOR_BLUE },
284     { "intensified-magenta",    8 + COLOR_MAGENTA },
285     { "intensified-cyan",	8 + COLOR_CYAN },
286     { "intensified-white",	8 + COLOR_WHITE },
287     { NULL,			0 }
288 };
289 
290 static int status_row = 0;	/* Row to display the status line on */
291 static int status_skip = 0;	/* Row to blank above the status line */
292 static int screen_yoffset = 0;	/* Vertical offset to top of screen.
293 				   If 0, there is no menu bar.
294 				   If nonzero (2, actually), menu bar is at the
295 				    top of the display. */
296 
297 static host_color_ix crosshair_color = HOST_COLOR_PURPLE;
298 static bool curses_alt = false;
299 #if defined(HAVE_USE_DEFAULT_COLORS) /*[*/
300 static bool default_colors = false;
301 #endif /*]*/
302 static bool screen_initted = false;
303 
304 static ioid_t disabled_done_id = NULL_IOID;
305 
306 /* Layered OIA messages. */
307 static char *disabled_msg = NULL;	/* layer 0 (top) */
308 static char *scrolled_msg = NULL;	/* layer 1 */
309 static char *info_msg = NULL;		/* layer 2 */
310 static char *other_msg = NULL;		/* layer 3 */
311 static curses_attr other_attr;		/* layer 3 color */
312 
313 static char *info_base_msg = NULL;	/* original info message (unscrolled) */
314 
315 /* Terminfo state. */
316 static struct {
317     int colors;		/* number of colors */
318     char *op;		/* original pair (restore color) */
319     char *setaf;	/* set foreground */
320     char *sgr;		/* set graphic rendition */
321     char *sgr0;		/* reset graphic rendition */
322     bool bold;		/* use bold SGR for certain colors */
323 } ti;
324 
325 static void kybd_input(iosrc_t fd, ioid_t id);
326 static void kybd_input2(int k, ucs4_t ucs4, int alt);
327 static void draw_oia(void);
328 static void status_connect(bool ignored);
329 static void status_3270_mode(bool ignored);
330 static void status_printer(bool on);
331 static int get_color_pair(int fg, int bg);
332 static int color_from_fa(unsigned char);
333 static void set_status_row(int screen_rows, int emulator_rows);
334 static bool ts_value(const char *s, enum ts *tsp);
335 static void display_linedraw(ucs4_t ucs);
336 static void display_ge(unsigned char ebc);
337 static void init_user_colors(void);
338 static void init_user_attribute_colors(void);
339 static void screen_init2(void);
340 
341 static action_t Redraw_action;
342 
343 /*
344  * Crosshair color init.
345  */
346 static void
crosshair_color_init(void)347 crosshair_color_init(void)
348 {
349     int c;
350 
351     if (appres.interactive.crosshair_color != NULL) {
352 	c = decode_host_color(appres.interactive.crosshair_color);
353 	if (c >= 0) {
354 	    crosshair_color = c;
355 	    return;
356 	} else {
357 	    xs_warning("Invalid %s: %s", ResCrosshairColor,
358 		    appres.interactive.crosshair_color);
359 	}
360     }
361     crosshair_color = HOST_COLOR_PURPLE;
362 }
363 
364 /* Initialize the screen. */
365 void
screen_init(void)366 screen_init(void)
367 {
368     setupterm(NULL, fileno(stdout), NULL);
369 
370     menu_init();
371 
372 #if defined(C3270_80_132) /*[*/
373     /* Parse altscreen/defscreen. */
374     if ((appres.c3270.altscreen != NULL) ^
375 	(appres.c3270.defscreen != NULL)) {
376 	fprintf(stderr, "Must specify both altscreen and defscreen\n");
377 	exit(1);
378     }
379     if (appres.c3270.altscreen != NULL) {
380 	parse_screen_spec(appres.c3270.altscreen, &altscreen_spec);
381 	if (altscreen_spec.rows < 27 || altscreen_spec.cols < 132) {
382 	    fprintf(stderr, "Rows and/or cols too small on "
383 		"alternate screen (mininum 27x132)\n");
384 	    exit(1);
385 	}
386 	parse_screen_spec(appres.c3270.defscreen, &defscreen_spec);
387 	if (defscreen_spec.rows < 24 || defscreen_spec.cols < 80) {
388 	    fprintf(stderr, "Rows and/or cols too small on "
389 		"default screen (mininum 24x80)\n");
390 	    exit(1);
391 	}
392     }
393 #endif /*]*/
394 
395     /*
396      * See about keyboard Meta-key behavior.
397      *
398      * Note: Formerly, "auto" meant to use the terminfo 'km' capability (if
399      * set, then disable metaEscape).  But popular terminals like the
400      * Linux console and xterms are actually configurable, though they have
401      * fixed terminfo capabilities.  It is harmless to enable metaEscape
402      * when the terminal supports it, so the default is now 'on'.
403      *
404      * Setting the high bit for the Meta key is a pretty achaic idea, IMO,
405      * so we no loger support it.
406      */
407     if (!ts_value(appres.c3270.meta_escape, &me_mode))
408 	popup_an_error("Invalid %s value: '%s', assuming 'auto'\n",
409 		ResMetaEscape, appres.c3270.meta_escape);
410     if (me_mode == TS_AUTO) {
411 	me_mode = TS_ON;
412     }
413 
414     /*
415      * If they don't want ACS and they're not in a UTF-8 locale, switch
416      * to ASCII-art mode for box drawing.
417      */
418     if (
419 #if defined(CURSES_WIDE) /*[*/
420 	!appres.c3270.acs &&
421 #endif /*]*/
422 			     !is_utf8) {
423 	appres.c3270.ascii_box_draw = true;
424     }
425 
426     /* Initialize the controller. */
427     ctlr_init(ALL_CHANGE);
428 }
429 
430 /*
431  * Find and save a terminfo string.
432  */
433 static char *
ti_save(const char * name)434 ti_save(const char *name)
435 {
436     char *str = tigetstr((char *)name);
437 
438     if (str != NULL && str != (char *)-1) {
439 	return NewString(str);
440     }
441     return NULL;
442 }
443 
444 /*
445  * Returns true if the screen supports ANSI color sequences.
446  */
447 bool
screen_has_ansi_color(void)448 screen_has_ansi_color(void)
449 {
450     /* Check for disqualifying conditions. */
451     if (appres.interactive.mono ||
452         ((ti.colors = tigetnum("colors")) < 8) ||
453         ((ti.setaf = ti_save("setaf")) == NULL) ||
454         ((ti.op = ti_save("op")) == NULL)) {
455 	return false;
456     }
457 
458     /* Save the other strings, which are optional. */
459     ti.sgr = ti_save("sgr");
460     ti.sgr0 = ti_save("sgr0");
461 
462     /* Figure out bold mode. */
463     if (ti.sgr != NULL && ti.sgr0 != NULL) {
464 	if (appres.c3270.all_bold_on) {
465 	    ti.bold = true;
466 	} else {
467 	    enum ts ab;
468 
469 	    if (!ts_value(appres.c3270.all_bold, &ab)) {
470 		ab = TS_AUTO;
471 	    }
472 	    if (ab == TS_AUTO) {
473 		ti.bold = ti.colors < 16;
474 	    } else {
475 		ti.bold = (ab == TS_ON);
476 	    }
477 	}
478     }
479 
480     /* Recompute 'op'. */
481     if (ti.op != NULL && ti.sgr0 != NULL) {
482 	char *s = xs_buffer("%s%s", ti.op, ti.sgr0);
483 
484 	Replace(ti.op, s);
485     }
486 
487     return true;
488 }
489 
490 /*
491  * Returns the "op" (original pair) string.
492  */
493 const char *
screen_op(void)494 screen_op(void)
495 {
496     return ti.op;
497 }
498 
499 /*
500  * Returns the sequence to set a foreground color.
501  */
502 const char *
screen_setaf(acolor_t color)503 screen_setaf(acolor_t color)
504 {
505     static int color_map8[] = { COLOR_BLUE, COLOR_RED, COLOR_YELLOW };
506     static int color_map16[] = { 8 + COLOR_BLUE, COLOR_RED, 8 + COLOR_YELLOW };
507     char *setaf;
508 
509     setaf = tiparm(ti.setaf,
510 	    (ti.colors >= 16)? color_map16[color]: color_map8[color]);
511     setaf = lazya(NewString(setaf));
512     if (ti.bold && color_map16[color] >= 8) {
513 	char *sgr = tiparm(ti.sgr, 0, 0, 0, 0, 0, 1, 0, 0, 0);
514 
515 	return lazyaf("%s%s", sgr, setaf);
516     } else {
517 	return setaf;
518     }
519 }
520 
521 /*
522  * Finish screen initialization, when a host connects or when we go into
523  * 'zombie' mode (no prompt, no connection).
524  */
525 static void
finish_screen_init(void)526 finish_screen_init(void)
527 {
528     int want_ov_rows = ov_rows;
529     int want_ov_cols = ov_cols;
530     bool oversize = false;
531     char *cl;
532 
533     if (screen_initted) {
534 	return;
535     }
536 
537     screen_initted = true;
538 
539     /* Clear the (original) screen first. */
540 #if defined(C3270_80_132) /*[*/
541     if (appres.c3270.defscreen != NULL) {
542 	putenv(xs_buffer("COLUMNS=%d", defscreen_spec.cols));
543 	putenv(xs_buffer("LINES=%d", defscreen_spec.rows));
544     }
545 #endif /*]*/
546     if ((cl = tigetstr("clear")) != NULL) {
547 	putp(cl);
548     }
549 
550 #if !defined(C3270_80_132) /*[*/
551     /* Initialize curses. */
552     if (initscr() == NULL) {
553 	fprintf(stderr, "Can't initialize terminal.\n");
554 	exit(1);
555     }
556     initscr_done = true;
557 #else /*][*/
558     /* Set up ncurses, and see if it's within bounds. */
559     if (appres.c3270.defscreen != NULL) {
560 	putenv(xs_buffer("COLUMNS=%d", defscreen_spec.cols));
561 	putenv(xs_buffer("LINES=%d", defscreen_spec.rows));
562 	def_screen = newterm(NULL, stdout, stdin);
563 	initscr_done = true;
564 	if (def_screen == NULL) {
565 	    fprintf(stderr, "Can't initialize %dx%d defscreen terminal.\n",
566 		    defscreen_spec.rows, defscreen_spec.cols);
567 	    exit(1);
568 	}
569 	if (write(1, defscreen_spec.mode_switch,
570 		    strlen(defscreen_spec.mode_switch)) < 0) {
571 	    endwin();
572 	    exit(1);
573 	}
574     }
575     if (appres.c3270.altscreen) {
576 	putenv(xs_buffer("COLUMNS=%d", altscreen_spec.cols));
577 	putenv(xs_buffer("LINES=%d", altscreen_spec.rows));
578     }
579     alt_screen = newterm(NULL, stdout, stdin);
580     if (alt_screen == NULL) {
581 	popup_an_error("Can't initialize terminal.\n");
582 	exit(1);
583     }
584     initscr_done = true;
585     if (def_screen == NULL) {
586 	def_screen = alt_screen;
587 	cur_screen = def_screen;
588     }
589     if (appres.c3270.altscreen) {
590 	set_term(alt_screen);
591 	cur_screen = alt_screen;
592     }
593 
594     /* If they want 80/132 switching, then they want a model 5. */
595     if (def_screen != alt_screen && model_num != 5) {
596 	set_rows_cols(5, 0, 0);
597     }
598 #endif /*]*/
599 
600     while (cursesLINES < maxROWS || cursesCOLS < maxCOLS) {
601 	/*
602 	 * First, cancel any oversize.  This will get us to the correct
603 	 * model number, if there is any.
604 	 */
605 	if ((ov_cols && ov_cols > cursesCOLS) ||
606 	    (ov_rows && ov_rows > cursesLINES)) {
607 
608 	    ov_cols = 0;
609 	    ov_rows = 0;
610 	    oversize = true;
611 	    continue;
612 	}
613 
614 	/* If we're at the smallest screen now, give up. */
615 	if (model_num == 2) {
616 	    popup_an_error("Emulator won't fit on a %dx%d display.\n",
617 		    cursesLINES, cursesCOLS);
618 	    exit(1);
619 	}
620 
621 	/* Try a smaller model. */
622 	set_rows_cols(model_num - 1, 0, 0);
623     }
624 
625     /*
626      * Now, if they wanted an oversize, but didn't get it, try applying it
627      * again.
628      */
629     if (oversize) {
630 	if (want_ov_rows > cursesLINES - 2) {
631 	    want_ov_rows = cursesLINES - 2;
632 	}
633 	if (want_ov_rows < maxROWS) {
634 	    want_ov_rows = maxROWS;
635 	}
636 	if (want_ov_cols > cursesCOLS) {
637 	    want_ov_cols = cursesCOLS;
638 	}
639 	set_rows_cols(model_num, want_ov_cols, want_ov_rows);
640     }
641 
642     /*
643      * Finally, if they want automatic oversize, see if that's possible.
644      */
645     if (ov_auto && (maxROWS < cursesLINES - 3 || maxCOLS < cursesCOLS)) {
646 	set_rows_cols(model_num, cursesCOLS, cursesLINES - 3);
647     }
648 
649 #if defined(NCURSES_MOUSE_VERSION) /*[*/
650     if (appres.c3270.mouse && mousemask(BUTTON1_RELEASED, NULL) == 0) {
651 	appres.c3270.mouse = false;
652     }
653 #endif /*]*/
654 
655     /* Figure out where the status line goes, if it fits. */
656 #if defined(C3270_80_132) /*[*/
657     if (def_screen != alt_screen) {
658 	/* Start out in defscreen mode. */
659 	set_status_row(defscreen_spec.rows, MODEL_2_ROWS);
660     } else
661 #endif /*]*/
662     {
663 	/* Start out in altscreen mode. */
664 	set_status_row(cursesLINES, maxROWS);
665     }
666 
667     /* Implement reverse video. */
668     if (appres.c3270.reverse_video) {
669 	bg_color = COLOR_WHITE;
670     }
671 
672     /* Play with curses color. */
673     if (!appres.interactive.mono) {
674 #if defined(HAVE_USE_DEFAULT_COLORS) /*[*/
675 	char *colorterm;
676 #endif /*]*/
677 	start_color();
678 	if (has_colors() && COLORS >= 16) {
679 	    cmap = appres.c3270.reverse_video? cmap16_rv: cmap16;
680 	    field_colors = appres.c3270.reverse_video? field_colors16_rv:
681 		field_colors16;
682 	    if (appres.c3270.reverse_video) {
683 		bg_color += 8;
684 	    } else {
685 		defcolor_offset = 8;
686 	    }
687 	} else if (appres.c3270.reverse_video) {
688 	    cmap = cmap8_rv;
689 	    field_colors = field_colors8_rv;
690 	}
691 
692 	init_user_colors();
693 	init_user_attribute_colors();
694 	crosshair_color_init();
695 
696 	/* See about all-bold behavior. */
697 	if (appres.c3270.all_bold_on) {
698 	    ab_mode = TS_ON;
699 	} else if (!ts_value(appres.c3270.all_bold, &ab_mode)) {
700 	    popup_an_error("Invalid %s value: '%s', assuming 'auto'\n",
701 		    ResAllBold, appres.c3270.all_bold);
702 	}
703 	if (ab_mode == TS_AUTO) {
704 	    ab_mode = (mode.m3279 && (COLORS < 16) &&
705 		    !appres.c3270.reverse_video)? TS_ON: TS_OFF;
706 	}
707 	if (ab_mode == TS_ON) {
708 	    int i;
709 
710 	    defattr |= A_BOLD;
711 	    for (i = 0; i < 4; i++) {
712 		field_cattrmap[i] = A_BOLD;
713 	    }
714 	}
715 
716 #if defined(HAVE_USE_DEFAULT_COLORS) /*[*/
717 	if ((appres.c3270.default_fgbg ||
718 	     (((colorterm = getenv("COLORTERM")) != NULL &&
719 	      !strcmp(colorterm, "gnome-terminal")) ||
720 	      getenv("VTE_VERSION") != NULL)) &&
721 	    use_default_colors() != ERR) {
722 
723 	    default_colors = true;
724 	}
725 #endif /*]*/
726 	if (has_colors() && COLORS >= 8) {
727 	    if (mode.m3279) {
728 		/* Use 'protected' attributes for the OIA. */
729 		defattr = get_color_pair(field_colors[2], bg_color) |
730 		    field_cattrmap[2];
731 		xhattr = get_color_pair(defcolor_offset + cmap[crosshair_color],
732 			bg_color) | cattrmap[crosshair_color];
733 	    } else {
734 		defattr = get_color_pair(defcolor_offset + COLOR_GREEN,
735 			bg_color);
736 		xhattr = get_color_pair(defcolor_offset + COLOR_GREEN,
737 			bg_color);
738 	    }
739 #if defined(C3270_80_132) && defined(NCURSES_VERSION)  /*[*/
740 	    if (def_screen != alt_screen) {
741 		SCREEN *s = cur_screen;
742 
743 		/* Initialize the colors for the other screen. */
744 		if (s == def_screen) {
745 		    set_term(alt_screen);
746 		} else {
747 		    set_term(def_screen);
748 		}
749 		start_color();
750 		curses_alt = !curses_alt;
751 		get_color_pair(field_colors[2], bg_color);
752 		curses_alt = !curses_alt;
753 		set_term(s);
754 
755 	    }
756 #endif /*]*/
757 	} else {
758 	    appres.interactive.mono = true;
759 	    mode.m3279 = false;
760 	    /* Get the terminal name right. */
761 	    set_rows_cols(model_num, want_ov_cols, want_ov_rows);
762 	}
763     }
764 
765     /* Set up the scrollbar. */
766     scroll_buf_init();
767 
768     screen_init2();
769 }
770 
771 /* Configure the TTY settings for a curses screen. */
772 static void
setup_tty(void)773 setup_tty(void)
774 {
775     extern void pause_for_errors(void);
776 
777     if (appres.c3270.cbreak_mode) {
778 	cbreak();
779     } else {
780 	raw();
781     }
782     noecho();
783     nonl();
784     intrflush(stdscr,FALSE);
785     if (appres.c3270.curses_keypad) {
786 	keypad(stdscr, TRUE);
787     }
788     meta(stdscr, TRUE);
789     nodelay(stdscr, TRUE);
790     refresh();
791 }
792 
793 #if defined(C3270_80_132) /*[*/
794 static void
swap_screens(SCREEN * new_screen)795 swap_screens(SCREEN *new_screen)
796 {
797     set_term(new_screen);
798     cur_screen = new_screen;
799 }
800 #endif /*]*/
801 
802 /* Secondary screen initialization. */
803 static void
screen_init2(void)804 screen_init2(void)
805 {
806     escaped = false;
807 
808     /*
809      * Finish initializing ncurses.  This should be the first time that it
810      * will send anything to the terminal.
811      */
812 
813     /* Set up the keyboard. */
814 #if defined(C3270_80_132) /*[*/
815     swap_screens(alt_screen);
816 #endif /*]*/
817     setup_tty();
818     scrollok(stdscr, FALSE);
819 
820 #if defined(C3270_80_132) /*[*/
821     if (def_screen != alt_screen) {
822 	/*
823 	 * The first setup_tty() set up altscreen.
824 	 * Set up defscreen now, and leave it as the
825 	 * current curses screen.
826 	 */
827 	swap_screens(def_screen);
828 	setup_tty();
829 	scrollok(stdscr, FALSE);
830 #if defined(NCURSES_MOUSE_VERSION) /*[*/
831 	if (appres.c3270.mouse) {
832 	    mousemask(BUTTON1_RELEASED, NULL);
833 	}
834 #endif /*]*/
835     }
836 #endif /*]*/
837 
838     /* Subscribe to input events. */
839     if (input_id == NULL_IOID) {
840 	input_id = AddInput(0, kybd_input);
841     }
842 
843     /* Ignore SIGINT and SIGTSTP. */
844     signal(SIGINT, SIG_IGN);
845     signal(SIGTSTP, SIG_IGN);
846 
847 #if defined(C3270_80_132) /*[*/
848     /* Ignore SIGWINCH -- it might happen when we do 80/132 changes. */
849     if (def_screen != alt_screen) {
850 	signal(SIGWINCH, SIG_IGN);
851     }
852 #endif /*]*/
853 }
854 
855 /* Calculate where the status line goes now. */
856 static void
set_status_row(int screen_rows,int emulator_rows)857 set_status_row(int screen_rows, int emulator_rows)
858 {
859     /* Check for OIA room first. */
860     if (screen_rows < emulator_rows + 1) {
861 	status_row = status_skip = 0;
862     } else if (screen_rows == emulator_rows + 1) {
863 	status_skip = 0;
864 	status_row = emulator_rows;
865     } else {
866 	status_skip = screen_rows - 2;
867 	status_row = screen_rows - 1;
868     }
869 
870     /* Then check for menubar room.  Use 2 rows, 1 in a pinch. */
871     if (appres.interactive.menubar && appres.c3270.mouse) {
872 	if (screen_rows >= emulator_rows + (status_row != 0) + 2) {
873 	    screen_yoffset = 2;
874 	} else if (screen_rows >= emulator_rows + (status_row != 0) + 1) {
875 	    screen_yoffset = 1;
876 	} else {
877 	    screen_yoffset = 0;
878 	}
879     }
880 }
881 
882 /*
883  * Parse a tri-state resource value.
884  * Returns true for success, false for failure.
885  */
886 static bool
ts_value(const char * s,enum ts * tsp)887 ts_value(const char *s, enum ts *tsp)
888 {
889     *tsp = TS_AUTO;
890 
891     if (s != NULL && s[0]) {
892 	int sl = strlen(s);
893 
894 	if (!strncasecmp(s, "true", sl)) {
895 	    *tsp = TS_ON;
896 	} else if (!strncasecmp(s, "false", sl)) {
897 	    *tsp = TS_OFF;
898 	} else if (strncasecmp(s, "auto", sl)) {
899 	    return false;
900 	}
901     }
902     return true;
903 }
904 
905 /* Allocate a color pair. */
906 static curses_attr
get_color_pair(curses_color fg,curses_color bg)907 get_color_pair(curses_color fg, curses_color bg)
908 {
909     static int next_pair[2] = { 1, 1 };
910     color_pair pair;
911 #if defined(C3270_80_132) && defined(NCURSES_VERSION) /*[*/
912 	    /* ncurses allocates colors for each screen. */
913     int pair_index = !!curses_alt;
914 #else /*][*/
915 	    /* curses allocates colors globally. */
916     const int pair_index = 0;
917 #endif /*]*/
918     curses_color bg_arg = bg;
919     curses_color fg_arg = fg;
920 
921     if ((pair = cp[fg][bg][pair_index])) {
922 	return COLOR_PAIR(pair);
923     }
924     if (next_pair[pair_index] >= COLOR_PAIRS) {
925 	return 0;
926     }
927 #if defined(HAVE_USE_DEFAULT_COLORS) /*[*/
928     /*
929      * Assume that by default, the terminal displays some sort of 'white'
930      * against some sort of 'black', and that looks better than the
931      * explicit curses COLOR_WHITE over COLOR_BLACK.
932      */
933     if (default_colors) {
934 	if (bg == COLOR_BLACK) {
935 	    bg_arg = -1; /* use the default background, not black */
936 	}
937 	if (fg == COLOR_WHITE) {
938 	    fg_arg = -1; /* use the default foreground, not white */
939 	}
940     }
941 #endif /*]*/
942     if (init_pair(next_pair[pair_index], fg_arg, bg_arg) != OK) {
943 	return 0;
944     }
945     pair = cp[fg][bg][pair_index] = next_pair[pair_index]++;
946     return COLOR_PAIR(pair);
947 }
948 
949 /*
950  * Initialize the user-specified attribute color mappings.
951  */
952 static void
init_user_attribute_color(curses_color * color,curses_attr * attr,const char * resname)953 init_user_attribute_color(curses_color *color, curses_attr *attr,
954 	const char *resname)
955 {
956     char *r;
957     unsigned long l;
958     char *ptr;
959     int i;
960 
961     if ((r = get_resource(resname)) == NULL) {
962 	return;
963     }
964     for (i = 0; cc_name[i].name != NULL; i++) {
965 	if (!strcasecmp(r, cc_name[i].name)) {
966 	    if (cc_name[i].index < COLORS) {
967 		*color = cc_name[i].index;
968 	    } else {
969 		*color = cc_name[i].index - 8;
970 		*attr = A_BOLD;
971 	    }
972 	    return;
973 	}
974     }
975     l = strtoul(r, &ptr, 0);
976     if (ptr == r || *ptr != '\0') {
977 	xs_warning("Invalid %s value: %s", resname, r);
978 	return;
979     }
980     if ((int)l >= COLORS) {
981 	if (l < 16 && COLORS == 8) {
982 	    *color = (int)l;
983 	    *attr = A_BOLD;
984 	} else {
985 	    xs_warning("Invalid %s value %s exceeds maximum color index %d",
986 		    resname, r, COLORS - 1);
987 	    return;
988 	}
989     }
990     *color = (int)l;
991 }
992 
993 static void
init_user_attribute_colors(void)994 init_user_attribute_colors(void)
995 {
996     init_user_attribute_color(&field_colors[0], &field_cattrmap[0],
997 	    ResCursesColorForDefault);
998     init_user_attribute_color(&field_colors[1], &field_cattrmap[0],
999 	    ResCursesColorForIntensified);
1000     init_user_attribute_color(&field_colors[2], &field_cattrmap[2],
1001 	    ResCursesColorForProtected);
1002     init_user_attribute_color(&field_colors[3], &field_cattrmap[3],
1003 	    ResCursesColorForProtectedIntensified);
1004 }
1005 
1006 /*
1007  * Map a field attribute to a curses color index.
1008  * Applies only to m3279 mode -- does not work for mono.
1009  */
1010 #define DEFCOLOR_MAP(f) \
1011 	((((f) & FA_PROTECT) >> 4) | (((f) & FA_INT_HIGH_SEL) >> 3))
1012 static curses_color
default_color_from_fa(unsigned char fa)1013 default_color_from_fa(unsigned char fa)
1014 {
1015     return field_colors[DEFCOLOR_MAP(fa)];
1016 }
1017 
1018 static int
attrmap_from_fa(unsigned char fa)1019 attrmap_from_fa(unsigned char fa)
1020 {
1021     return DEFCOLOR_MAP(fa);
1022 }
1023 
1024 static curses_attr
color_from_fa(unsigned char fa)1025 color_from_fa(unsigned char fa)
1026 {
1027     if (mode.m3279) {
1028 	int ai = attrmap_from_fa(fa);
1029 	curses_color fg = default_color_from_fa(fa);
1030 
1031 	return get_color_pair(fg, bg_color) |
1032 	    (((ab_mode == TS_ON) || FA_IS_HIGH(fa))? A_BOLD: A_NORMAL) |
1033 	    field_cattrmap[ai];
1034     } else if (!appres.interactive.mono) {
1035 	return get_color_pair(defcolor_offset + COLOR_GREEN, bg_color) |
1036 	    (((ab_mode == TS_ON) || FA_IS_HIGH(fa))? A_BOLD: A_NORMAL);
1037     } else {
1038 	/* No color at all. */
1039 	return ((ab_mode == TS_ON) || FA_IS_HIGH(fa))? A_BOLD: A_NORMAL;
1040     }
1041 }
1042 
1043 /*
1044  * Set up the user-specified color mappings.
1045  */
1046 void
init_user_color(const char * name,host_color_ix ix)1047 init_user_color(const char *name, host_color_ix ix)
1048 {
1049     char *r;
1050     int i;
1051     unsigned long l;
1052     curses_color il;
1053     char *ptr;
1054 
1055     r = get_fresource("%s%s", ResCursesColorForHostColor, name);
1056     if (r == NULL) {
1057 	r = get_fresource("%s%d", ResCursesColorForHostColor, ix);
1058     }
1059     if (r == NULL) {
1060 	return;
1061     }
1062 
1063     for (i = 0; cc_name[i].name != NULL; i++) {
1064 	if (!strcasecmp(r, cc_name[i].name)) {
1065 	    cmap[ix] = cc_name[i].index;
1066 	    if (COLORS < 16 && cmap[ix] > 8) {
1067 		/*
1068 		 * When there are only 8 colors, the intensified colors are
1069 		 * mapped to bold.
1070 		 */
1071 		cmap[ix] -= 8;
1072 		cattrmap[ix] = A_BOLD;
1073 	    }
1074 	    return;
1075 	}
1076     }
1077 
1078     l = strtoul(r, &ptr, 0);
1079     if (ptr == r || *ptr == '\0') {
1080 	xs_warning("Invalid %s value '%s'", ResCursesColorForHostColor, r);
1081 	return;
1082     }
1083     il = (curses_color)l;
1084     if (COLORS < 16 && il > 8 && il <= 16) {
1085 	/* Map 9..15 to 0..8 + bold as above. */
1086 	cmap[ix] = il - 8;
1087 	cattrmap[ix] = A_BOLD;
1088 	return;
1089     }
1090     if (il < COLORS) {
1091 	cmap[ix] = il;
1092 	return;
1093     }
1094 
1095     xs_warning("Out of range %s value '%s'", ResCursesColorForHostColor, r);
1096 }
1097 
1098 static void
init_user_colors(void)1099 init_user_colors(void)
1100 {
1101     int i;
1102 
1103     for (i = 0; host_color[i].name != NULL; i++) {
1104 	init_user_color(host_color[i].name, host_color[i].index);
1105     }
1106 }
1107 
1108 /*
1109  * Find the display attributes for a baddr, fa_addr and fa.
1110  */
1111 static curses_attr
calc_attrs(int baddr,int fa_addr,int fa)1112 calc_attrs(int baddr, int fa_addr, int fa)
1113 {
1114     curses_color fg, bg;
1115     int gr;
1116     curses_attr a;
1117 
1118     if (FA_IS_ZERO(fa)) {
1119 	return color_from_fa(fa);
1120     }
1121 
1122     /* Compute the color. */
1123 
1124     /*
1125      * Monochrome is easy, and so is color if nothing is
1126      * specified.
1127      */
1128     if (!mode.m3279 ||
1129 	    (!ea_buf[baddr].fg &&
1130 	     !ea_buf[fa_addr].fg &&
1131 	     !ea_buf[baddr].bg &&
1132 	     !ea_buf[fa_addr].bg)) {
1133 
1134 	a = color_from_fa(fa);
1135 
1136     } else {
1137 	host_color_ix ix;
1138 	curses_attr attr;
1139 
1140 	/* The current location or the fa specifies the fg or bg. */
1141 	if (ea_buf[baddr].fg) {
1142 	    ix = ea_buf[baddr].fg & 0x0f;
1143 	    fg = cmap[ix];
1144 	    attr = cattrmap[ix];
1145 	} else if (ea_buf[fa_addr].fg) {
1146 	    ix = ea_buf[fa_addr].fg & 0x0f;
1147 	    fg = cmap[ix];
1148 	    attr = cattrmap[ix];
1149 	} else {
1150 	    ix = attrmap_from_fa(fa);
1151 	    fg = default_color_from_fa(fa);
1152 	    attr = field_cattrmap[attrmap_from_fa(fa)];
1153 	}
1154 
1155 	if (ea_buf[baddr].bg) {
1156 	    bg = cmap[ea_buf[baddr].bg & 0x0f];
1157 	} else if (ea_buf[fa_addr].bg) {
1158 	    bg = cmap[ea_buf[fa_addr].bg & 0x0f];
1159 	} else {
1160 	    bg = cmap[HOST_COLOR_NEUTRAL_BLACK];
1161 	}
1162 
1163 	a = get_color_pair(fg, bg) | attr;
1164     }
1165 
1166     /* Compute the display attributes. */
1167     if (ea_buf[baddr].gr) {
1168 	gr = ea_buf[baddr].gr;
1169     } else if (ea_buf[fa_addr].gr) {
1170 	gr = ea_buf[fa_addr].gr;
1171     } else {
1172 	gr = 0;
1173     }
1174 
1175     if (gr & GR_BLINK) {
1176 	a |= A_BLINK;
1177     }
1178     if (gr & GR_REVERSE) {
1179 	a |= A_REVERSE;
1180     }
1181     if (gr & GR_UNDERLINE) {
1182 	a |= A_UNDERLINE;
1183     }
1184     if ((gr & GR_INTENSIFY) || (ab_mode == TS_ON) || FA_IS_HIGH(fa)) {
1185 	a |= A_BOLD;
1186     }
1187 
1188     return a;
1189 }
1190 
1191 /*
1192  * Return a visible control character for a field attribute.
1193  */
1194 static unsigned char
visible_fa(unsigned char fa)1195 visible_fa(unsigned char fa)
1196 {
1197     static unsigned char varr[32] = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
1198 
1199     unsigned ix;
1200 
1201     /*
1202      * This code knows that:
1203      *  FA_PROTECT is   0b100000, and we map it to 0b010000
1204      *  FA_NUMERIC is   0b010000, and we map it to 0b001000
1205      *  FA_INTENSITY is 0b001100, and we map it to 0b000110
1206      *  FA_MODIFY is    0b000001, and we copy to   0b000001
1207      */
1208     ix = ((fa & (FA_PROTECT | FA_NUMERIC | FA_INTENSITY)) >> 1) |
1209 	(fa & FA_MODIFY);
1210     return varr[ix];
1211 }
1212 
1213 /**
1214  * Return a space or a line-drawing character, depending on whether the
1215  * given buffer address has a crosshair cursor on it.
1216  *
1217  * @param[in] baddr		Buffer address
1218  * @param[out] acs		Returned true if the returned character is a
1219  * 				curses ACS code
1220  *
1221  * @return Blank if not a crosshair region, possibly an ACS code (if acs
1222  *         returned true), possibly an ASCII-art character (if asciiBoxDraw is
1223  *         set), possibly a Unicode line-drawing character.
1224  */
1225 static ucs4_t
crosshair_blank(int baddr,unsigned char * acs)1226 crosshair_blank(int baddr, unsigned char *acs)
1227 {
1228     ucs4_t u = ' ';
1229 
1230     *acs = 0;
1231     if (toggled(CROSSHAIR)) {
1232 	bool same_row = ((baddr / cCOLS) == (cursor_addr / cCOLS));
1233 	bool same_col = ((baddr % cCOLS) == (cursor_addr % cCOLS));
1234 
1235 	if (same_row && same_col) {
1236 	    map_acs('n', &u, acs); /* cross */
1237 	} else if (same_row) {
1238 	    map_acs('q', &u, acs); /* horizontal */
1239 	} else if (same_col) {
1240 	    map_acs('x', &u, acs); /* vertical */
1241 	}
1242     }
1243     return u;
1244 }
1245 
1246 /**
1247  * Draw a crosshair line-drawing character returned by crosshair_blank().
1248  *
1249  * @param[in] u		Line-drawing character
1250  * @param[in] acs	true if u is a curses ACS code
1251  */
1252 static void
draw_crosshair(ucs4_t u,bool acs)1253 draw_crosshair(ucs4_t u, bool acs)
1254 {
1255     char mb[16];
1256 
1257     attrset(xhattr);
1258 #if defined(CURSES_WIDE) /*[*/
1259     if (u < 0x100 || acs) {
1260 	addch(u);
1261     } else if (unicode_to_multibyte(u, mb, sizeof(mb))) {
1262 	addstr(mb);
1263     } else {
1264 	addch(' ');
1265     }
1266 #else /*][*/
1267     addch(u);
1268 #endif /*]*/
1269 }
1270 
1271 /* Display what's in the buffer. */
1272 void
screen_disp(bool erasing _is_unused)1273 screen_disp(bool erasing _is_unused)
1274 {
1275     int row, col;
1276     curses_attr field_attrs;
1277     unsigned char fa;
1278     struct screen_spec *cur_spec;
1279     enum dbcs_state d;
1280     int fa_addr;
1281     char mb[16];
1282 
1283     /* This may be called when it isn't time. */
1284     if (escaped) {
1285 	return;
1286     }
1287 
1288 #if defined(C3270_80_132) /*[*/
1289     /* See if they've switched screens on us. */
1290     if (def_screen != alt_screen && screen_alt != curses_alt) {
1291 	if (screen_alt) {
1292 	    if (write(1, altscreen_spec.mode_switch,
1293 			strlen(altscreen_spec.mode_switch)) < 0) {
1294 		exit(1);
1295 	    }
1296 	    vtrace("Switching to alt (%dx%d) screen.\n",
1297 		    altscreen_spec.rows, altscreen_spec.cols);
1298 	    swap_screens(alt_screen);
1299 	    cur_spec = &altscreen_spec;
1300 	} else {
1301 	    if (write(1, defscreen_spec.mode_switch,
1302 			strlen(defscreen_spec.mode_switch)) < 0) {
1303 		exit(1);
1304 	    }
1305 	    vtrace("Switching to default (%dx%d) screen.\n",
1306 		    defscreen_spec.rows, defscreen_spec.cols);
1307 	    swap_screens(def_screen);
1308 	    cur_spec = &defscreen_spec;
1309 	}
1310 
1311 	/* Figure out where the status line goes now, if it fits. */
1312 	set_status_row(cur_spec->rows, ROWS);
1313 
1314 	curses_alt = screen_alt;
1315 
1316 	/* Tell curses to forget what may be on the screen already. */
1317 	clear();
1318     }
1319 #endif /*]*/
1320 
1321     /* If the menubar is separate, draw it first. */
1322     if (screen_yoffset) {
1323 	ucs4_t u = 0;
1324 	bool highlight;
1325 	unsigned char acs;
1326 	curses_attr norm, high;
1327 
1328 	if (menu_is_up) {
1329 	    if (mode.m3279) {
1330 		norm = get_color_pair(COLOR_WHITE, COLOR_BLACK);
1331 		high = get_color_pair(COLOR_BLACK, COLOR_WHITE);
1332 	    } else {
1333 		norm = defattr & ~A_BOLD;
1334 		high = defattr | A_BOLD;
1335 	    }
1336 	} else {
1337 	    if (mode.m3279) {
1338 		norm = get_color_pair(COLOR_WHITE, COLOR_BLACK);
1339 		high = get_color_pair(COLOR_WHITE, COLOR_BLACK);
1340 	    } else {
1341 		norm = defattr & ~A_BOLD;
1342 		high = defattr & ~A_BOLD;
1343 	    }
1344 	}
1345 
1346 	for (row = 0; row < screen_yoffset; row++) {
1347 	    move(row, 0);
1348 	    for (col = 0; col < cCOLS; col++) {
1349 		if (menu_char(row, col, true, &u, &highlight, &acs)) {
1350 		    attrset(highlight? high: norm);
1351 #if defined(CURSES_WIDE) /*[*/
1352 		    if (u < 0x100 || acs) {
1353 			addch(u);
1354 		    } else if (unicode_to_multibyte(u, mb, sizeof(mb))) {
1355 			addstr(mb);
1356 		    } else {
1357 			addch(' ');
1358 		    }
1359 #else /*][*/
1360 		    addch(u);
1361 #endif /*]*/
1362 		} else {
1363 		    attrset(norm);
1364 		    addch(' ');
1365 		}
1366 	    }
1367 	}
1368     }
1369 
1370     fa = get_field_attribute(0);
1371     fa_addr = find_field_attribute(0);
1372     field_attrs = calc_attrs(fa_addr, fa_addr, fa);
1373     for (row = 0; row < ROWS; row++) {
1374 	int baddr;
1375 
1376 	if (!flipped) {
1377 	    move(row + screen_yoffset, 0);
1378 	}
1379 	for (col = 0; col < cCOLS; col++) {
1380 	    bool underlined = false;
1381 	    curses_attr attr_mask = toggled(UNDERSCORE)? (int)~A_UNDERLINE: -1;
1382 	    bool is_menu = false;
1383 	    ucs4_t u = 0;
1384 	    bool highlight = false;
1385 	    unsigned char acs = 0;
1386 
1387 	    if (flipped) {
1388 		move(row + screen_yoffset, cCOLS-1 - col);
1389 	    }
1390 
1391 	    is_menu = menu_char(row + screen_yoffset,
1392 		    flipped? (cCOLS-1 - col): col,
1393 		    false,
1394 		    &u, &highlight, &acs);
1395 	    if (is_menu) {
1396 
1397 		if (!u) {
1398 		    abort();
1399 		}
1400 		if (mode.m3279) {
1401 		    if (highlight) {
1402 			attrset(get_color_pair(HOST_COLOR_NEUTRAL_BLACK,
1403 				    HOST_COLOR_NEUTRAL_WHITE));
1404 		    } else {
1405 			attrset(get_color_pair(HOST_COLOR_NEUTRAL_WHITE,
1406 				    HOST_COLOR_NEUTRAL_BLACK));
1407 		    }
1408 		} else {
1409 		    if (highlight) {
1410 			attrset(defattr | A_BOLD);
1411 		    } else {
1412 			attrset(defattr);
1413 		    }
1414 		}
1415 #if defined(CURSES_WIDE) /*[*/
1416 		if (u < 0x100 || acs) {
1417 		    addch(u);
1418 		} else if (unicode_to_multibyte(u, mb, sizeof(mb))) {
1419 		    addstr(mb);
1420 		} else {
1421 		    addch(' ');
1422 		}
1423 #else /*][*/
1424 		addch(u);
1425 #endif /*]*/
1426 	    }
1427 
1428 	    baddr = row*cCOLS+col;
1429 	    if (ea_buf[baddr].fa) {
1430 		fa_addr = baddr;
1431 		fa = ea_buf[baddr].fa;
1432 		field_attrs = calc_attrs(baddr, baddr, fa);
1433 		if (!is_menu) {
1434 		    if (toggled(VISIBLE_CONTROL)) {
1435 			attrset(get_color_pair(COLOR_YELLOW,
1436 				    COLOR_BLACK) | A_BOLD | A_UNDERLINE);
1437 			addch(visible_fa(fa));
1438 		    } else {
1439 			u = crosshair_blank(baddr, &acs);
1440 			if (u == ' ') {
1441 			    attrset(defattr);
1442 			    addch(' ');
1443 			} else {
1444 			    draw_crosshair(u, acs);
1445 			}
1446 		    }
1447 		}
1448 	    } else if (FA_IS_ZERO(fa)) {
1449 		if (!is_menu) {
1450 		    u = crosshair_blank(baddr, &acs);
1451 		    if (u == ' ') {
1452 			attrset(field_attrs & attr_mask);
1453 			addch(' ');
1454 		    } else {
1455 			draw_crosshair(u, acs);
1456 		    }
1457 		    if (field_attrs & A_UNDERLINE) {
1458 			underlined = true;
1459 		    }
1460 		}
1461 	    } else {
1462 		char mb[16];
1463 		int len;
1464 		int attrs;
1465 
1466 		if (is_menu) {
1467 		    continue;
1468 		}
1469 
1470 		if (!(ea_buf[baddr].gr ||
1471 		      ea_buf[baddr].fg ||
1472 		      ea_buf[baddr].bg)) {
1473 		    attrs = field_attrs & attr_mask;
1474 		    attrset(attrs);
1475 		    if (field_attrs & A_UNDERLINE) {
1476 			underlined = true;
1477 		    }
1478 
1479 		} else {
1480 		    int buf_attrs;
1481 
1482 		    buf_attrs = calc_attrs(baddr, fa_addr, fa);
1483 		    attrs = buf_attrs & attr_mask;
1484 		    attrset(attrs);
1485 		    if (buf_attrs & A_UNDERLINE) {
1486 			underlined = true;
1487 		    }
1488 		}
1489 		d = ctlr_dbcs_state(baddr);
1490 		if (IS_LEFT(d)) {
1491 		    int xaddr = baddr;
1492 
1493 		    INC_BA(xaddr);
1494 		    if (toggled(VISIBLE_CONTROL) &&
1495 			    ea_buf[baddr].ec == EBC_null &&
1496 			    ea_buf[xaddr].ec == EBC_null) {
1497 			attrset(attrs | A_UNDERLINE);
1498 			addstr("..");
1499 		    } else {
1500 			if (ea_buf[baddr].ucs4 != 0) {
1501 			    len = unicode_to_multibyte(ea_buf[baddr].ucs4,
1502 				    mb, sizeof(mb));
1503 			} else {
1504 			    len = ebcdic_to_multibyte(
1505 				    (ea_buf[baddr].ec << 8) |
1506 				     ea_buf[xaddr].ec,
1507 				    mb, sizeof(mb));
1508 			}
1509 			addstr(mb);
1510 		    }
1511 		} else if (!IS_RIGHT(d)) {
1512 		    if (toggled(VISIBLE_CONTROL) &&
1513 			    ea_buf[baddr].ucs4 == 0 &&
1514 			    ea_buf[baddr].ec == EBC_null) {
1515 			attrset(attrs | A_UNDERLINE);
1516 			addstr(".");
1517 		    } else if (toggled(VISIBLE_CONTROL) &&
1518 			    ea_buf[baddr].ec == EBC_so) {
1519 			attrset(attrs | A_UNDERLINE);
1520 			addstr("<");
1521 		    } else if (toggled(VISIBLE_CONTROL) &&
1522 			    ea_buf[baddr].ec == EBC_si) {
1523 			attrset(attrs | A_UNDERLINE);
1524 			addstr(">");
1525 		    } else if (ea_buf[baddr].cs == CS_LINEDRAW) {
1526 			display_linedraw(ea_buf[baddr].ucs4);
1527 		    } else if (ea_buf[baddr].cs == CS_APL ||
1528 			    (ea_buf[baddr].cs & CS_GE)) {
1529 			display_ge(ea_buf[baddr].ec);
1530 		    } else {
1531 			bool done_sbcs = false;
1532 			ucs4_t uu;
1533 
1534 			if ((uu = ea_buf[baddr].ucs4) != 0) {
1535 			    if (toggled(MONOCASE)) {
1536 				uu = u_toupper(uu);
1537 			    }
1538 			    len = unicode_to_multibyte(uu, mb, sizeof(mb));
1539 			} else {
1540 			    unsigned flags = EUO_BLANK_UNDEF |
1541 			       (appres.c3270.ascii_box_draw? EUO_ASCII_BOX: 0) |
1542 			       (toggled(MONOCASE)? EUO_TOUPPER: 0);
1543 
1544 			    len = ebcdic_to_multibyte_x(
1545 					ea_buf[baddr].ec,
1546 					CS_BASE, mb,
1547 					sizeof(mb),
1548 					flags,
1549 					NULL);
1550 			}
1551 			if (len > 0) {
1552 			    len--;
1553 			}
1554 			if ((len == 1) && (mb[0] == ' ')) {
1555 			    u = crosshair_blank(baddr, &acs);
1556 			    if (u != ' ') {
1557 				draw_crosshair(u, acs);
1558 				done_sbcs = true;
1559 			    }
1560 			}
1561 			if (!done_sbcs) {
1562 			    if (toggled(UNDERSCORE) && underlined &&
1563 				    (len == 1) && mb[0] == ' ') {
1564 				mb[0] = '_';
1565 			    }
1566 #if defined(CURSES_WIDE) /*[*/
1567 			    addstr(mb);
1568 #else /*][*/
1569 			    if (len > 1) {
1570 				addch(' ');
1571 			    } else {
1572 				addch(mb[0] & 0xff);
1573 			    }
1574 #endif /*]*/
1575 			}
1576 		    }
1577 		}
1578 	    }
1579 	}
1580     }
1581     if (status_row) {
1582 	draw_oia();
1583     }
1584     attrset(defattr);
1585     if (menu_is_up) {
1586 	menu_cursor(&row, &col);
1587 	move(row, col);
1588     } else {
1589 	if (flipped) {
1590 	    move((cursor_addr / cCOLS + screen_yoffset),
1591 		    cCOLS-1 - (cursor_addr % cCOLS));
1592 	} else {
1593 	    move((cursor_addr / cCOLS) + screen_yoffset,
1594 		    cursor_addr % cCOLS);
1595 	}
1596     }
1597     refresh();
1598 }
1599 
1600 /* ESC processing. */
1601 static unsigned long eto = 0L;
1602 static bool meta_escape = false;
1603 
1604 static void
escape_timeout(ioid_t id _is_unused)1605 escape_timeout(ioid_t id _is_unused)
1606 {
1607     vtrace("Timeout waiting for key following Escape, processing separately\n");
1608     eto = 0L;
1609     meta_escape = false;
1610     kybd_input2(0, 0x1b, 0);
1611 }
1612 
1613 /* Keyboard input. */
1614 static void
kybd_input(iosrc_t fd _is_unused,ioid_t id _is_unused)1615 kybd_input(iosrc_t fd _is_unused, ioid_t id _is_unused)
1616 {
1617     int k = 0;		/* KEY_XXX, or 0 */
1618     ucs4_t ucs4 = 0;	/* input character, or 0 */
1619     bool first = true;
1620     static bool failed_first = false;
1621 
1622     for (;;) {
1623 	volatile int alt = 0;
1624 	char dbuf[128];
1625 #if defined(CURSES_WIDE) /*[*/
1626 	wint_t wch;
1627 	size_t sz;
1628 #endif /*]*/
1629 
1630 	if (!initscr_done || isendwin()) {
1631 	    return;
1632 	}
1633 	ucs4 = 0;
1634 #if defined(CURSES_WIDE) /*[*/
1635 	k = wget_wch(stdscr, &wch);
1636 #else /*][*/
1637 	k = wgetch(stdscr);
1638 #endif /*]*/
1639 	vtrace("kbd_input: k=%d "
1640 # if defined(CURSES_WIDE) /*[*/
1641 		       "wch=%lu "
1642 # endif /*]*/
1643 				  "\n",
1644 					k
1645 # if defined(CURSES_WIDE) /*[*/
1646 					 , (unsigned long)wch
1647 # endif /*]*/
1648 						             );
1649 	if (k == ERR) {
1650 	    if (first) {
1651 		if (failed_first) {
1652 		    vtrace("End of File, exiting.\n");
1653 		    x3270_exit(1);
1654 		}
1655 		failed_first = true;
1656 	    }
1657 	    vtrace("kbd_input: k == ERR, return\n");
1658 	    return;
1659 	} else {
1660 	    failed_first = false;
1661 	}
1662 #if !defined(CURSES_WIDE) /*[*/
1663 	/* Differentiate between KEY_XXX and regular input. */
1664 	if (!(k & ~0xff)) {
1665 	    char mb[2];
1666 	    int consumed;
1667 	    enum me_fail error;
1668 
1669 	    /* Convert from local multi-byte to Unicode. */
1670 	    mb[0] = k;
1671 	    mb[1] = '\0';
1672 	    ucs4 = multibyte_to_unicode(mb, 1, &consumed, &error);
1673 	    if (ucs4 == 0) {
1674 		vtrace("Invalid input char 0x%x\n", k);
1675 		return;
1676 	    }
1677 	    k = 0;
1678 	}
1679 #endif /*]*/
1680 #if defined(CURSES_WIDE) /*[*/
1681 	if (k == KEY_CODE_YES)
1682 	    k = (int)wch;	/* KEY_XXX */
1683 	else {
1684 	    char mbs[16];
1685 	    wchar_t wcs[2];
1686 
1687 	    k = 0;
1688 	    wcs[0] = wch;
1689 	    wcs[1] = 0;
1690 	    sz = wcstombs(mbs, wcs, sizeof(mbs));
1691 	    if (sz == (size_t)-1) {
1692 		vtrace("Invalid input wchar 0x%lx\n", (unsigned long)wch);
1693 		return;
1694 	    }
1695 	    if (sz == 1) {
1696 		ucs4 = mbs[0] & 0xff;
1697 	    } else {
1698 		int consumed;
1699 		enum me_fail error;
1700 
1701 		ucs4 = multibyte_to_unicode(mbs, sz, &consumed, &error);
1702 		if (ucs4 == 0) {
1703 		    vtrace("Unsupported input wchar 0x%lx\n",
1704 			    (unsigned long)wch);
1705 		    return;
1706 		}
1707 	    }
1708 	}
1709 #endif /*]*/
1710 
1711 #if defined(NCURSES_MOUSE_VERSION) /*[*/
1712 	if (k == KEY_MOUSE) {
1713 	    MEVENT m;
1714 
1715 	    if (menu_is_up) {
1716 		menu_key(MK_MOUSE, 0);
1717 		return;
1718 	    }
1719 	    if (getmouse(&m) != OK) {
1720 		return;
1721 	    }
1722 	    if ((m.bstate & BUTTON1_RELEASED)) {
1723 		vtrace("Mouse BUTTON1_RELEASED (x=%d,y=%d)\n", m.x, m.y);
1724 		if (screen_yoffset != 0 && m.y == 0) {
1725 		    popup_menu(m.x, (screen_yoffset != 0));
1726 		    screen_disp(false);
1727 		} else if (status_row &&
1728 		    m.x == rmargin - 28 &&
1729 		    m.y == status_row) {
1730 			run_action(AnShow, IA_DEFAULT, KwStatus, NULL);
1731 		} else if (m.x < cCOLS &&
1732 			   m.y - screen_yoffset >= 0 &&
1733 			   m.y - screen_yoffset < ROWS) {
1734 		    if (flipped) {
1735 			cursor_move(((m.y - screen_yoffset) * cCOLS) +
1736 				(cCOLS - m.x));
1737 		    } else {
1738 			cursor_move(((m.y - screen_yoffset) * cCOLS) + m.x);
1739 		    }
1740 		    move(m.y + screen_yoffset, m.x);
1741 		    refresh();
1742 		}
1743 	    }
1744 	    return;
1745 	}
1746 #endif /*]*/
1747 
1748 	/* Handle Meta-Escapes. */
1749 	if (meta_escape) {
1750 	    if (eto != 0L) {
1751 		RemoveTimeOut(eto);
1752 		eto = 0L;
1753 	    }
1754 	    meta_escape = false;
1755 	    alt = KM_ALT;
1756 	} else if (me_mode == TS_ON && ucs4 == 0x1b) {
1757 	    vtrace("Key '%s' (curses key 0x%x, char code 0x%x)\n",
1758 		    decode_key(k, ucs4, alt, dbuf), k, ucs4);
1759 	    eto = AddTimeOut(100L, escape_timeout);
1760 	    vtrace(" waiting to see if Escape is followed by another key\n");
1761 	    meta_escape = true;
1762 	    continue;
1763 	}
1764 	vtrace("Key '%s' (curses key 0x%x, char code 0x%x)\n",
1765 		decode_key(k, ucs4, alt, dbuf), k, ucs4);
1766 	kybd_input2(k, ucs4, alt);
1767 	first = false;
1768     }
1769 }
1770 
1771 /* Translate a curses key to a menubar abstract key. */
1772 static menu_key_t
key_to_mkey(int k)1773 key_to_mkey(int k)
1774 {
1775     switch (k) {
1776 #if defined(NCURSES_MOUSE_VERSION) /*[*/
1777     case KEY_MOUSE:
1778 	return MK_MOUSE;
1779 #endif /*]*/
1780     case KEY_UP:
1781 	return MK_UP;
1782     case KEY_DOWN:
1783 	return MK_DOWN;
1784     case KEY_LEFT:
1785 	return MK_LEFT;
1786     case KEY_RIGHT:
1787 	return MK_RIGHT;
1788     case KEY_HOME:
1789 	return MK_HOME;
1790     case KEY_END:
1791 	return MK_END;
1792     case KEY_ENTER:
1793 	return MK_ENTER;
1794     case 0:
1795 	return MK_NONE;
1796     default:
1797 	return MK_OTHER;
1798     }
1799 }
1800 
1801 static void
kybd_input2(int k,ucs4_t ucs4,int alt)1802 kybd_input2(int k, ucs4_t ucs4, int alt)
1803 {
1804     char buf[16];
1805     char *action;
1806     int i;
1807 
1808     if (menu_is_up) {
1809 	menu_key(key_to_mkey(k), ucs4);
1810 	screen_disp(false);
1811 	return;
1812     }
1813 
1814     action = lookup_key(k, ucs4, alt);
1815     if (action != NULL) {
1816 	if (strcmp(action, "[ignore]")) {
1817 	    push_keymap_action(action);
1818 	}
1819 	return;
1820     }
1821     ia_cause = IA_DEFAULT;
1822 
1823     /* These first cases apply to both 3270 and NVT modes. */
1824     switch (k) {
1825     case KEY_UP:
1826 	run_action(AnUp, IA_DEFAULT, NULL, NULL);
1827 	return;
1828     case KEY_DOWN:
1829 	run_action(AnDown, IA_DEFAULT, NULL, NULL);
1830 	return;
1831     case KEY_LEFT:
1832 	run_action(AnLeft, IA_DEFAULT, NULL, NULL);
1833 	return;
1834     case KEY_RIGHT:
1835 	run_action(AnRight, IA_DEFAULT, NULL, NULL);
1836 	return;
1837     case KEY_HOME:
1838 	run_action(AnRight, IA_DEFAULT, NULL, NULL);
1839 	return;
1840     default:
1841 	break;
1842     }
1843     switch (ucs4) {
1844     case 0x1d:
1845 	run_action(AnEscape, IA_DEFAULT, NULL, NULL);
1846 	return;
1847     }
1848 
1849     /* Then look for 3270-only cases. */
1850     if (IN_3270) {
1851 	switch (k) {
1852 	case KEY_DC:
1853 	    run_action(AnDelete, IA_DEFAULT, NULL, NULL);
1854 	    return;
1855 	case KEY_BACKSPACE:
1856 	    run_action(AnBackSpace, IA_DEFAULT, NULL, NULL);
1857 	    return;
1858 	case KEY_HOME:
1859 	    run_action(AnHome, IA_DEFAULT, NULL, NULL);
1860 	    return;
1861 	default:
1862 	    break;
1863 	}
1864 	switch (ucs4) {
1865 	case 0x03:
1866 	    run_action(AnClear, IA_DEFAULT, NULL, NULL);
1867 	    return;
1868 	case 0x12:
1869 	    run_action(AnReset, IA_DEFAULT, NULL, NULL);
1870 	    return;
1871 	case 'L' & 0x1f:
1872 	    run_action(AnRedraw, IA_DEFAULT, NULL, NULL);
1873 	    return;
1874 	case '\t':
1875 	    run_action(AnTab, IA_DEFAULT, NULL, NULL);
1876 	    return;
1877 	case 0177:
1878 	    run_action(AnDelete, IA_DEFAULT, NULL, NULL);
1879 	    return;
1880 	case '\b':
1881 	    run_action(AnBackSpace, IA_DEFAULT, NULL, NULL);
1882 	    return;
1883 	case '\r':
1884 	    run_action(AnEnter, IA_DEFAULT, NULL, NULL);
1885 	    return;
1886 	case '\n':
1887 	    run_action(AnNewline, IA_DEFAULT, NULL, NULL);
1888 	    return;
1889 	default:
1890 	    break;
1891 	}
1892     }
1893 
1894     /* Do some NVT-only translations. */
1895     if (IN_NVT) {
1896 	switch (k) {
1897 	case KEY_DC:
1898 	    ucs4 = 0x7f;
1899 	    k = 0;
1900 	    break;
1901 	case KEY_BACKSPACE:
1902 	    ucs4 = '\b';
1903 	    k = 0;
1904 	    break;
1905 	}
1906     }
1907 
1908     /* Catch PF keys. */
1909     for (i = 1; i <= 24; i++) {
1910 	if (k == KEY_F(i)) {
1911 	    sprintf(buf, "%d", i);
1912 	    run_action(AnPF, IA_DEFAULT, buf, NULL);
1913 	    return;
1914 	}
1915     }
1916 
1917     /* Then any other 8-bit ASCII character. */
1918     if (ucs4) {
1919 	char ks[16];
1920 
1921 	sprintf(ks, "U+%04x", ucs4);
1922 	run_action(AnKey, IA_DEFAULT, ks, NULL);
1923 	return;
1924     }
1925 
1926     vtrace(" dropped (no default)\n");
1927 }
1928 
1929 bool
screen_suspend(void)1930 screen_suspend(void)
1931 {
1932     static bool need_to_scroll = false;
1933     bool needed = false;
1934 
1935     if (!initscr_done) {
1936 	return false;
1937     }
1938 
1939     if (!isendwin()) {
1940 #if defined(C3270_80_132) /*[*/
1941 	if (def_screen != alt_screen) {
1942 	    /*
1943 	     * Call endwin() for the last-defined screen
1944 	     * (altscreen) first.  Note that this will leave
1945 	     * the curses screen set to defscreen when this
1946 	     * function exits; if the 3270 is really in altscreen
1947 	     * mode, we will have to switch it back when we resume
1948 	     * the screen, below.
1949 	     */
1950 	    if (!curses_alt) {
1951 		swap_screens(alt_screen);
1952 	    }
1953 	    endwin();
1954 	    swap_screens(def_screen);
1955 	    endwin();
1956 	} else {
1957 	    endwin();
1958 	}
1959 #else /*][*/
1960 	endwin();
1961 #endif /*]*/
1962 	needed = true;
1963     }
1964 
1965     if (!escaped) {
1966 	escaped = true;
1967 
1968 	if (need_to_scroll) {
1969 	    printf("\n");
1970 	} else {
1971 	    need_to_scroll = true;
1972 	}
1973 #if defined(C3270_80_132) /*[*/
1974 	if (curses_alt && def_screen != alt_screen) {
1975 	    if (write(1, defscreen_spec.mode_switch,
1976 			strlen(defscreen_spec.mode_switch)) < 0) {
1977 		x3270_exit(1);
1978 	    }
1979 	}
1980 #endif /*]*/
1981 	RemoveInput(input_id);
1982 	input_id = NULL_IOID;
1983     }
1984 
1985     return needed;
1986 }
1987 
1988 void
screen_resume(void)1989 screen_resume(void)
1990 {
1991     char *cl;
1992 
1993     if (!escaped) {
1994 	return;
1995     }
1996 
1997     escaped = false;
1998 
1999     /* Ignore signals we don't like. */
2000     signal(SIGINT, SIG_IGN);
2001     signal(SIGTSTP, SIG_IGN);
2002 
2003     /*
2004      * Clear the screen first, if possible, so future command output
2005      * starts at the bottom of the screen.
2006      */
2007     if ((cl = tigetstr("clear")) != NULL) {
2008 	putp(cl);
2009     }
2010 
2011     /* Finish screen initialization. */
2012     if (!screen_initted) {
2013 	finish_screen_init();
2014     }
2015 
2016 #if defined(C3270_80_132) /*[*/
2017     if (def_screen != alt_screen && curses_alt) {
2018 	/*
2019 	 * When we suspended the screen, we switched to defscreen so
2020 	 * that endwin() got called in the right order.  Switch back.
2021 	 */
2022 	swap_screens(alt_screen);
2023 	if (write(1, altscreen_spec.mode_switch,
2024 	    strlen(altscreen_spec.mode_switch)) < 0) {
2025 	    x3270_exit(1);
2026 	}
2027     }
2028 #endif /*]*/
2029     screen_disp(false);
2030     refresh();
2031     if (input_id == NULL_IOID) {
2032 	input_id = AddInput(0, kybd_input);
2033     }
2034 }
2035 
2036 void
cursor_move(int baddr)2037 cursor_move(int baddr)
2038 {
2039     cursor_addr = baddr;
2040 }
2041 
2042 static void
toggle_monocase(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)2043 toggle_monocase(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
2044 {
2045     screen_disp(false);
2046 }
2047 
2048 static void
toggle_underscore(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)2049 toggle_underscore(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
2050 {
2051     screen_disp(false);
2052 }
2053 
2054 static void
toggle_visibleControl(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)2055 toggle_visibleControl(toggle_index_t ix _is_unused,
2056 	enum toggle_type tt _is_unused)
2057 {
2058     screen_disp(false);
2059 }
2060 
2061 /**
2062  * Toggle timing display.
2063  */
2064 static void
toggle_showTiming(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)2065 toggle_showTiming(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
2066 {
2067     if (!toggled(SHOW_TIMING)) {
2068 	status_untiming();
2069     }
2070 }
2071 
2072 /**
2073  * Toggle crosshair cursor.
2074  */
2075 static void
toggle_crosshair(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)2076 toggle_crosshair(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
2077 {
2078     screen_disp(false);
2079 }
2080 
2081 /* Status line stuff. */
2082 
2083 static bool status_ta = false;
2084 static bool status_rm = false;
2085 static bool status_im = false;
2086 static enum {
2087     SS_INSECURE,
2088     SS_UNVERIFIED,
2089     SS_SECURE
2090 } status_secure = SS_INSECURE;
2091 static bool oia_boxsolid = false;
2092 static bool oia_undera = true;
2093 static bool oia_compose = false;
2094 static bool oia_printer = false;
2095 static ucs4_t oia_compose_char = 0;
2096 static enum keytype oia_compose_keytype = KT_STD;
2097 #define LUCNT	8
2098 static char oia_lu[LUCNT+1];
2099 static char oia_timing[32]; /* :ss.s*/
2100 static char oia_screentrace = ' ';
2101 static char oia_script = ' ';
2102 
2103 static ioid_t info_done_timeout = NULL_IOID;
2104 static ioid_t info_scroll_timeout = NULL_IOID;
2105 
2106 void
status_ctlr_done(void)2107 status_ctlr_done(void)
2108 {
2109     oia_undera = true;
2110 }
2111 
2112 void
status_insert_mode(bool on)2113 status_insert_mode(bool on)
2114 {
2115     status_im = on;
2116 }
2117 
2118 /* Remove the info message. */
2119 static void
info_done(ioid_t id _is_unused)2120 info_done(ioid_t id _is_unused)
2121 {
2122     Replace(info_base_msg, NULL);
2123     info_msg = NULL;
2124     info_done_timeout = NULL_IOID;
2125 }
2126 
2127 /* Scroll the info message. */
2128 static void
info_scroll(ioid_t id _is_unused)2129 info_scroll(ioid_t id _is_unused)
2130 {
2131     ++info_msg;
2132     if (strlen(info_msg) > 35) {
2133 	info_scroll_timeout = AddTimeOut(STATUS_SCROLL_MS, info_scroll);
2134     } else {
2135 	info_done_timeout = AddTimeOut(STATUS_PUSH_MS, info_done);
2136 	info_scroll_timeout = NULL_IOID;
2137     }
2138 }
2139 
2140 /* Pop up an info message. */
2141 void
status_push(char * msg)2142 status_push(char *msg)
2143 {
2144     char *new_msg = msg? NewString(msg): NULL;
2145 
2146     Replace(info_base_msg, new_msg);
2147     info_msg = info_base_msg;
2148 
2149     if (info_scroll_timeout != NULL_IOID) {
2150 	RemoveTimeOut(info_scroll_timeout);
2151 	info_scroll_timeout = NULL_IOID;
2152     }
2153     if (info_done_timeout != NULL_IOID) {
2154 	RemoveTimeOut(info_done_timeout);
2155 	info_done_timeout = NULL_IOID;
2156     }
2157 }
2158 
2159 /*
2160  * Reset the info message, so when it is revealed, it starts at the beginning.
2161  */
2162 static void
reset_info(void)2163 reset_info(void)
2164 {
2165     if (info_base_msg != NULL) {
2166 	info_msg = info_base_msg;
2167     }
2168 
2169     /* Stop any timers. */
2170     if (info_scroll_timeout != NULL_IOID) {
2171 	RemoveTimeOut(info_scroll_timeout);
2172 	info_scroll_timeout = NULL_IOID;
2173     }
2174     if (info_done_timeout != NULL_IOID) {
2175 	RemoveTimeOut(info_done_timeout);
2176 	info_done_timeout = NULL_IOID;
2177     }
2178 }
2179 
2180 /*
2181  * The info message has been displayed. Set the timer to scroll or erase it.
2182  */
2183 static void
set_info_timer(void)2184 set_info_timer(void)
2185 {
2186     if (info_scroll_timeout != NULL_IOID || info_done_timeout != NULL_IOID) {
2187 	return;
2188 
2189     }
2190     if (strlen(info_msg) > 35) {
2191 	info_scroll_timeout = AddTimeOut(STATUS_SCROLL_START_MS, info_scroll);
2192     } else {
2193 	info_done_timeout = AddTimeOut(STATUS_PUSH_MS, info_done);
2194     }
2195 }
2196 
2197 /* Compute the color pair for an OIA field. */
2198 static curses_attr
status_colors(curses_color fg)2199 status_colors(curses_color fg)
2200 {
2201     if (appres.c3270.reverse_video &&
2202 	    (fg == COLOR_WHITE || fg == defcolor_offset + COLOR_WHITE)) {
2203 	fg = COLOR_BLACK;
2204     }
2205     return mode.m3279? get_color_pair(fg, bg_color): defattr;
2206 }
2207 
2208 void
status_minus(void)2209 status_minus(void)
2210 {
2211     other_msg = "X -f";
2212     other_attr = status_colors(defcolor_offset + COLOR_RED) | A_BOLD;
2213 }
2214 
2215 void
status_oerr(int error_type)2216 status_oerr(int error_type)
2217 {
2218     switch (error_type) {
2219     case KL_OERR_PROTECTED:
2220 	other_msg = "X Protected";
2221 	break;
2222     case KL_OERR_NUMERIC:
2223 	other_msg = "X NUM";
2224 	break;
2225     case KL_OERR_OVERFLOW:
2226 	other_msg = "X Overflow";
2227 	break;
2228     }
2229     other_attr = status_colors(defcolor_offset + COLOR_RED) | A_BOLD;
2230 }
2231 
2232 void
status_reset(void)2233 status_reset(void)
2234 {
2235     if (kybdlock & KL_ENTER_INHIBIT) {
2236 	other_msg = "X Inhibit";
2237     } else if (kybdlock & KL_DEFERRED_UNLOCK) {
2238 	other_msg = "X";
2239     } else {
2240 	status_connect(PCONNECTED);
2241     }
2242     other_attr = status_colors(defcolor_offset + COLOR_WHITE) | A_BOLD;
2243 }
2244 
2245 void
status_reverse_mode(bool on)2246 status_reverse_mode(bool on)
2247 {
2248     status_rm = on;
2249 }
2250 
2251 void
status_syswait(void)2252 status_syswait(void)
2253 {
2254     other_msg = "X SYSTEM";
2255     other_attr = status_colors(defcolor_offset + COLOR_WHITE) | A_BOLD;
2256 }
2257 
2258 void
status_twait(void)2259 status_twait(void)
2260 {
2261     oia_undera = false;
2262     other_msg = "X Wait";
2263     other_attr = status_colors(defcolor_offset + COLOR_WHITE) | A_BOLD;
2264 }
2265 
2266 void
status_typeahead(bool on)2267 status_typeahead(bool on)
2268 {
2269     status_ta = on;
2270 }
2271 
2272 void
status_compose(bool on,ucs4_t ucs4,enum keytype keytype)2273 status_compose(bool on, ucs4_t ucs4, enum keytype keytype)
2274 {
2275     oia_compose = on;
2276     oia_compose_char = ucs4;
2277     oia_compose_keytype = keytype;
2278 }
2279 
2280 void
status_lu(const char * lu)2281 status_lu(const char *lu)
2282 {
2283     if (lu != NULL) {
2284 	strncpy(oia_lu, lu, LUCNT);
2285 	oia_lu[LUCNT] = '\0';
2286     } else {
2287 	memset(oia_lu, '\0', sizeof(oia_lu));
2288     }
2289 }
2290 
2291 static void
status_connect(bool connected)2292 status_connect(bool connected)
2293 {
2294     if (connected) {
2295 	oia_boxsolid = IN_3270 && !IN_SSCP;
2296 	if (cstate == RECONNECTING) {
2297 	    other_msg = "X Reconnecting";
2298 	} else if (cstate == RESOLVING) {
2299 	    other_msg = "X [DNS]";
2300 	} else if (cstate == TCP_PENDING) {
2301 	    other_msg = "X [TCP]";
2302 	    oia_boxsolid = false;
2303 	    status_secure = SS_INSECURE;
2304 	} else if (cstate == TLS_PENDING) {
2305 	    other_msg = "X [TLS]";
2306 	    oia_boxsolid = false;
2307 	    status_secure = SS_INSECURE;
2308 	} else if (cstate == PROXY_PENDING) {
2309 	    other_msg = "X [Proxy]";
2310 	    oia_boxsolid = false;
2311 	    status_secure = SS_INSECURE;
2312 	} else if (cstate == TELNET_PENDING) {
2313 	    other_msg = "X [TELNET]";
2314 	    oia_boxsolid = false;
2315 	    status_secure = SS_INSECURE;
2316 	} else if (cstate == CONNECTED_UNBOUND) {
2317 	    other_msg = "X [TN3270E]";
2318 	} else if (kybdlock & KL_AWAITING_FIRST) {
2319 	    other_msg = "X [Field]";
2320 	} else {
2321 	    other_msg = NULL;
2322 	}
2323 	if (net_secure_connection()) {
2324 	    if (net_secure_unverified()) {
2325 		status_secure = SS_UNVERIFIED;
2326 	    } else {
2327 		status_secure = SS_SECURE;
2328 	    }
2329 	} else {
2330 	    status_secure = SS_INSECURE;
2331 	}
2332     } else {
2333 	oia_boxsolid = false;
2334 	other_msg = "X Not Connected";
2335 	status_secure = SS_INSECURE;
2336     }
2337     other_attr = status_colors(defcolor_offset + COLOR_WHITE) | A_BOLD;
2338     status_untiming();
2339 }
2340 
2341 static void
status_3270_mode(bool ignored _is_unused)2342 status_3270_mode(bool ignored _is_unused)
2343 {
2344     oia_boxsolid = IN_3270 && !IN_SSCP;
2345     if (oia_boxsolid) {
2346 	oia_undera = true;
2347     }
2348     status_connect(CONNECTED);
2349 }
2350 
2351 static void
status_printer(bool on)2352 status_printer(bool on)
2353 {
2354     oia_printer = on;
2355 }
2356 
2357 void
status_timing(struct timeval * t0,struct timeval * t1)2358 status_timing(struct timeval *t0, struct timeval *t1)
2359 {
2360     static char no_time[] = ":??.?";
2361 
2362     if (t1->tv_sec - t0->tv_sec > (99*60)) {
2363 	strcpy(oia_timing, no_time);
2364     } else {
2365 	unsigned long cs;	/* centiseconds */
2366 
2367 	cs = (t1->tv_sec - t0->tv_sec) * 10 +
2368 	     (t1->tv_usec - t0->tv_usec + 50000) / 100000;
2369 	if (cs < CM) {
2370 	    snprintf(oia_timing, sizeof(oia_timing),
2371 		    ":%02ld.%ld", cs / 10, cs % 10);
2372 	} else {
2373 	    snprintf(oia_timing, sizeof(oia_timing),
2374 		    "%02ld:%02ld", cs / CM, (cs % CM) / 10);
2375 	}
2376     }
2377 }
2378 
2379 void
status_untiming(void)2380 status_untiming(void)
2381 {
2382     oia_timing[0] = '\0';
2383 }
2384 
2385 void
status_scrolled(int n)2386 status_scrolled(int n)
2387 {
2388     if (n) {
2389 	Replace(scrolled_msg, xs_buffer("X Scrolled %d", n));
2390     } else {
2391 	Replace(scrolled_msg, NULL);
2392     }
2393 }
2394 
2395 /* Remove 'X Disabled'. */
2396 static void
disabled_done(ioid_t id _is_unused)2397 disabled_done(ioid_t id _is_unused)
2398 {
2399     disabled_msg = NULL;
2400     disabled_done_id = NULL_IOID;
2401 }
2402 
2403 /* Flash 'X Disabled' in the OIA. */
2404 void
status_keyboard_disable_flash(void)2405 status_keyboard_disable_flash(void)
2406 {
2407     if (disabled_done_id == NULL_IOID) {
2408 	disabled_msg = "X Disabled";
2409     } else {
2410 	RemoveTimeOut(disabled_done_id);
2411 	disabled_done_id = NULL_IOID;
2412     }
2413     disabled_done_id = AddTimeOut(1000L, disabled_done);
2414 }
2415 
2416 void
status_screentrace(int n)2417 status_screentrace(int n)
2418 {
2419     if (n < 0) {
2420 	oia_screentrace = ' ';
2421     } else if (n < 9) {
2422 	oia_screentrace = "123456789"[n];
2423     } else {
2424 	oia_screentrace = '+';
2425     }
2426 }
2427 
2428 void
status_script(bool on)2429 status_script(bool on)
2430 {
2431     oia_script = on? 's': ' ';
2432 }
2433 
2434 static void
draw_oia(void)2435 draw_oia(void)
2436 {
2437     static bool filled_extra[2] = { false, false };
2438     int i, j;
2439     int cursor_row = cursor_addr / cCOLS;
2440     int cursor_col = cursor_addr % cCOLS;
2441     int fl_cursor_col = flipped? (cursesCOLS - 1 - cursor_col): cursor_col;
2442     char *status_msg_now;
2443     int msg_attr;
2444     static struct {
2445 	ucs4_t u;
2446 	unsigned char acs;
2447     } vbar, hbar;
2448     static bool bars_done = false;
2449 
2450     /* Prepare the line-drawing characters for the crosshair. */
2451     if (toggled(CROSSHAIR) && !bars_done) {
2452 	map_acs('x', &vbar.u, &vbar.acs);
2453 	map_acs('q', &hbar.u, &hbar.acs);
2454 	bars_done = true;
2455     }
2456 
2457 #if defined(C3270_80_132) /*[*/
2458     if (def_screen != alt_screen) {
2459 	if (curses_alt) {
2460 	    rmargin = altscreen_spec.cols - 1;
2461 	} else {
2462 	    rmargin = defscreen_spec.cols - 1;
2463 	}
2464     } else
2465 #endif /*]*/
2466     {
2467 	rmargin = maxCOLS - 1;
2468     }
2469 
2470     /* Black out the parts of the screen we aren't using. */
2471     if (!appres.interactive.mono && !filled_extra[!!curses_alt]) {
2472 	int r, c;
2473 
2474 	attrset(defattr);
2475 	for (r = 0; r <= status_row; r++) {
2476 	    int c0;
2477 
2478 	    if (r >= maxROWS && r != status_row) {
2479 		c0 = 0;
2480 	    } else {
2481 		c0 = maxCOLS;
2482 	    }
2483 	    move(r + screen_yoffset, c0);
2484 	    for (c = c0; c < cursesCOLS; c++) {
2485 		printw(" ");
2486 	    }
2487 	}
2488     }
2489 
2490     /* Make sure the status line region is filled in properly. */
2491     if (!appres.interactive.mono) {
2492 	int i;
2493 
2494 	attrset(defattr);
2495 	if (status_skip) {
2496 	    move(status_skip + screen_yoffset, 0);
2497 	    for (i = 0; i < rmargin; i++) {
2498 		printw(" ");
2499 	    }
2500 	}
2501 	move(status_row + screen_yoffset, 0);
2502 	for (i = 0; i < rmargin; i++) {
2503 	    printw(" ");
2504 	}
2505     }
2506 
2507     /* Draw or undraw the crosshair cursor outside the primary display. */
2508     attrset(xhattr);
2509 
2510     /* Draw the crosshair over the menubar line. */
2511     if (screen_yoffset && toggled(CROSSHAIR) && !menu_is_up &&
2512 	    (mvinch(0, fl_cursor_col) & A_CHARTEXT) == ' ') {
2513 	draw_crosshair(vbar.u, vbar.acs);
2514     }
2515 
2516     /* Draw the crosshair between the menubar and display. */
2517     if (!menu_is_up && screen_yoffset > 1) {
2518 	for (j = 0; j < cursesCOLS; j++) {
2519 	    move(1, j);
2520 	    if (toggled(CROSSHAIR) && j == fl_cursor_col) {
2521 		draw_crosshair(vbar.u, vbar.acs);
2522 	    } else {
2523 		addch(' ');
2524 	    }
2525 	}
2526     }
2527 
2528     /* Draw the crosshair to the right of the display. */
2529     for (i = 0; i < ROWS; i++) {
2530 	for (j = cCOLS; j < cursesCOLS; j++) {
2531 	    move(i + screen_yoffset, j);
2532 	    if (toggled(CROSSHAIR) && i == cursor_row) {
2533 		draw_crosshair(hbar.u, hbar.acs);
2534 	    } else {
2535 		addch(' ');
2536 	    }
2537 	}
2538     }
2539 
2540     /*
2541      * Draw the crosshair between the bottom of the display and the
2542      * OIA.
2543      */
2544     for (i = screen_yoffset + ROWS; i < status_row; i++) {
2545 	for (j = 0; j < cursesCOLS; j++) {
2546 	    move(i, j);
2547 	    if (toggled(CROSSHAIR) && j == fl_cursor_col) {
2548 		draw_crosshair(vbar.u, vbar.acs);
2549 	    } else {
2550 		addch(' ');
2551 	    }
2552 	}
2553     }
2554 
2555 /* The OIA looks like (in Model 2/3/4 mode):
2556 
2557           1         2         3         4         5         6         7
2558 01234567890123456789012345678901234567890123456789012345678901234567890123456789
2559 4AN    Status-Message--------------------- Cn TRIPS+s LU-Name-   :ss.s  000/000
2560          7         6         5         4         3         2         1
2561 98765432109876543210987654321098765432109876543210987654321098765432109876543210
2562                                                                         ^ -7
2563                                                                  ^ -14
2564                                                       ^-25
2565 
2566    On wider displays, there is a bigger gap between TRIPSs and LU-Name.
2567 
2568 */
2569 
2570     /*
2571      * If there is at least one black line between the 3270 display and the
2572      * OIA, draw a row of underlined blanks above the OIA. This is
2573      * something c3270 can do that wc3270 cannot, since Windows consoles
2574      * can't do underlining.
2575      */
2576     if (status_row > screen_yoffset + maxROWS) {
2577 	int i;
2578 	attrset(A_UNDERLINE | defattr);
2579 	move(status_row - 1, 0);
2580 	for (i = 0; i < rmargin; i++) {
2581 	    if (toggled(CROSSHAIR) && i == fl_cursor_col) {
2582 		move(status_row - 1, i + 1);
2583 	    } else {
2584 		printw(" ");
2585 	    }
2586 	}
2587     }
2588 
2589     /* Clean up the OIA first, from a possible previous crosshair cursor. */
2590     {
2591 	int i;
2592 
2593 	move(status_row, 0);
2594 	attrset(defattr);
2595 	for (i = 0; i < cursesCOLS - 1; i++) {
2596 	    printw(" ");
2597 	}
2598     }
2599 
2600     attrset(A_REVERSE | defattr);
2601     mvprintw(status_row, 0, "4");
2602     attrset(A_UNDERLINE | defattr);
2603     if (oia_undera) {
2604 	printw("%c", IN_E? 'B': 'A');
2605     } else {
2606 	printw(" ");
2607     }
2608     attrset(A_REVERSE | defattr);
2609     if (IN_NVT) {
2610 	printw("N");
2611     } else if (oia_boxsolid) {
2612 	printw(" ");
2613     } else if (IN_SSCP) {
2614 	printw("S");
2615     } else {
2616 	printw("?");
2617     }
2618 
2619     /* Figure out the status message. */
2620     msg_attr = defattr;
2621     if (disabled_msg != NULL) {
2622 	msg_attr = status_colors(defcolor_offset + COLOR_RED) | A_BOLD;
2623 	status_msg_now = disabled_msg;
2624 	reset_info();
2625     } else if (scrolled_msg != NULL) {
2626 	msg_attr = status_colors(defcolor_offset + COLOR_WHITE) | A_BOLD;
2627 	status_msg_now = scrolled_msg;
2628 	reset_info();
2629     } else if (info_msg != NULL) {
2630 	msg_attr = status_colors(defcolor_offset + COLOR_WHITE) | A_BOLD;
2631 	status_msg_now = info_msg;
2632 	set_info_timer();
2633     } else if (other_msg != NULL) {
2634 	status_msg_now = other_msg;
2635 	msg_attr = other_attr;
2636     } else {
2637 	status_msg_now = "";
2638     }
2639 
2640     attrset(msg_attr);
2641     mvprintw(status_row, 7, "%-35.35s", status_msg_now);
2642     attrset(defattr);
2643     mvprintw(status_row, rmargin-35,
2644 	"%c%c %c%c%c%c",
2645 	oia_compose? 'C': ' ',
2646 	oia_compose? oia_compose_char: ' ',
2647 	status_ta? 'T': ' ',
2648 	status_rm? 'R': ' ',
2649 	status_im? 'I': ' ',
2650 	oia_printer? 'P': ' ');
2651     if (status_secure != SS_INSECURE) {
2652 	attrset(status_colors(defcolor_offset +
2653 		    ((status_secure == SS_SECURE)? COLOR_GREEN: COLOR_YELLOW))
2654 		| A_BOLD);
2655 	printw("S");
2656 	attrset(defattr);
2657     } else {
2658 	printw(" ");
2659     }
2660     printw("%c%c", oia_screentrace,oia_script);
2661 
2662     mvprintw(status_row, rmargin-25, "%s", oia_lu);
2663 
2664     mvprintw(status_row, rmargin-14, "%s", oia_timing);
2665 
2666     mvprintw(status_row, rmargin-7, "%03d/%03d ", cursor_addr/cCOLS + 1,
2667 	    cursor_addr%cCOLS + 1);
2668 
2669     /* Draw the crosshair in the OIA. */
2670     if (toggled(CROSSHAIR) &&
2671 	    cursor_col > 2 &&
2672 	    (mvinch(status_row, fl_cursor_col) & A_CHARTEXT) == ' ') {
2673 	draw_crosshair(vbar.u, vbar.acs);
2674     }
2675 }
2676 
2677 bool
Redraw_action(ia_t ia,unsigned argc,const char ** argv)2678 Redraw_action(ia_t ia, unsigned argc, const char **argv)
2679 {
2680     action_debug(AnRedraw, ia, argc, argv);
2681     if (check_argc(AnRedraw, argc, 0, 0) < 0) {
2682 	return false;
2683     }
2684 
2685     if (!escaped) {
2686 	endwin();
2687 	refresh();
2688     }
2689     return true;
2690 }
2691 
2692 void
ring_bell(void)2693 ring_bell(void)
2694 {
2695     beep();
2696 }
2697 
2698 void
screen_flip(void)2699 screen_flip(void)
2700 {
2701     flipped = !flipped;
2702     screen_disp(false);
2703 }
2704 
2705 bool
screen_flipped(void)2706 screen_flipped(void)
2707 {
2708     return flipped;
2709 }
2710 
2711 #if defined(C3270_80_132) /*[*/
2712 /* Alt/default screen spec parsing. */
2713 static void
parse_screen_spec(const char * str,struct screen_spec * spec)2714 parse_screen_spec(const char *str, struct screen_spec *spec)
2715 {
2716     char msbuf[3];
2717     char *s, *t, c;
2718     bool escaped = false;
2719 
2720     if (sscanf(str, "%dx%d=%2s", &spec->rows, &spec->cols, msbuf) != 3) {
2721 	fprintf(stderr, "Invalid screen screen spec '%s', must "
2722 		"be '<rows>x<cols>=<init_string>'\n", str);
2723 	exit(1);
2724     }
2725     s = strchr(str, '=') + 1;
2726     spec->mode_switch = Malloc(strlen(s) + 1);
2727     t = spec->mode_switch;
2728     while ((c = *s++)) {
2729 	if (escaped) {
2730 	    switch (c) {
2731 	    case 'E':
2732 		*t++ = 0x1b;
2733 		break;
2734 	    case 'n':
2735 		*t++ = '\n';
2736 		break;
2737 	    case 'r':
2738 		*t++ = '\r';
2739 		break;
2740 	    case 'b':
2741 		*t++ = '\b';
2742 		break;
2743 	    case 't':
2744 		*t++ = '\t';
2745 		break;
2746 	    case '\\':
2747 		*t++ = '\\';
2748 		break;
2749 	    default:
2750 		*t++ = c;
2751 		break;
2752 	    }
2753 	    escaped = false;
2754 	} else if (c == '\\') {
2755 	    escaped = true;
2756 	} else {
2757 	    *t++ = c;
2758 	}
2759     }
2760     *t = '\0';
2761 }
2762 #endif /*]*/
2763 
2764 void
screen_132(void)2765 screen_132(void)
2766 {
2767 #if defined(C3270_80_132) /*[*/
2768     if (cur_screen != alt_screen) {
2769 	swap_screens(alt_screen);
2770 	if (write(1, altscreen_spec.mode_switch,
2771 		    strlen(altscreen_spec.mode_switch)) < 0) {
2772 	    x3270_exit(1);
2773 	}
2774 	ctlr_erase(true);
2775 	screen_disp(true);
2776     }
2777 #endif /*]*/
2778 }
2779 
2780 void
screen_80(void)2781 screen_80(void)
2782 {
2783 #if defined(C3270_80_132) /*[*/
2784     if (cur_screen != def_screen) {
2785 	swap_screens(def_screen);
2786 	if (write(1, defscreen_spec.mode_switch,
2787 		    strlen(defscreen_spec.mode_switch)) < 0) {
2788 	    x3270_exit(1);
2789 	}
2790 	ctlr_erase(false);
2791 	screen_disp(true);
2792     }
2793 #endif /*]*/
2794 }
2795 
2796 /*
2797  * Translate an x3270 font line-drawing character (the first two rows of a
2798  * standard X11 fixed-width font) to a curses ACS character.
2799  *
2800  * Returns -1 if there is no translation.
2801  */
2802 static int
linedraw_to_acs(unsigned char c)2803 linedraw_to_acs(unsigned char c)
2804 {
2805     switch (c) {
2806 #if defined(ACS_BLOCK) /*[*/
2807     case 0x0:
2808 	return ACS_BLOCK;
2809 #endif /*]*/
2810 #if defined(ACS_DIAMOND) /*[*/
2811     case 0x1:
2812 	return ACS_DIAMOND;
2813 #endif /*]*/
2814 #if defined(ACS_CKBOARD) /*[*/
2815     case 0x2:
2816 	return ACS_CKBOARD;
2817 #endif /*]*/
2818 #if defined(ACS_DEGREE) /*[*/
2819     case 0x7:
2820 	return ACS_DEGREE;
2821 #endif /*]*/
2822 #if defined(ACS_PLMINUS) /*[*/
2823     case 0x8:
2824 	return ACS_PLMINUS;
2825 #endif /*]*/
2826 #if defined(ACS_BOARD) /*[*/
2827     case 0x9:
2828 	return ACS_BOARD;
2829 #endif /*]*/
2830 #if defined(ACS_LANTERN) /*[*/
2831     case 0xa:
2832 	return ACS_LANTERN;
2833 #endif /*]*/
2834 #if defined(ACS_LRCORNER) /*[*/
2835     case 0xb:
2836 	return ACS_LRCORNER;
2837 #endif /*]*/
2838 #if defined(ACS_URCORNER) /*[*/
2839     case 0xc:
2840 	return ACS_URCORNER;
2841 #endif /*]*/
2842 #if defined(ACS_ULCORNER) /*[*/
2843     case 0xd:
2844 	return ACS_ULCORNER;
2845 #endif /*]*/
2846 #if defined(ACS_LLCORNER) /*[*/
2847     case 0xe:
2848 	return ACS_LLCORNER;
2849 #endif /*]*/
2850 #if defined(ACS_PLUS) /*[*/
2851     case 0xf:
2852 	return ACS_PLUS;
2853 #endif /*]*/
2854 #if defined(ACS_S1) /*[*/
2855     case 0x10:
2856 	return ACS_S1;
2857 #endif /*]*/
2858 #if defined(ACS_S3) /*[*/
2859     case 0x11:
2860 	return ACS_S3;
2861 #endif /*]*/
2862 #if defined(ACS_HLINE) /*[*/
2863     case 0x12:
2864 	return ACS_HLINE;
2865 #endif /*]*/
2866 #if defined(ACS_S7) /*[*/
2867     case 0x13:
2868 	return ACS_S7;
2869 #endif /*]*/
2870 #if defined(ACS_S9) /*[*/
2871     case 0x14:
2872 	return ACS_S9;
2873 #endif /*]*/
2874 #if defined(ACS_LTEE) /*[*/
2875     case 0x15:
2876 	return ACS_LTEE;
2877 #endif /*]*/
2878 #if defined(ACS_RTEE) /*[*/
2879     case 0x16:
2880 	return ACS_RTEE;
2881 #endif /*]*/
2882 #if defined(ACS_BTEE) /*[*/
2883     case 0x17:
2884 	return ACS_BTEE;
2885 #endif /*]*/
2886 #if defined(ACS_TTEE) /*[*/
2887     case 0x18:
2888 	return ACS_TTEE;
2889 #endif /*]*/
2890 #if defined(ACS_VLINE) /*[*/
2891     case 0x19:
2892 	return ACS_VLINE;
2893 #endif /*]*/
2894 #if defined(ACS_LEQUAL) /*[*/
2895     case 0x1a:
2896 	return ACS_LEQUAL;
2897 #endif /*]*/
2898 #if defined(ACS_GEQUAL) /*[*/
2899     case 0x1b:
2900 	return ACS_GEQUAL;
2901 #endif /*]*/
2902 #if defined(ACS_PI) /*[*/
2903     case 0x1c:
2904 	return ACS_PI;
2905 #endif /*]*/
2906 #if defined(ACS_NEQUAL) /*[*/
2907     case 0x1d:
2908 	return ACS_NEQUAL;
2909 #endif /*]*/
2910 #if defined(ACS_STERLING) /*[*/
2911     case 0x1e:
2912 	return ACS_STERLING;
2913 #endif /*]*/
2914 #if defined(ACS_BULLET) /*[*/
2915     case 0x1f:
2916 	return ACS_BULLET;
2917 #endif /*]*/
2918     default:
2919 	return -1;
2920     }
2921 }
2922 
2923 static void
display_linedraw(ucs4_t u)2924 display_linedraw(ucs4_t u)
2925 {
2926     char mb[16];
2927     int len;
2928 
2929 #if defined(CURSES_WIDE) /*[*/
2930     if (appres.c3270.acs)
2931 #endif /*]*/
2932     {
2933 	/* Try ACS first. */
2934 	int c = linedraw_to_acs(u);
2935 
2936 	if (c != -1) {
2937 	    addch(c);
2938 	    return;
2939 	}
2940     }
2941 
2942     /* Then try Unicode. */
2943     len = unicode_to_multibyte(
2944 	    linedraw_to_unicode(u, appres.c3270.ascii_box_draw),
2945 	    mb, sizeof(mb));
2946     if (len > 0) {
2947 	len--;
2948     }
2949 #if defined(CURSES_WIDE) /*[*/
2950     addstr(mb);
2951 #else /*][*/
2952     if (len > 1) {
2953 	addch(mb[0] & 0xff);
2954     } else {
2955 	addch(' ');
2956     }
2957 #endif /*]*/
2958 }
2959 
2960 static int
apl_to_acs(unsigned char c)2961 apl_to_acs(unsigned char c)
2962 {
2963     switch (c) {
2964 #if defined(ACS_DEGREE) /*[*/
2965     case 0xaf: /* CG 0xd1 */
2966 	return ACS_DEGREE;
2967 #endif /*]*/
2968 #if defined(ACS_LRCORNER) /*[*/
2969     case 0xd4: /* CG 0xac */
2970 	return ACS_LRCORNER;
2971 #endif /*]*/
2972 #if defined(ACS_URCORNER) /*[*/
2973     case 0xd5: /* CG 0xad */
2974 	return ACS_URCORNER;
2975 #endif /*]*/
2976 #if defined(ACS_ULCORNER) /*[*/
2977     case 0xc5: /* CG 0xa4 */
2978 	return ACS_ULCORNER;
2979 #endif /*]*/
2980 #if defined(ACS_LLCORNER) /*[*/
2981     case 0xc4: /* CG 0xa3 */
2982 	return ACS_LLCORNER;
2983 #endif /*]*/
2984 #if defined(ACS_PLUS) /*[*/
2985     case 0xd3: /* CG 0xab */
2986 	return ACS_PLUS;
2987 #endif /*]*/
2988 #if defined(ACS_HLINE) /*[*/
2989     case 0xa2: /* CG 0x92 */
2990 	return ACS_HLINE;
2991 #endif /*]*/
2992 #if defined(ACS_LTEE) /*[*/
2993     case 0xc6: /* CG 0xa5 */
2994 	return ACS_LTEE;
2995 #endif /*]*/
2996 #if defined(ACS_RTEE) /*[*/
2997     case 0xd6: /* CG 0xae */
2998 	return ACS_RTEE;
2999 #endif /*]*/
3000 #if defined(ACS_BTEE) /*[*/
3001     case 0xc7: /* CG 0xa6 */
3002 	return ACS_BTEE;
3003 #endif /*]*/
3004 #if defined(ACS_TTEE) /*[*/
3005     case 0xd7: /* CG 0xaf */
3006 	return ACS_TTEE;
3007 #endif /*]*/
3008 #if defined(ACS_VLINE) /*[*/
3009     case 0x85: /* CG 0xa84? */
3010 	return ACS_VLINE;
3011 #endif /*]*/
3012 #if defined(ACS_LEQUAL) /*[*/
3013     case 0x8c: /* CG 0xf7 */
3014 	return ACS_LEQUAL;
3015 #endif /*]*/
3016 #if defined(ACS_GEQUAL) /*[*/
3017     case 0xae: /* CG 0xd9 */
3018 	return ACS_GEQUAL;
3019 #endif /*]*/
3020 #if defined(ACS_NEQUAL) /*[*/
3021     case 0xbe: /* CG 0x3e */
3022 	return ACS_NEQUAL;
3023 #endif /*]*/
3024 #if defined(ACS_BULLET) /*[*/
3025     case 0xa3: /* CG 0x93 */
3026 	return ACS_BULLET;
3027 #endif /*]*/
3028     case 0xad:
3029 	return '[';
3030     case 0xbd:
3031 	return ']';
3032     default:
3033 	return -1;
3034     }
3035 }
3036 
3037 static void
display_ge(unsigned char ebc)3038 display_ge(unsigned char ebc)
3039 {
3040     int c;
3041     char mb[16];
3042     int len;
3043 
3044 #if defined(CURSES_WIDE) /*[*/
3045     if (appres.c3270.acs)
3046 #endif /*]*/
3047     {
3048 	/* Try UCS first. */
3049 	c = apl_to_acs(ebc);
3050 	if (c != -1) {
3051 	    addch(c);
3052 	    return;
3053 	}
3054     }
3055 
3056     /* Then try Unicode. */
3057     len = ebcdic_to_multibyte_x(ebc, CS_GE, mb, sizeof(mb),
3058 	    EUO_BLANK_UNDEF | (appres.c3270.ascii_box_draw? EUO_ASCII_BOX: 0),
3059 	    NULL);
3060     if (len > 0) {
3061 	len--;
3062     }
3063 #if defined(CURSES_WIDE) /*[*/
3064     addstr(mb);
3065 #else /*][*/
3066     if (len > 1) {
3067 	addch(mb[0] & 0xff);
3068     } else {
3069 	addch(' ');
3070     }
3071 #endif /*]*/
3072 }
3073 
3074 void
screen_final()3075 screen_final()
3076 {
3077     char *cl;
3078 
3079     if ((cl = tigetstr("clear")) != NULL) {
3080 	putp(cl);
3081     }
3082 }
3083 
3084 /**
3085  * Check if an area of the screen is selected.
3086  * This is implemented by wc3270, but not c3270, so it is a stub here.
3087  *
3088  * @param[in] baddr	Buffer address.
3089  */
3090 bool
screen_selected(int baddr _is_unused)3091 screen_selected(int baddr _is_unused)
3092 {
3093     return false;
3094 }
3095 
3096 /**
3097  * Stub for scrollbar function.
3098  *
3099  * @param[in] top	Where the top of the scrollbar should be (percentage)
3100  * @param[in] shown	How much of the scrollbar to show (percentage)
3101  * @param[in] saved	Number of lines saved
3102  * @param[in] screen	Size of a screen
3103  * @param[in] back	Number of lines scrolled back
3104  */
3105 void
screen_set_thumb(float top _is_unused,float shown _is_unused,int saved _is_unused,int screen _is_unused,int back _is_unused)3106 screen_set_thumb(float top _is_unused, float shown _is_unused,
3107 	int saved _is_unused, int screen _is_unused, int back _is_unused)
3108 {
3109 }
3110 
3111 /**
3112  * Enable or disable the cursor when scrolling.
3113  *
3114  * @param[in] on	Enable (true) or disable (false) the cursor display.
3115  */
3116 void
enable_cursor(bool on)3117 enable_cursor(bool on)
3118 {
3119     if (initscr_done && !isendwin()) {
3120 	curs_set(on? 1: 0);
3121     }
3122 }
3123 
3124 /**
3125  * Screen module registration.
3126  */
3127 void
screen_register(void)3128 screen_register(void)
3129 {
3130     static toggle_register_t toggles[] = {
3131 	{ MONOCASE,	toggle_monocase,	0 },
3132 	{ SHOW_TIMING,	toggle_showTiming,	0 },
3133 	{ UNDERSCORE,	toggle_underscore,	0 },
3134 	{ VISIBLE_CONTROL, toggle_visibleControl, 0 },
3135 	{ CROSSHAIR,	toggle_crosshair,	0 },
3136 	{ TYPEAHEAD,	NULL,			0 }
3137     };
3138     static action_table_t screen_actions[] = {
3139 	{ AnRedraw,	Redraw_action,	ACTION_KE }
3140     };
3141 
3142     /* Register the toggles. */
3143     register_toggles(toggles, array_count(toggles));
3144 
3145     /* Register for state changes. */
3146     register_schange(ST_NEGOTIATING, status_connect);
3147     register_schange(ST_CONNECT, status_connect);
3148     register_schange(ST_3270_MODE, status_3270_mode);
3149     register_schange(ST_PRINTER, status_printer);
3150 
3151     /* Register the actions. */
3152     register_actions(screen_actions, array_count(screen_actions));
3153 }
3154