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