1 /*
2  * Copyright (c) 1993-2016, 2018-2020 Paul Mattes.
3  * Copyright (c) 1990, Jeff Sparkes.
4  * Copyright (c) 1989, Georgia Tech Research Corporation (GTRC), Atlanta,
5  *  GA 30332.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in the
15  *       documentation and/or other materials provided with the distribution.
16  *     * Neither the names of Paul Mattes, Jeff Sparkes, GTRC nor their
17  *       contributors may be used to endorse or promote products derived
18  *       from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES, JEFF SPARKES AND GTRC "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PAUL MATTES, JEFF SPARKES OR
24  * GTRC BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
25  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  *	screen.c
35  *		This module handles the X display.  It has been extensively
36  *		optimized to minimize X drawing operations.
37  */
38 
39 #include "globals.h"
40 #include "xglobals.h"
41 
42 #include <X11/IntrinsicP.h>
43 #include <X11/StringDefs.h>
44 #include <X11/Xatom.h>
45 #include <X11/Shell.h>
46 #include <X11/Composite.h>
47 #include <X11/Xaw/Dialog.h>
48 #include <X11/Xaw/Scrollbar.h>
49 #include <X11/Xaw/Form.h>
50 #include <X11/Xaw/Label.h>
51 #include "Husk.h"
52 #include <X11/cursorfont.h>
53 #include <X11/keysym.h>
54 #include <assert.h>
55 #include <errno.h>
56 #include <locale.h>
57 #include "3270ds.h"
58 #include "appres.h"
59 #include "screen.h"
60 #include "ctlr.h"
61 #include "cg.h"
62 #include "resources.h"
63 #include "toggles.h"
64 
65 #include "actions.h"
66 #include "codepage.h"
67 #include "ctlrc.h"
68 #include "display8.h"
69 #include "display_charsets.h"
70 #include "display_charsets_dbcs.h"
71 #include "host.h"
72 #include "keymap.h"
73 #include "kybd.h"
74 #include "lazya.h"
75 #include "names.h"
76 #include "nvt.h"
77 #include "popups.h"
78 #include "save.h"
79 #include "screen.h"
80 #include "scroll.h"
81 #include "see.h"
82 #include "status.h"
83 #include "tables.h"
84 #include "telnet.h"
85 #include "toupper.h"
86 #include "trace.h"
87 #include "unicodec.h"
88 #include "unicode_dbcs.h"
89 #include "utils.h"
90 #include "xactions.h"
91 #include "xappres.h"
92 #include "xio.h"
93 #include "xkeypad.h"
94 #include "xmenubar.h"
95 #include "xsave.h"
96 #include "xscreen.h"
97 #include "xscroll.h"
98 #include "xstatus.h"
99 #include "xpopups.h"
100 #include "xtables.h"
101 
102 #if defined(HAVE_SYS_SELECT_H) /*[*/
103 #include <sys/select.h>		/* fd_set declaration */
104 #endif /*]*/
105 
106 #include "x3270.bm"
107 #include "wait.bm"
108 
109 #define SCROLLBAR_WIDTH	15
110 
111 #define NO_BANG(s)	(((s)[0] == '!')? (s)+1: (s))
112 
113 #if !defined(NBBY) /*[*/
114 #define NBBY 8
115 #endif /*]*/
116 #define BPW	(NBBY * sizeof(unsigned long))
117 
118 #define MAX_FONTS	50000
119 
120 #define SELECTED(baddr)		(selected[(baddr)/8] & (1 << ((baddr)%8)))
121 #define SET_SELECT(baddr)	(selected[(baddr)/8] |= (1 << ((baddr)%8)))
122 
123 /* Globals */
124 Dimension       main_width;		/* desired toplevel width */
125 bool            scrollbar_changed = false;
126 bool            model_changed = false;
127 bool		efont_changed = false;
128 bool		oversize_changed = false;
129 bool		scheme_changed = false;
130 Pixel           keypadbg_pixel;
131 bool            flipped = false;
132 Pixmap          x3270_icon;
133 bool		shifted = false;
134 struct font_list *font_list = (struct font_list *) NULL;
135 int             font_count = 0;
136 char	       *efontname;
137 char           *efont_charset;
138 char           *efont_charset_dbcs;
139 bool		efont_matches = true;
140 char	       *full_efontname;
141 char	       *full_efontname_dbcs;
142 bool		visible_control = false;
143 unsigned	fixed_width, fixed_height;
144 bool		user_resize_allowed = true;
145 int		hhalo, vhalo;
146 int		dpi = 96;
147 int		dpi_scale = 100;
148 bool		dpi_override = false;
149 
150 #define gray_width 2
151 #define gray_height 2
152 static char gray_bits[] = { 0x01, 0x02 };
153 
154 /* Statics */
155 static unsigned char  *selected;	/* selection bitmap */
156 static bool	allow_resize;
157 static Dimension main_height;		/* desired toplevel width */
158 static struct sp *temp_image;		/* temporary for X display */
159 static Pixel	colorbg_pixel;
160 static bool	crosshair_enabled = true;
161 static bool     cursor_displayed = false;
162 static bool	lower_crosshair_displayed = false;
163 static bool     cursor_enabled = true;
164 static bool     cursor_blink_pending = false;
165 static XtIntervalId cursor_blink_id;
166 static int	field_colors[4];
167 static bool     in_focus = false;
168 static bool     line_changed = false;
169 static bool     cursor_changed = false;
170 static bool     iconic = false;
171 static bool     maximized = false;
172 static Widget   container;
173 static Widget   scrollbar;
174 static Dimension menubar_height;
175 static Dimension container_width;
176 static Dimension cwidth_nkp;		/* container width, without integral
177 					   keypad */
178 static Dimension container_height;
179 static Dimension scrollbar_width;
180 static char    *aicon_text = NULL;
181 static XFontStruct *ailabel_font;
182 static Dimension aicon_label_height = 0;
183 static GC       ailabel_gc;
184 static Pixel    cpx[16];
185 static bool     cpx_done[16];
186 static Pixel    normal_pixel;
187 static Pixel    select_pixel;
188 static Pixel    bold_pixel;
189 static Pixel    selbg_pixel;
190 static Pixel    cursor_pixel;
191 static bool     text_blinking_on = true;
192 static bool     text_blinkers_exist = false;
193 static bool     text_blink_scheduled = false;
194 static XtIntervalId text_blink_id;
195 static XtTranslations screen_t00 = NULL;
196 static XtTranslations screen_t0 = NULL;
197 static XtTranslations container_t00 = NULL;
198 static XtTranslations container_t0 = NULL;
199 static XChar2b *rt_buf = (XChar2b *) NULL;
200 static char    *color_name[16] = {
201     NULL, NULL, NULL, NULL,
202     NULL, NULL, NULL, NULL,
203     NULL, NULL, NULL, NULL,
204     NULL, NULL, NULL, NULL
205 };
206 static bool	configure_ticking = false;
207 static XtIntervalId configure_id;
208 static bool highlight_bold = false;
209 
210 static Pixmap   inv_icon;
211 static Pixmap   wait_icon;
212 static Pixmap   inv_wait_icon;
213 static bool     icon_inverted = false;
214 static Widget	icon_shell;
215 
216 static struct font_list *font_last = (struct font_list *) NULL;
217 
218 static struct {
219     Font font;
220     XFontStruct *font_struct;
221     bool unicode;
222     int char_height;
223     int char_width;
224     int ascent;
225     int descent;
226     int xtra_width;
227     int d16_ix;
228 } dbcs_font;
229 static void xim_init(void);
230 XIM im;
231 XIC ic;
232 bool xim_error = false;
233 char *locale_name = NULL;
234 int ovs_offset = 1;
235 typedef struct {
236     XIMStyle style;
237     char *description;
238 } im_style_t;
239 static XIMStyle style;
240 char ic_focus;
241 static void send_spot_loc(void);
242 
243 /* Globals for undoing reconfigurations. */
244 static enum {
245     REDO_NONE,
246     REDO_FONT,
247     REDO_MODEL,
248     REDO_KEYPAD,
249     REDO_SCROLLBAR,
250     REDO_RESIZE
251 } screen_redo = REDO_NONE;
252 static char *redo_old_font = NULL;
253 static int redo_old_model;
254 static int redo_old_ov_cols;
255 static int redo_old_ov_rows;
256 
257 static unsigned char blank_map[32];
258 #define BKM_SET(n)	blank_map[(n)/8] |= 1 << ((n)%8)
259 #define BKM_ISSET(n)	((blank_map[(n)/8] & (1 << ((n)%8))) != 0)
260 
261 enum fallback_color { FB_WHITE, FB_BLACK };
262 static enum fallback_color ibm_fb = FB_WHITE;
263 
264 static char *default_display_charset = "3270cg-1a,3270cg-1,iso8859-1";
265 static char *required_display_charsets;
266 
267 static int crosshair_color = HOST_COLOR_PURPLE;
268 
269 #define CROSSABLE	(toggled(CROSSHAIR) && cursor_enabled && \
270 			 crosshair_enabled && in_focus)
271 #define CROSSED(b)	((BA_TO_COL(b) == cursor_col) || \
272 			 (BA_TO_ROW(b) == cursor_row))
273 
274 #define CROSS_COLOR	(mode.m3279? (GC_NONDEFAULT | crosshair_color) : FA_INT_NORM_NSEL)
275 
276 /*
277  * The screen state structure.  This structure is swapped whenever we switch
278  * between normal and active-iconic states.
279  */
280 #define NGCS	16
281 struct sstate {
282     Widget          widget;	/* the widget */
283     Window          window;	/* the window */
284     struct sp       *image;	/* what's on the X display */
285     int             cursor_daddr;	/* displayed cursor address */
286     bool	    xh_alt;	/* crosshair was drawn in alt area */
287     bool            exposed_yet;	/* have we been exposed yet? */
288     bool            overstrike;	/* are we overstriking? */
289     Dimension       screen_width;	/* screen dimensions in pixels */
290     Dimension       screen_height;
291     GC              gc[NGCS * 2],	/* standard, inverted GCs */
292 		    selgc[NGCS],	/* color selected text GCs */
293 		    mcgc,		/* monochrome block cursor GC */
294 		    ucgc,		/* unique-cursor-color cursor GC */
295 		    invucgc,	/* inverse ucgc */
296 		    clrselgc;	/* selected clearing GC */
297     int             char_height;
298     int             char_width;
299     Font		fid;
300     XFontStruct   *font;
301     int		   ascent;
302     int		   descent;
303     int		   xtra_width;
304     bool           standard_font;
305     bool	   extended_3270font;
306     bool	   full_apl_font;
307     bool           font_8bit;
308     bool	   font_16bit;
309     bool	   funky_font;
310     bool           obscured;
311     bool           copied;
312     bool	   unicode_font;
313     int		   d8_ix;
314     unsigned long  odd_width[256 / BPW];
315     unsigned long  odd_lbearing[256 / BPW];
316     XChar2b       *hx_text;
317     int            nhx_text;
318 };
319 static struct sstate nss;
320 static struct sstate iss;
321 static struct sstate *ss = &nss;
322 
323 #define	INIT_ODD(odd)	memset(odd, '\0', sizeof(odd))
324 #define SET_ODD(odd, n)	(odd)[(n) / BPW] |= 1 << ((n) % BPW)
325 #define IS_ODD(odd, n)	((odd)[(n) / BPW] & 1 << ((n) % BPW))
326 
327 #define DEFAULT_CHAR(f) (((f)->default_char >= (f)->min_char_or_byte2 && \
328 			  (f)->default_char <= (f)->max_char_or_byte2)? \
329 			    (f)->default_char: 32)
330 #define WHICH_CHAR(f, n) (((n) < (f)->min_char_or_byte2 || \
331 			   (n) > (f)->max_char_or_byte2)? \
332 			    DEFAULT_CHAR(f) : (n))
333 #define PER_CHAR(f, n)	((f)->per_char[WHICH_CHAR(f, n) - (f)->min_char_or_byte2])
334 
335 /* Globals based on nss, used mostly by status and select routines. */
336 Widget         *screen = &nss.widget;
337 Window         *screen_window = &nss.window;
338 int            *char_width = &nss.char_width;
339 int            *char_height = &nss.char_height;
340 int            *ascent = &nss.ascent;
341 int            *descent = &nss.descent;
342 bool           *standard_font = &nss.standard_font;
343 bool           *font_8bit = &nss.font_8bit;
344 bool           *font_16bit = &nss.font_16bit;
345 bool           *extended_3270font = &nss.extended_3270font;
346 bool           *full_apl_font = &nss.full_apl_font;
347 bool           *funky_font = &nss.funky_font;
348 int            *xtra_width = &nss.xtra_width;
349 Font           *fid = &nss.fid;
350 Dimension      *screen_height = &nss.screen_height;
351 
352 /* Mouse-cursor state */
353 enum mcursor_state { LOCKED, NORMAL, WAIT };
354 static enum mcursor_state mcursor_state = LOCKED;
355 static enum mcursor_state icon_cstate = NORMAL;
356 
357 /* Dumb font cache. */
358 typedef struct dfc {
359     	struct dfc *next;
360     	char *name;
361 	char *weight;
362 	int points;
363 	char *spacing;
364 	char *charset;
365 	bool good;
366 } dfc_t;
367 
368 static void aicon_init(void);
369 static void aicon_reinit(unsigned cmask);
370 static void screen_focus(bool in);
371 static void make_gc_set(struct sstate *s, int i, Pixel fg, Pixel bg);
372 static void make_gcs(struct sstate *s);
373 static void put_cursor(int baddr, bool on);
374 static void resync_display(struct sp *buffer, int first, int last);
375 static void draw_fields(struct sp *buffer, int first, int last);
376 static void render_text(struct sp *buffer, int baddr, int len,
377     bool block_cursor, struct sp *attrs);
378 static void cursor_on(const char *why);
379 static void schedule_cursor_blink(void);
380 static void schedule_text_blink(void);
381 static void inflate_screen(void);
382 static int fa_color(unsigned char fa);
383 static void redraw_lower_crosshair(void);
384 static bool cursor_off(const char *why, bool including_lower_crosshair,
385 	bool *xwo);
386 static void draw_aicon_label(void);
387 static void set_mcursor(void);
388 static void scrollbar_init(bool is_reset);
389 static void init_rsfonts(char *charset_name);
390 static void allocate_pixels(void);
391 static int fl_baddr(int baddr);
392 static GC get_gc(struct sstate *s, int color);
393 static GC get_selgc(struct sstate *s, int color);
394 static void default_color_scheme(void);
395 static bool xfer_color_scheme(char *cs, bool do_popup);
396 static void set_font_globals(XFontStruct *f, const char *ef, const char *fef,
397     Font ff, bool is_dbcs);
398 static void screen_connect(bool ignored);
399 static void configure_stable(XtPointer closure, XtIntervalId *id);
400 static void cancel_blink(void);
401 static void render_blanks(int baddr, int height, struct sp *buffer);
402 static void resync_text(int baddr, int len, struct sp *buffer);
403 static void screen_reinit(unsigned cmask);
404 static void aicon_font_init(void);
405 static void aicon_size(Dimension *iw, Dimension *ih);
406 static void invert_icon(bool inverted);
407 static char *lff_single(const char *name, const char *reqd_display_charset,
408     bool is_dbcs);
409 static char *load_fixed_font(const char *names, const char *reqd_charsets);
410 static void lock_icon(enum mcursor_state state);
411 static char *expand_cslist(const char *s);
412 static void hollow_cursor(int baddr);
413 static void xlate_dbcs(unsigned char, unsigned char, XChar2b *);
414 static void xlate_dbcs_unicode(ucs4_t, XChar2b *);
415 static void dfc_init(void);
416 static const char *dfc_search_family(const char *charset, dfc_t **dfc,
417 	void **cookie);
418 
419 static action_t SetFont_action;
420 static action_t Title_action;
421 static action_t WindowState_action;
422 
423 static XChar2b apl_to_udisplay(int d8_ix, unsigned char c);
424 static XChar2b apl_to_ldisplay(unsigned char c);
425 
426 /* Resize font list. */
427 struct rsfont {
428 	struct rsfont *next;
429 	char *name;
430 	int width;
431 	int height;
432 	int descent;
433 	int total_width;	/* transient */
434 	int total_height;	/* transient */
435 	int area;		/* transient */
436 };
437 static struct rsfont *rsfonts;
438 
439 #define BASE_MASK		0x0f	/* mask for 16 actual colors */
440 #define INVERT_MASK		0x10	/* toggle for inverted colors */
441 #define GC_NONDEFAULT		0x20	/* distinguishes "color 0" from zeroed
442 					    memory */
443 
444 #define COLOR_MASK		(GC_NONDEFAULT | BASE_MASK)
445 #define INVERT_COLOR(c)		((c) ^ INVERT_MASK)
446 #define NO_INVERT(c)		((c) & ~INVERT_MASK)
447 
448 #define DEFAULT_PIXEL		(mode.m3279 ? HOST_COLOR_BLUE : FA_INT_NORM_NSEL)
449 #define PIXEL_INDEX(c)		((c) & BASE_MASK)
450 
451 /*
452  * Rescale a dimension according to the DPI settings.
453  */
454 Dimension
rescale(Dimension d)455 rescale(Dimension d)
456 {
457     return (d * dpi_scale) / 100;
458 }
459 
460 /*
461  * Save 00 event translations.
462  */
463 void
save_00translations(Widget w,XtTranslations * t00)464 save_00translations(Widget w, XtTranslations *t00)
465 {
466 	*t00 = w->core.tm.translations;
467 }
468 
469 /*
470  * Define our event translations
471  */
472 void
set_translations(Widget w,XtTranslations * t00,XtTranslations * t0)473 set_translations(Widget w, XtTranslations *t00, XtTranslations *t0)
474 {
475 	struct trans_list *t;
476 
477 	if (t00 != NULL)
478 		XtOverrideTranslations(w, *t00);
479 
480 	for (t = trans_list; t != NULL; t = t->next)
481 		XtOverrideTranslations(w, lookup_tt(t->name, NULL));
482 
483 	*t0 = w->core.tm.translations;
484 }
485 
486 /*
487  * Add or clear a temporary keymap.
488  */
489 void
screen_set_temp_keymap(XtTranslations trans)490 screen_set_temp_keymap(XtTranslations trans)
491 {
492 	if (trans != NULL) {
493 		XtOverrideTranslations(nss.widget, trans);
494 		XtOverrideTranslations(container, trans);
495 	} else {
496 		XtUninstallTranslations(nss.widget);
497 		XtOverrideTranslations(nss.widget, screen_t0);
498 		XtUninstallTranslations(container);
499 		XtOverrideTranslations(container, container_t0);
500 	}
501 }
502 
503 /*
504  * Change the baselevel keymap.
505  */
506 void
screen_set_keymap(void)507 screen_set_keymap(void)
508 {
509 	XtUninstallTranslations(nss.widget);
510 	set_translations(nss.widget, &screen_t00, &screen_t0);
511 	XtUninstallTranslations(container);
512 	set_translations(container, &container_t00, &container_t0);
513 }
514 
515 /*
516  * Crosshair color init.
517  */
518 static void
crosshair_color_init(void)519 crosshair_color_init(void)
520 {
521     int c = decode_host_color(appres.interactive.crosshair_color);
522 
523     if (c >= 0) {
524 	crosshair_color = c;
525     } else {
526 	xs_warning("Invalid %s: %s", ResCrosshairColor,
527 		appres.interactive.crosshair_color);
528 	crosshair_color = HOST_COLOR_PURPLE;
529     }
530 }
531 
532 /*
533  * Screen pre-initialization (before charset init).
534  */
535 void
screen_preinit(void)536 screen_preinit(void)
537 {
538     dfc_init();
539 }
540 
541 /*
542  * Clear fixed_width and fixed_height.
543  */
544 static void
clear_fixed(void)545 clear_fixed(void)
546 {
547     if (!maximized && user_resize_allowed && (fixed_width || fixed_height)) {
548 	vtrace("clearing fixed_width and fixed_height\n");
549 	fixed_width = 0;
550 	fixed_height = 0;
551     }
552 }
553 
554 /*
555  * Get the DPI of the display.
556  */
557 static void
dpi_init(void)558 dpi_init(void)
559 {
560     int rdpi = 0;
561     char *res_dpi;
562     char *type;
563     XrmValue value;
564 
565     res_dpi = xappres.dpi;
566     if (res_dpi != NULL) {
567 	rdpi = atoi(res_dpi);
568     } else if (XrmGetResource(rdb, "Xft.dpi", "Xft.dpi", &type, &value) == True
569 	    && !strcmp(type, "String")) {
570 	rdpi = atoi(value.addr);
571     }
572 
573     if (rdpi > 0) {
574 	dpi = rdpi;
575 	dpi_scale = (dpi * 100) / 96;
576 	dpi_override = true;
577     }
578 
579 #if defined(DPI_DEBUG) /*[*/
580     printf("display dpi %d -> scale %d (%s)\n", dpi, dpi_scale,
581 	    dpi_override? "override": "default");
582 #endif /*]*/
583 
584     hhalo = HHALO;
585     vhalo = VHALO;
586 }
587 
588 /*
589  * Initialize the screen.
590  */
591 void
screen_init(void)592 screen_init(void)
593 {
594     int i;
595 
596     dpi_init();
597 
598     visible_control = toggled(VISIBLE_CONTROL);
599 
600     /* Parse the fixed window size, if there is any. */
601     if (xappres.fixed_size) {
602 	char c;
603 
604 	if (sscanf(xappres.fixed_size, "%ux%u%c", &fixed_width,
605 				&fixed_height, &c) != 2 ||
606 			!fixed_width ||
607 			!fixed_height) {
608 	    popup_an_error("Invalid fixed size");
609 	    clear_fixed();
610 	} else {
611 	    /* Success. Don't allow user resize operations. */
612 	    user_resize_allowed = false;
613 	}
614     }
615     menubar_snap_enable(user_resize_allowed);
616 
617     /* Initialize ss. */
618     nss.cursor_daddr = 0;
619     nss.xh_alt = false;
620     nss.exposed_yet = false;
621 
622     /* Initialize "gray" bitmap. */
623     if (appres.interactive.mono) {
624 	gray = XCreatePixmapFromBitmapData(display,
625 		root_window, (char *)gray_bits,
626 		gray_width, gray_height,
627 		xappres.foreground, xappres.background, screen_depth);
628     }
629 
630     /* Initialize the blank map. */
631     memset((char *)blank_map, '\0', sizeof(blank_map));
632     for (i = 0; i < 256; i++) {
633 	if (ebc2asc0[i] == 0x20 || ebc2asc0[i] == 0xa0) {
634 	    BKM_SET(i);
635 	}
636     }
637 
638     /* Initialize the emulated 3270 controller hardware. */
639     ctlr_init(ALL_CHANGE);
640 
641     /* Initialize the actve icon. */
642     aicon_init();
643 
644     /* Initialize the status line. */
645     status_init();
646 
647     /* Initialize the placement of the pop-up keypad. */
648     keypad_placement_init();
649 
650     /* Initialize the crosshair color. */
651     crosshair_color_init();
652 
653     /* Now call the "reinitialize" function to set everything else up. */
654     screen_reinit(ALL_CHANGE);
655 }
656 
657 /*
658  * Re-initialize the screen.
659  */
660 static void
screen_reinit(unsigned cmask)661 screen_reinit(unsigned cmask)
662 {
663     Dimension cwidth_curr;
664 
665     /* Allocate colors. */
666     if (cmask & COLOR_CHANGE) {
667 	if (mode.m3279) {
668 	    default_color_scheme();
669 	    xfer_color_scheme(xappres.color_scheme, false);
670 	}
671 	allocate_pixels();
672 
673 	/*
674 	 * In color mode, set highlight_bold from the resource.
675 	 * In monochrome, set it unconditionally.
676 	 */
677 	if (mode.m3279) {
678 	    highlight_bold = appres.highlight_bold;
679 	} else {
680 	    highlight_bold = true;
681 	}
682     }
683 
684     /* Define graphics contexts. */
685     if (cmask & (FONT_CHANGE | COLOR_CHANGE)) {
686 	make_gcs(&nss);
687     }
688 
689     /* Undo the horizonal crosshair buffers. */
690     if (cmask & FONT_CHANGE) {
691 	if (nss.hx_text != NULL) {
692 	    Replace(nss.hx_text, NULL);
693 	    nss.nhx_text = 0;
694 	}
695     }
696 
697     /* Reinitialize the controller. */
698     ctlr_reinit(cmask);
699 
700     /* Allocate buffers. */
701     if (cmask & MODEL_CHANGE) {
702 	/* Selection bitmap */
703 	Replace(selected,
704 	    (unsigned char *)XtCalloc(sizeof(unsigned char),
705 				      (maxROWS * maxCOLS + 7) / 8));
706 
707 	/* X display image */
708 	Replace(nss.image, (struct sp *)XtCalloc(sizeof(struct sp),
709 						maxROWS * maxCOLS));
710 	Replace(temp_image, (struct sp *)XtCalloc(sizeof(struct sp),
711 						 maxROWS*maxCOLS));
712 
713 	/* render_text buffers */
714 	Replace(rt_buf,
715 	    (XChar2b *)XtMalloc(maxCOLS * sizeof(XChar2b)));
716     } else {
717 	memset((char *) nss.image, 0,
718 		      sizeof(struct sp) * maxROWS * maxCOLS);
719     }
720 
721     /* Compute SBCS/DBCS size differences. */
722     if ((cmask & FONT_CHANGE) && dbcs) {
723 	int wdiff, adiff, ddiff;
724 	char *xs;
725 	int xx;
726 
727 #if defined(_ST) /*[*/
728 	printf("nss ascent %d descent %d\n"
729 	       "dbcs ascent %d descent %d\n",
730 	       nss.ascent, nss.descent,
731 	       dbcs_font.ascent, dbcs_font.descent);
732 #endif /*]*/
733 
734 	/* Compute width difference. */
735 	wdiff = (2 * nss.char_width) - dbcs_font.char_width;
736 	if (wdiff > 0) {
737 	    /* SBCS font is too wide */
738 	    dbcs_font.xtra_width = wdiff;
739 #if defined(_ST) /*[*/
740 	    printf("SBCS wider %d\n", wdiff);
741 #endif /*]*/
742 	} else if (wdiff < 0) {
743 	    /* SBCS font is too narrow */
744 	    if (wdiff % 2) {
745 		nss.xtra_width = (-wdiff)/2 + 1;
746 		dbcs_font.xtra_width = 1;
747 #if defined(_ST) /*[*/
748 		printf("SBCS odd\n");
749 #endif /*]*/
750 	    } else {
751 		nss.xtra_width = (-wdiff)/2;
752 	    }
753 #if defined(_ST) /*[*/
754 	    printf("DBCS wider %d\n", -wdiff);
755 #endif /*]*/
756 	} else {
757 	    dbcs_font.xtra_width = nss.xtra_width = 0;
758 #if defined(_ST) /*[*/
759 	    printf("Width matches.\n");
760 #endif /*]*/
761 	}
762 	/* Add some extra on top of that. */
763 	if ((xs = getenv("X3270_XWIDTH")) != NULL) {
764 	    xx = atoi(xs);
765 	    if (xx && xx < 10) {
766 		nss.xtra_width += xx;
767 		dbcs_font.xtra_width += 2*xx;
768 	    }
769 	}
770 	nss.char_width += nss.xtra_width;
771 	dbcs_font.char_width += dbcs_font.xtra_width;
772 
773 	/*
774 	 * Compute height difference, doing ascent and descent
775 	 * separately.
776 	 */
777 	adiff = nss.ascent - dbcs_font.ascent;
778 	if (adiff > 0) {
779 #if defined(_ST) /*[*/
780 	    printf("SBCS higher by %d\n", adiff);
781 #endif /*]*/
782 	    dbcs_font.ascent += adiff;
783 	    dbcs_font.char_height += adiff;
784 	} else if (adiff < 0) {
785 #if defined(_ST) /*[*/
786 	    printf("DBCS higher by %d\n", -adiff);
787 #endif /*]*/
788 	    nss.ascent += -adiff;
789 	    nss.char_height += -adiff;
790 	} else {
791 #if defined(_ST) /*[*/
792 	    printf("Ascent matches\n");
793 #endif /*]*/
794 	}
795 	ddiff = nss.descent - dbcs_font.descent;
796 	if (ddiff > 0) {
797 #if defined(_ST) /*[*/
798 	    printf("SBCS lower by %d\n", ddiff);
799 #endif /*]*/
800 	    dbcs_font.descent += ddiff;
801 	    dbcs_font.char_height += ddiff;
802 	} else if (ddiff < 0) {
803 #if defined(_ST) /*[*/
804 	    printf("DBCS lower by %d\n", -ddiff);
805 #endif /*]*/
806 	    nss.descent += -ddiff;
807 	    nss.char_height += -ddiff;
808 	} else {
809 #if defined(_ST) /*[*/
810 	    printf("Descent matches\n");
811 #endif /*]*/
812 	}
813 
814 	/* Add a constant to the height. */
815 	if ((xs = getenv("X3270_XHEIGHT")) != NULL) {
816 	    xx = atoi(xs);
817 	    if (xx && xx < 10) {
818 		dbcs_font.descent += xx;
819 		nss.descent += xx;
820 		nss.char_height += xx;
821 	    }
822 	}
823     }
824 
825     /* Set up a container for the menubar, screen and keypad */
826 
827     if (toggled(SCROLL_BAR)) {
828 	scrollbar_width = rescale(SCROLLBAR_WIDTH);
829     } else {
830 	scrollbar_width = 0;
831     }
832 
833     if (cmask & (FONT_CHANGE | MODEL_CHANGE | SCROLL_CHANGE)) {
834 	Dimension sw;
835 	bool h_clip = false;
836 
837 	if (fixed_width) {
838 	    Dimension w, h;
839 
840 	    /* Compute the horizontal halo. */
841 	    w = SCREEN_WIDTH(ss->char_width, 0)+2 + scrollbar_width;
842 	    if (w > fixed_width) {
843 		vtrace("Screen is too wide for fixed width, will clip\n");
844 		hhalo = HHALO;
845 		h_clip = true;
846 	    } else {
847 		/* Set the horizontal halo to center the screen. */
848 		hhalo = (fixed_width - w) / 2;
849 	    }
850 
851 	    /* Compute the vertical halo. */
852 	    h = menubar_qheight(fixed_width) +
853 		SCREEN_HEIGHT(ss->char_height, ss->descent, 0)+2;
854 	    if (kp_placement == kp_integral && xappres.keypad_on) {
855 		/*
856 		 * If the integral keypad is on, the fixed height includes it.
857 		 */
858 		h += keypad_qheight();
859 	    }
860 	    if (h > fixed_height) {
861 		vtrace("Screen is too tall for fixed height, will clip\n");
862 		vhalo = VHALO;
863 	    } else {
864 		/*
865 		 * Center the screen, sort of.
866 		 * '3' is a magic number here -- the vertical halo is used once
867 		 * above the screen and twice below. That should change.
868 		 */
869 		vhalo = (fixed_height - h) / 3;
870 	    }
871 	} else {
872 	    vhalo = VHALO;
873 	    hhalo = HHALO;
874 	}
875 
876 	/* Increase the horizontal halo to hold the integral keypad. */
877 	sw = SCREEN_WIDTH(ss->char_width, hhalo)+2 + scrollbar_width;
878 	if (!h_clip &&
879 		(!fixed_width || (min_keypad_width() < fixed_width)) &&
880 		user_resize_allowed &&
881 		kp_placement == kp_integral &&
882 		xappres.keypad_on &&
883 		min_keypad_width() > sw) {
884 	    hhalo = (min_keypad_width() - (SCREEN_WIDTH(ss->char_width, 0)+2 + scrollbar_width)) / 2;
885 	}
886 
887 	nss.screen_width = SCREEN_WIDTH(ss->char_width, hhalo);
888 	nss.screen_height = SCREEN_HEIGHT(ss->char_height, ss->descent, vhalo);
889     }
890 
891     if (fixed_width) {
892 	container_width = fixed_width;
893     } else {
894 	container_width = nss.screen_width+2 + scrollbar_width;
895     }
896     cwidth_nkp = container_width;
897 
898     if (container == NULL) {
899 	container = XtVaCreateManagedWidget(
900 		"container", huskWidgetClass, toplevel,
901 		XtNborderWidth, 0,
902 		XtNwidth, container_width,
903 		XtNheight, 10, /* XXX -- a temporary lie to make Xt happy */
904 		NULL);
905 	save_00translations(container, &container_t00);
906 	set_translations(container, NULL, &container_t0);
907 	if (appres.interactive.mono) {
908 	    XtVaSetValues(container, XtNbackgroundPixmap, gray, NULL);
909 	}
910     }
911 
912     /* Initialize the menu bar and integral keypad */
913 
914     cwidth_curr = xappres.keypad_on? container_width: cwidth_nkp;
915     menubar_height = menubar_qheight(cwidth_curr);
916     menubar_init(container, container_width, cwidth_curr);
917 
918     if (fixed_height) {
919 	container_height = fixed_height;
920     } else {
921 	container_height = menubar_height + nss.screen_height+2;
922 	if (kp_placement == kp_integral && xappres.keypad_on) {
923 	    container_height += keypad_qheight();
924 	}
925     }
926     if (kp_placement == kp_integral) {
927 	if (xappres.keypad_on) {
928 	    keypad_init(container,
929 		    menubar_height + nss.screen_height+2,
930 		    container_width,
931 		    false, false);
932 	} else {
933 	    ikeypad_destroy();
934 	}
935     }
936 
937     /* Create screen and set container dimensions */
938     inflate_screen();
939 
940     /* Create scrollbar */
941     scrollbar_init((cmask & MODEL_CHANGE) != 0);
942 
943     XtRealizeWidget(toplevel);
944     nss.window = XtWindow(nss.widget);
945     set_mcursor();
946 
947     /* Reinitialize the active icon. */
948     aicon_reinit(cmask);
949 
950     /* Reinitialize the status line. */
951     status_reinit(cmask);
952 
953     /* Initialize the input method. */
954     if ((cmask & CODEPAGE_CHANGE) && dbcs) {
955 	    xim_init();
956     }
957 
958     cursor_changed = true;
959 
960     line_changed = true;
961 
962     /* Redraw the screen. */
963     xaction_internal(PA_Expose_xaction, IA_REDRAW, NULL, NULL);
964 
965     /*
966      * We're all done processing the user's request, so allow normal resizing
967      * again.
968      */
969     clear_fixed();
970 }
971 
972 static void
set_toplevel_sizes(const char * why)973 set_toplevel_sizes(const char *why)
974 {
975     Dimension tw, th;
976 
977     tw = container_width;
978     th = container_height;
979     if (fixed_width) {
980 	if (!maximized) {
981 	    XtVaSetValues(toplevel,
982 		    XtNwidth, fixed_width,
983 		    XtNheight, fixed_height,
984 		    NULL);
985 	    vtrace("set_toplevel_sizes(%s), fixed: %dx%d\n", why, fixed_width,
986 		    fixed_height);
987 	    if (!user_resize_allowed) {
988 		XtVaSetValues(toplevel,
989 			XtNbaseWidth, fixed_width,
990 			XtNbaseHeight, fixed_height,
991 			XtNminWidth, fixed_width,
992 			XtNminHeight, fixed_height,
993 			XtNmaxWidth, fixed_width,
994 			XtNmaxHeight, fixed_height,
995 			NULL);
996 	    }
997 	    XtVaSetValues(container,
998 		    XtNwidth, fixed_width,
999 		    XtNheight, fixed_height,
1000 		    NULL);
1001 	}
1002 	main_width = fixed_width;
1003 	main_height = fixed_height;
1004     } else {
1005 	if (!maximized) {
1006 	    XtVaSetValues(toplevel,
1007 		    XtNwidth, tw,
1008 		    XtNheight, th,
1009 		    NULL);
1010 	    vtrace("set_toplevel_sizes(%s), not fixed: %hux%hu\n", why, tw, th);
1011 	    if (!allow_resize) {
1012 		XtVaSetValues(toplevel,
1013 			XtNbaseWidth, tw,
1014 			XtNbaseHeight, th,
1015 			XtNminWidth, tw,
1016 			XtNminHeight, th,
1017 			XtNmaxWidth, tw,
1018 			XtNmaxHeight, th,
1019 			NULL);
1020 	    }
1021 	    XtVaSetValues(container,
1022 		    XtNwidth, container_width,
1023 		    XtNheight, container_height,
1024 		    NULL);
1025 	}
1026 	main_width = tw;
1027 	main_height = th;
1028     }
1029 
1030     /*
1031      * Start a timer ticking, in case the window manager doesn't approve
1032      * of the change.
1033      */
1034     if (configure_ticking) {
1035 	XtRemoveTimeOut(configure_id);
1036     }
1037     configure_id = XtAppAddTimeOut(appcontext, 500, configure_stable, 0);
1038     configure_ticking = true;
1039 
1040     keypad_move();
1041     {
1042 	static bool first = true;
1043 	if (first) {
1044 	    first = false;
1045 	} else {
1046 	    popups_move();
1047 	}
1048     }
1049 }
1050 
1051 static void
inflate_screen(void)1052 inflate_screen(void)
1053 {
1054     /* Create the screen window */
1055     if (nss.widget == NULL) {
1056 	nss.widget = XtVaCreateManagedWidget(
1057 	    "screen", widgetClass, container,
1058 	    XtNwidth, nss.screen_width,
1059 	    XtNheight, nss.screen_height,
1060 	    XtNx, 0,
1061 	    XtNy, menubar_height,
1062 	    XtNbackground,
1063 		appres.interactive.mono? xappres.background: colorbg_pixel,
1064 	    NULL);
1065 	save_00translations(nss.widget, &screen_t00);
1066 	set_translations(nss.widget, NULL,
1067 	    &screen_t0);
1068     } else {
1069 	XtVaSetValues(nss.widget,
1070 	    XtNwidth, nss.screen_width,
1071 	    XtNheight, nss.screen_height,
1072 	    XtNx, 0,
1073 	    XtNy, menubar_height,
1074 	    XtNbackground,
1075 		appres.interactive.mono? xappres.background: colorbg_pixel,
1076 	    NULL);
1077     }
1078 
1079     /* Set the container and toplevel dimensions */
1080     XtVaSetValues(container,
1081 	    XtNwidth, container_width,
1082 	    XtNheight, container_height,
1083 	    NULL);
1084 
1085     set_toplevel_sizes("inflate_screen");
1086 }
1087 
1088 /* Scrollbar support. */
1089 void
screen_set_thumb(float top,float shown,int saved _is_unused,int screen _is_unused,int back _is_unused)1090 screen_set_thumb(float top, float shown, int saved _is_unused,
1091 	int screen _is_unused, int back _is_unused)
1092 {
1093     if (toggled(SCROLL_BAR)) {
1094 	XawScrollbarSetThumb(scrollbar, top, shown);
1095     }
1096 }
1097 
1098 static void
screen_scroll_proc(Widget w _is_unused,XtPointer client_data _is_unused,XtPointer position)1099 screen_scroll_proc(Widget w _is_unused, XtPointer client_data _is_unused,
1100     XtPointer position)
1101 {
1102     scroll_proc((long)position, (int)nss.screen_height);
1103 }
1104 
1105 static void
screen_jump_proc(Widget w _is_unused,XtPointer client_data _is_unused,XtPointer percent_ptr)1106 screen_jump_proc(Widget w _is_unused, XtPointer client_data _is_unused,
1107     XtPointer percent_ptr)
1108 {
1109     jump_proc(*(float *)percent_ptr);
1110 }
1111 
1112 /* Create, move, or reset the scrollbar. */
1113 static void
scrollbar_init(bool is_reset)1114 scrollbar_init(bool is_reset)
1115 {
1116     if (!scrollbar_width) {
1117 	if (scrollbar != NULL) {
1118 	    XtUnmapWidget(scrollbar);
1119 	}
1120     } else {
1121 	if (scrollbar == NULL) {
1122 	    scrollbar = XtVaCreateManagedWidget(
1123 		    "scrollbar", scrollbarWidgetClass,
1124 		    container,
1125 		    XtNx, nss.screen_width+1,
1126 		    XtNy, menubar_height,
1127 		    XtNwidth, scrollbar_width-1,
1128 		    XtNheight, nss.screen_height,
1129 		    NULL);
1130 		XtAddCallback(scrollbar, XtNscrollProc,
1131 		    screen_scroll_proc, NULL);
1132 		XtAddCallback(scrollbar, XtNjumpProc,
1133 		    screen_jump_proc, NULL);
1134 	} else {
1135 	    XtVaSetValues(scrollbar,
1136 		    XtNx, nss.screen_width+1,
1137 		    XtNy, menubar_height,
1138 		    XtNwidth, scrollbar_width-1,
1139 		    XtNheight, nss.screen_height,
1140 		    NULL);
1141 		XtMapWidget(scrollbar);
1142 	}
1143 	XawScrollbarSetThumb(scrollbar, 0.0, 1.0);
1144     }
1145 
1146     /*
1147      * If the screen dimensions have changed, reallocate the scroll
1148      * save area.
1149      */
1150     if (is_reset || !scroll_initted) {
1151 	scroll_buf_init();
1152     } else {
1153 	rethumb();
1154     }
1155 }
1156 
1157 /* Turn the scrollbar on or off */
1158 static void
toggle_scrollBar(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)1159 toggle_scrollBar(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
1160 {
1161     scrollbar_changed = true;
1162 
1163     if (toggled(SCROLL_BAR)) {
1164 	scrollbar_width = rescale(SCROLLBAR_WIDTH);
1165 	screen_redo = REDO_SCROLLBAR;
1166     } else {
1167 	scroll_to_bottom();
1168 	scrollbar_width = 0;
1169     }
1170 
1171     screen_reinit(SCROLL_CHANGE);
1172     if (toggled(SCROLL_BAR)) {
1173 	rethumb();
1174     }
1175 }
1176 
1177 /* Register an APL mode toggle. */
1178 static void
toggle_aplMode(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)1179 toggle_aplMode(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
1180 {
1181     status_apl_mode(toggled(APL_MODE));
1182 }
1183 
1184 /*
1185  * Called when a host connects, disconnects or changes NVT/3270 modes.
1186  */
1187 static void
screen_connect(bool ignored _is_unused)1188 screen_connect(bool ignored _is_unused)
1189 {
1190     if (ea_buf == NULL) {
1191 	return;		/* too soon */
1192     }
1193 
1194     if (CONNECTED) {
1195 	ctlr_erase(true);
1196 	cursor_enabled = true;
1197 	cursor_on("connect");
1198 	schedule_cursor_blink();
1199     } else {
1200 	if (appres.disconnect_clear) {
1201 	    ctlr_erase(true);
1202 	}
1203 	cursor_enabled = false;
1204 	cursor_off("connect", true, NULL);
1205     }
1206     if (toggled(CROSSHAIR)) {
1207 	screen_changed = true;
1208 	first_changed = 0;
1209 	last_changed = ROWS*COLS;
1210 	screen_disp(false);
1211     }
1212 
1213     mcursor_normal();
1214 }
1215 
1216 /*
1217  * Mouse cursor changes
1218  */
1219 
1220 static void
set_mcursor(void)1221 set_mcursor(void)
1222 {
1223     switch (mcursor_state) {
1224     case LOCKED:
1225 	XDefineCursor(display, nss.window, xappres.locked_mcursor);
1226 	break;
1227     case NORMAL:
1228 	XDefineCursor(display, nss.window, xappres.normal_mcursor);
1229 	break;
1230     case WAIT:
1231 	XDefineCursor(display, nss.window, xappres.wait_mcursor);
1232 	break;
1233     }
1234     lock_icon(mcursor_state);
1235 }
1236 
1237 void
mcursor_normal(void)1238 mcursor_normal(void)
1239 {
1240     if (CONNECTED) {
1241 	mcursor_state = NORMAL;
1242     } else if (HALF_CONNECTED) {
1243 	mcursor_state = WAIT;
1244     } else {
1245 	mcursor_state = LOCKED;
1246     }
1247     set_mcursor();
1248 }
1249 
1250 void
mcursor_waiting(void)1251 mcursor_waiting(void)
1252 {
1253     mcursor_state = WAIT;
1254     set_mcursor();
1255 }
1256 
1257 void
mcursor_locked(void)1258 mcursor_locked(void)
1259 {
1260     mcursor_state = LOCKED;
1261     set_mcursor();
1262 }
1263 
1264 /*
1265  * Called from the keypad button to expose or hide the integral keypad.
1266  */
1267 void
screen_showikeypad(bool on)1268 screen_showikeypad(bool on)
1269 {
1270     if (on) {
1271 	screen_redo = REDO_KEYPAD;
1272     }
1273 
1274     inflate_screen(); /* redundant now? */
1275     screen_reinit(FONT_CHANGE);
1276 }
1277 
1278 /*
1279  * The host just wrote a blinking character; make sure it blinks
1280  */
1281 void
blink_start(void)1282 blink_start(void)
1283 {
1284     text_blinkers_exist = true;
1285     if (!text_blink_scheduled) {
1286 	/* Start in "on" state and start first iteration */
1287 	text_blinking_on = true;
1288 	schedule_text_blink();
1289     }
1290 }
1291 
1292 /*
1293  * Restore blanked blinking text
1294  */
1295 static void
text_blink_it(XtPointer closure _is_unused,XtIntervalId * id _is_unused)1296 text_blink_it(XtPointer closure _is_unused, XtIntervalId *id _is_unused)
1297 {
1298     /* Flip the state. */
1299     text_blinking_on = !text_blinking_on;
1300 
1301     /* Force a screen redraw. */
1302     ctlr_changed(0, ROWS*COLS);
1303 
1304     /* If there is still blinking text, schedule the next iteration */
1305     if (text_blinkers_exist) {
1306 	schedule_text_blink();
1307     } else {
1308 	text_blink_scheduled = false;
1309     }
1310 }
1311 
1312 /*
1313  * Schedule an event to restore blanked blinking text
1314  */
1315 static void
schedule_text_blink(void)1316 schedule_text_blink(void)
1317 {
1318     text_blink_scheduled = true;
1319     text_blink_id = XtAppAddTimeOut(appcontext, 500, text_blink_it, 0);
1320 }
1321 
1322 
1323 /*
1324  * Fill in an XChar2b from an APL character.
1325  */
1326 static void
apl_display_char(XChar2b * text,unsigned char apl)1327 apl_display_char(XChar2b *text, unsigned char apl)
1328 {
1329     if (ss->extended_3270font) {
1330 	text->byte1 = 1;
1331 	text->byte2 = ebc2cg0[apl];
1332     } else {
1333 	if (ss->font_16bit) {
1334 	    *text = apl_to_udisplay(ss->d8_ix, apl);
1335 	} else {
1336 	    *text = apl_to_ldisplay(apl);
1337 	}
1338     }
1339 }
1340 
1341 /*
1342  * Return the vertical crosshair character for the current font.
1343  */
1344 XChar2b
screen_vcrosshair(void)1345 screen_vcrosshair(void)
1346 {
1347     XChar2b v;
1348 
1349     apl_display_char(&v, 0xbf);
1350     return v;
1351 }
1352 
1353 /*
1354  * Return a GC for drawing the crosshair.
1355  */
1356 GC
screen_crosshair_gc(void)1357 screen_crosshair_gc(void)
1358 {
1359     return screen_gc(CROSS_COLOR);
1360 }
1361 
1362 /* Draw the line at the top of the OIA. */
1363 static void
draw_oia_line(void)1364 draw_oia_line(void)
1365 {
1366     XDrawLine(display, ss->window,
1367 	    get_gc(ss, GC_NONDEFAULT | DEFAULT_PIXEL),
1368 	    0,
1369 	    nss.screen_height - nss.char_height - 3,
1370 	    ssCOL_TO_X(maxCOLS)+hhalo,
1371 	    nss.screen_height - nss.char_height - 3);
1372 }
1373 
1374 /*
1375  * Draw or erase the crosshair in the margin between the primary and alternate
1376  * screens.
1377  */
1378 static void
crosshair_margin(bool draw,const char * why)1379 crosshair_margin(bool draw, const char *why)
1380 {
1381     int column;
1382     int hhalo_chars = 0, vhalo_chars = 0;
1383 
1384 #ifdef CROSSHAIR_DEBUG /*[*/
1385     vtrace("crosshair_margin(%s, %s) cursor=%d", why,
1386 	    draw? "draw": "undraw",
1387 	    draw? cursor_addr: ss->cursor_daddr);
1388 #endif /*]*/
1389 
1390     /* Compute the number of halo characters. */
1391     if (hhalo > HHALO) {
1392 	hhalo_chars = (hhalo + (ss->char_width - 1)) / ss->char_width;
1393     }
1394     if (vhalo > VHALO) {
1395 	vhalo_chars = (vhalo + (ss->char_height - 1)) / ss->char_height;
1396     }
1397 
1398     if (draw) {
1399 	int nhx;
1400 	XTextItem16 text1;
1401 	int i;
1402 
1403 	ss->xh_alt = false;
1404 
1405 	/* Compute the cursor column. */
1406 	column = BA_TO_COL(cursor_addr);
1407 	if (flipped) {
1408 	    column = (cCOLS - 1) - column;
1409 	}
1410 
1411 	/* Set up an array of characters for drawing horizonal lines. */
1412 	nhx = maxCOLS - cCOLS;
1413 	if (hhalo_chars > nhx) {
1414 	    nhx = hhalo_chars;
1415 	}
1416 	if (nhx > 0 && (ss->hx_text == NULL || ss->nhx_text < nhx)) {
1417 	    ss->nhx_text = nhx;
1418 	    Replace(ss->hx_text, (XChar2b *)Malloc(nhx * sizeof(XChar2b)));
1419 	    for (i = 0; i < nhx; i++) {
1420 		apl_display_char(&ss->hx_text[i], 0xa2);
1421 	    }
1422 	}
1423 
1424 	/* To the right. */
1425 	if (maxCOLS > cCOLS) {
1426 	    text1.chars = ss->hx_text;
1427 	    text1.nchars = maxCOLS - cCOLS;
1428 	    text1.delta = 0;
1429 	    text1.font = ss->fid;
1430 	    XDrawText16(display, ss->window, get_gc(ss, CROSS_COLOR),
1431 		    ssCOL_TO_X(cCOLS),
1432 		    ssROW_TO_Y(BA_TO_ROW(cursor_addr)),
1433 		    &text1, 1);
1434 
1435 	    /* Remember we need to erase later. */
1436 	    ss->xh_alt = true;
1437 	}
1438 
1439 	/* Down the bottom. */
1440 	if (maxROWS > ROWS) {
1441 	    XChar2b text;
1442 
1443 	    apl_display_char(&text, 0xbf);
1444 	    text1.chars = &text;
1445 	    text1.nchars = 1;
1446 	    text1.delta = 0;
1447 	    text1.font = ss->fid;
1448 	    for (i = ROWS; i < maxROWS; i++) {
1449 		XDrawText16(display, ss->window, get_gc(ss, CROSS_COLOR),
1450 			ssCOL_TO_X(column), ssROW_TO_Y(i), &text1, 1);
1451 	    }
1452 
1453 	    /* Remember we need to erase later. */
1454 	    ss->xh_alt = true;
1455 	}
1456 
1457 	/* Inside the vertical halo. */
1458 	if (vhalo_chars) {
1459 	    XChar2b text;
1460 
1461 	    apl_display_char(&text, 0xbf);
1462 	    text1.chars = &text;
1463 	    text1.nchars = 1;
1464 	    text1.delta = 0;
1465 	    text1.font = ss->fid;
1466 
1467 	    for (i = -vhalo_chars; i < 0; i++) {
1468 		XDrawText16(display, ss->window, get_gc(ss, CROSS_COLOR),
1469 			ssCOL_TO_X(column), ssROW_TO_Y(i), &text1, 1);
1470 	    }
1471 	    for (i = maxROWS;
1472 		 i < maxROWS + (2 * vhalo_chars);
1473 		 i++) {
1474 		XDrawText16(display, ss->window, get_gc(ss, CROSS_COLOR),
1475 			ssCOL_TO_X(column), ssROW_TO_Y(i), &text1, 1);
1476 	    }
1477 	}
1478 
1479 	/* In the horizontal halo. */
1480 	if (hhalo_chars) {
1481 	    text1.chars = ss->hx_text;
1482 	    text1.nchars = hhalo_chars;
1483 	    text1.delta = 0;
1484 	    text1.font = ss->fid;
1485 	    XDrawText16(display, ss->window, get_gc(ss, CROSS_COLOR),
1486 		    ssCOL_TO_X(-hhalo_chars),
1487 		    ssROW_TO_Y(BA_TO_ROW(cursor_addr)),
1488 		    &text1, 1);
1489 	    XDrawText16(display, ss->window, get_gc(ss, CROSS_COLOR),
1490 		    ssCOL_TO_X(maxCOLS),
1491 		    ssROW_TO_Y(BA_TO_ROW(cursor_addr)),
1492 		    &text1, 1);
1493 	}
1494 
1495 #ifdef CROSSHAIR_DEBUG /*[*/
1496 	vtrace(" -> %s\n", ss->xh_alt? "draw": "nop");
1497 #endif /*]*/
1498 	goto fix_status;
1499     }
1500 
1501     /* Erasing. */
1502 
1503     /* Compute the column. */
1504     column = BA_TO_COL(ss->cursor_daddr);
1505     if (flipped) {
1506 	column = (COLS - 1) - column;
1507     }
1508 
1509     if (vhalo_chars) {
1510 	/* Vertical halo. */
1511 	XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)),
1512 		ssCOL_TO_X(column), /* x */
1513 		ssROW_TO_Y(-vhalo_chars) - ss->ascent, /* y */
1514 		ss->char_width + 1, /* width */
1515 		ss->char_height * vhalo_chars /* height */);
1516 	XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)),
1517 		ssCOL_TO_X(column), /* x */
1518 		ssROW_TO_Y(maxROWS) - ss->ascent, /* y */
1519 		ss->char_width + 1, /* width */
1520 		ss->char_height * (2 * vhalo_chars) /* height */);
1521     }
1522     if (hhalo_chars) {
1523 	/* Horizontal halo. */
1524 	XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)),
1525 		ssCOL_TO_X(-hhalo_chars),
1526 		ssROW_TO_Y(BA_TO_ROW(ss->cursor_daddr)) - ss->ascent,
1527 		(ss->char_width * hhalo_chars) + 1, ss->char_height);
1528 	XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)),
1529 		ssCOL_TO_X(maxCOLS),
1530 		ssROW_TO_Y(BA_TO_ROW(ss->cursor_daddr)) - ss->ascent,
1531 		(ss->char_width * hhalo_chars) + 1, ss->char_height);
1532     }
1533 
1534     if (!ss->xh_alt) {
1535 #ifdef CROSSHAIR_DEBUG /*[*/
1536 	vtrace(" -> nop\n");
1537 #endif /*]*/
1538 	goto fix_status;
1539     }
1540 #ifdef CROSSHAIR_DEBUG /*[*/
1541     vtrace(" -> erase\n");
1542 #endif /*]*/
1543 
1544     /* To the right. */
1545     if (maxCOLS > defCOLS) {
1546 	XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)),
1547 		ssCOL_TO_X(defCOLS),
1548 		ssROW_TO_Y(BA_TO_ROW(ss->cursor_daddr)) - ss->ascent,
1549 		(ss->char_width * (maxCOLS - defCOLS)) + 1, ss->char_height);
1550     }
1551 
1552     /* Down the bottom. */
1553     if (maxROWS > defROWS) {
1554 	XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)),
1555 		ssCOL_TO_X(column), ssROW_TO_Y(defROWS) - ss->ascent,
1556 		ss->char_width + 1, ss->char_height * (maxROWS - defROWS));
1557     }
1558     ss->xh_alt = false;
1559 
1560 fix_status:
1561     status_touch(); /* could be more efficient */
1562     status_disp();
1563     draw_oia_line();
1564 }
1565 
1566 /* Redraw the lower crosshair. */
1567 static void
redraw_lower_crosshair(void)1568 redraw_lower_crosshair(void)
1569 {
1570     if (!lower_crosshair_displayed && toggled(CROSSHAIR)) {
1571 	int column;
1572 
1573 	crosshair_margin(true, "redraw");
1574 	column = cursor_addr % COLS;
1575 	if (flipped) {
1576 	    column = (COLS - 1) - column;
1577 	}
1578 	status_crosshair(column);
1579 	lower_crosshair_displayed = true;
1580 
1581 	/* Even though the cursor isn't visible, this is where it is. */
1582 	ss->cursor_daddr = cursor_addr;
1583     }
1584 }
1585 
1586 /*
1587  * Make the (displayed) cursor disappear.  Returns a bool indiciating if
1588  * the cursor was on before the call.
1589  *
1590  * *xwo is returned true if the lower crosshair was displayed and would then
1591  * need to be restored, independently of the cursor.
1592  */
1593 static bool
cursor_off(const char * why,bool including_lower_crosshair,bool * xwo)1594 cursor_off(const char *why, bool including_lower_crosshair, bool *xwo)
1595 {
1596     bool was_on = cursor_displayed;
1597     bool xwo_ret = false;
1598 
1599     if (cursor_displayed) {
1600 	cursor_displayed = false;
1601 	put_cursor(ss->cursor_daddr, false);
1602     }
1603 
1604     if (including_lower_crosshair && toggled(CROSSHAIR) &&
1605 	    lower_crosshair_displayed) {
1606 	/*
1607 	 * Erase the crosshair in the empty region between the primary
1608 	 * and alternate screens.
1609 	 */
1610 	crosshair_margin(false, why);
1611 	status_crosshair_off();
1612 	lower_crosshair_displayed = false;
1613 	xwo_ret = true;
1614     }
1615 
1616     if (xwo != NULL) {
1617 	*xwo = xwo_ret;
1618     }
1619     return was_on;
1620 }
1621 
1622 /*
1623  * Blink the cursor
1624  */
1625 static void
cursor_blink_it(XtPointer closure _is_unused,XtIntervalId * id _is_unused)1626 cursor_blink_it(XtPointer closure _is_unused, XtIntervalId *id _is_unused)
1627 {
1628     cursor_blink_pending = false;
1629     if (!CONNECTED || !toggled(CURSOR_BLINK)) {
1630 	return;
1631     }
1632     if (cursor_displayed) {
1633 	if (in_focus) {
1634 	    cursor_off("blink", false, NULL);
1635 	}
1636     } else {
1637 	cursor_on("blink");
1638     }
1639     schedule_cursor_blink();
1640 }
1641 
1642 /*
1643  * Schedule a cursor blink
1644  */
1645 static void
schedule_cursor_blink(void)1646 schedule_cursor_blink(void)
1647 {
1648     if (!toggled(CURSOR_BLINK) || cursor_blink_pending) {
1649 	return;
1650     }
1651     cursor_blink_pending = true;
1652     cursor_blink_id = XtAppAddTimeOut(appcontext, 500, cursor_blink_it, 0);
1653 }
1654 
1655 /*
1656  * Cancel a cursor blink
1657  */
1658 static void
cancel_blink(void)1659 cancel_blink(void)
1660 {
1661     if (cursor_blink_pending) {
1662 	XtRemoveTimeOut(cursor_blink_id);
1663 	cursor_blink_pending = false;
1664     }
1665 }
1666 
1667 /*
1668  * Toggle cursor blinking (called from menu)
1669  */
1670 static void
toggle_cursorBlink(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)1671 toggle_cursorBlink(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
1672 {
1673     if (!CONNECTED) {
1674 	return;
1675     }
1676 
1677     if (toggled(CURSOR_BLINK)) {
1678 	schedule_cursor_blink();
1679     } else {
1680 	cursor_on("toggleBlink");
1681     }
1682 }
1683 
1684 /*
1685  * Make the cursor visible at its (possibly new) location.
1686  */
1687 static void
cursor_on(const char * why)1688 cursor_on(const char *why)
1689 {
1690     if (cursor_enabled && !cursor_displayed) {
1691 	cursor_displayed = true;
1692 	put_cursor(cursor_addr, true);
1693 	ss->cursor_daddr = cursor_addr;
1694 	cursor_changed = false;
1695 
1696 	/*
1697 	 * Draw in the crosshair in the empty region between the primary
1698 	 * and alternate screens.
1699 	 */
1700 	if (in_focus && toggled(CROSSHAIR)) {
1701 	    int column;
1702 
1703 	    crosshair_margin(true, why);
1704 	    column = cursor_addr % COLS;
1705 	    if (flipped) {
1706 		column = (COLS - 1) - column;
1707 	    }
1708 	    status_crosshair(column);
1709 	    lower_crosshair_displayed = true;
1710 	}
1711     }
1712 }
1713 
1714 /*
1715  * Toggle the cursor (block/underline).
1716  */
1717 static void
toggle_altCursor(toggle_index_t ix,enum toggle_type tt _is_unused)1718 toggle_altCursor(toggle_index_t ix, enum toggle_type tt _is_unused)
1719 {
1720     bool was_on;
1721 
1722     /* do_toggle already changed the value; temporarily change it back */
1723     toggle_toggle(ix);
1724 
1725     was_on = cursor_off("toggleAlt", false, NULL);
1726 
1727     /* Now change it back again */
1728     toggle_toggle(ix);
1729 
1730     if (was_on) {
1731 	cursor_on("toggleAlt");
1732     }
1733 }
1734 
1735 /*
1736  * Move the cursor to the specified buffer address.
1737  */
1738 void
cursor_move(int baddr)1739 cursor_move(int baddr)
1740 {
1741     cursor_addr = baddr;
1742     if (CONNECTED) {
1743 	status_cursor_pos(cursor_addr);
1744     }
1745 }
1746 
1747 /*
1748  * Enable or disable cursor display (used by scroll logic)
1749  */
1750 void
enable_cursor(bool on)1751 enable_cursor(bool on)
1752 {
1753     if ((cursor_enabled = on) && CONNECTED) {
1754 	cursor_on("enable");
1755 	cursor_changed = true;
1756     } else {
1757 	cursor_off("enable", true, NULL);
1758     }
1759 }
1760 
1761 /*
1762  * Toggle the crosshair cursor.
1763  */
1764 static void
toggle_crosshair(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)1765 toggle_crosshair(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
1766 {
1767     bool turning_off = false;
1768 
1769     if (!toggled(CROSSHAIR)) {
1770 	/*
1771 	 * Turning it off. Turn it on momemtarily while we turn off the cursor,
1772 	 * so it gets erased.
1773 	 */
1774 	turning_off = true;
1775 	toggle_toggle(CROSSHAIR);
1776     }
1777 
1778     /*
1779      * Flip the cursor, which will undraw or draw the crosshair in the margins.
1780      *
1781      * Don't forget to turn the toggle back off, if we temporarily turned it
1782      * on above.
1783      */
1784     if (cursor_off("toggleCrosshair", true, NULL)) {
1785 	if (turning_off) {
1786 	    toggle_toggle(CROSSHAIR);
1787 	}
1788 	cursor_on("toggleCrosshair");
1789     } else {
1790 	if (turning_off) {
1791 	    toggle_toggle(CROSSHAIR);
1792 	}
1793     }
1794 
1795     /* Refresh the screen. */
1796     screen_changed = true;
1797     first_changed = 0;
1798     last_changed = ROWS*COLS;
1799     screen_disp(false);
1800 }
1801 
1802 /*
1803  * Toggle visible control characters.
1804  */
1805 static void
toggle_visible_control(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)1806 toggle_visible_control(toggle_index_t ix _is_unused,
1807 	enum toggle_type tt _is_unused)
1808 {
1809     visible_control = toggled(VISIBLE_CONTROL);
1810     screen_changed = true;
1811     first_changed = 0;
1812     last_changed = ROWS*COLS;
1813     screen_disp(false);
1814 }
1815 
1816 /*
1817  * Redraw the screen.
1818  */
1819 static void
do_redraw(Widget w,XEvent * event,String * params _is_unused,Cardinal * num_params _is_unused)1820 do_redraw(Widget w, XEvent *event, String *params _is_unused,
1821 	Cardinal *num_params _is_unused)
1822 {
1823     int x, y, width, height;
1824     int startcol, ncols;
1825     int	startrow, endrow, row;
1826     int i;
1827     int c0;
1828 
1829     if (w == nss.widget) {
1830 	keypad_first_up();
1831 	if (xappres.active_icon && iconic) {
1832 	    ss = &nss;
1833 	    iconic = false;
1834 	}
1835     } else if (xappres.active_icon && w == iss.widget) {
1836 	if (xappres.active_icon && !iconic) {
1837 	    ss = &iss;
1838 	    iconic = true;
1839 	}
1840     } else if (event) {
1841 	return;
1842     }
1843 
1844     /* Only redraw as necessary for an expose event */
1845     if (event && event->type == Expose) {
1846 	ss->exposed_yet = true;
1847 	x = event->xexpose.x;
1848 	y = event->xexpose.y;
1849 	width = event->xexpose.width;
1850 	height = event->xexpose.height;
1851 	startrow = ssY_TO_ROW(y);
1852 	if (startrow < 0) {
1853 	    startrow = 0;
1854 	}
1855 	if (startrow > 0) {
1856 	    startrow--;
1857 	}
1858 	endrow = ssY_TO_ROW(y+height);
1859 	endrow = endrow >= maxROWS ? maxROWS : endrow + 1;
1860 	startcol = ssX_TO_COL(x);
1861 	if (startcol < 0) {
1862 	    startcol = 0;
1863 	}
1864 	if (startcol > 0) {
1865 	    startcol--;
1866 	}
1867 	if (startcol >= maxCOLS) {
1868 	    goto no_draw;
1869 	}
1870 	ncols = (width / ss->char_width) + 2;
1871 	if (startcol + ncols > maxCOLS) {
1872 	    ncols = maxCOLS - startcol;
1873 	}
1874 	while ((ROWCOL_TO_BA(startrow, startcol) % maxCOLS) + ncols > maxCOLS) {
1875 	    ncols--;
1876 	}
1877 	for (row = startrow; row < endrow; row++) {
1878 	    memset((char *) &ss->image[ROWCOL_TO_BA(row, startcol)],
1879 		    0, ncols * sizeof(struct sp));
1880 	    if (visible_control) {
1881 		c0 = ROWCOL_TO_BA(row, startcol);
1882 
1883 		for (i = 0; i < ncols; i++) {
1884 		    ss->image[c0 + i].u.bits.ec = EBC_space;
1885 		}
1886 	    }
1887 	}
1888     no_draw:
1889 	;
1890 
1891     } else {
1892 	XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)), 0, 0,
1893 		ss->screen_width, ss->screen_height);
1894 	memset((char *) ss->image, 0,
1895 		(maxROWS*maxCOLS) * sizeof(struct sp));
1896 	if (visible_control) {
1897 	    for (i = 0; i < maxROWS*maxCOLS; i++) {
1898 		ss->image[i].u.bits.ec = EBC_space;
1899 	    }
1900 	}
1901 	ss->copied = false;
1902     }
1903     ctlr_changed(0, ROWS*COLS);
1904     cursor_changed = true;
1905     if (!xappres.active_icon || !iconic) {
1906 	line_changed = true;
1907 	status_touch();
1908     }
1909 }
1910 
1911 /*
1912  * Explicitly redraw the screen (invoked from the keyboard).
1913  */
1914 void
Redraw_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)1915 Redraw_xaction(Widget w, XEvent *event, String *params, Cardinal *num_params)
1916 {
1917     xaction_debug(Redraw_xaction, event, params, num_params);
1918     do_redraw(w, event, params, num_params);
1919 }
1920 
1921 /*
1922  * Implicitly redraw the screen (triggered by Expose events).
1923  */
1924 void
PA_Expose_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)1925 PA_Expose_xaction(Widget w, XEvent *event, String *params, Cardinal *num_params)
1926 {
1927 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
1928     xaction_debug(PA_Expose_xaction, event, params, num_params);
1929 #endif /*]*/
1930     do_redraw(w, event, params, num_params);
1931 }
1932 
1933 /*
1934  * Redraw the changed parts of the screen.
1935  */
1936 
1937 void
screen_disp(bool erasing)1938 screen_disp(bool erasing)
1939 {
1940     /* No point in doing anything if we aren't visible yet. */
1941     if (!ss->exposed_yet) {
1942 	return;
1943     }
1944 
1945     /*
1946      * We don't set "cursor_changed" when the host moves the cursor,
1947      * 'cause he might just move it back later.  Set it here if the cursor
1948      * has moved since the last call to screen_disp.
1949      */
1950     if (cursor_addr != ss->cursor_daddr) {
1951 	cursor_changed = true;
1952     }
1953 
1954     /* If the cursor has moved, tell the input method. */
1955     if (cursor_changed && ic != NULL &&
1956 	    style == (XIMPreeditPosition|XIMStatusNothing)) {
1957 #if defined(_ST) /*[*/
1958 	printf("spot_loc%s\n", rcba(cursor_addr));
1959 #endif /*]*/
1960 	send_spot_loc();
1961     }
1962 
1963     /*
1964      * If the cursor moves while the crosshair is toggled, redraw the whole
1965      * screen.
1966      */
1967     if (cursor_changed && toggled(CROSSHAIR)) {
1968 	screen_changed = true;
1969 	first_changed = 0;
1970 	last_changed = ROWS * COLS;
1971     }
1972 
1973     /*
1974      * If only the cursor has changed (and not the screen image), draw it.
1975      */
1976     if (cursor_changed && !screen_changed) {
1977 	if (!toggled(CROSSHAIR)) {
1978 	    if (cursor_off("disp", false, NULL)) {
1979 		cursor_on("disp");
1980 	    }
1981 	} else {
1982 	    screen_changed = true; /* repaint crosshair */
1983 	}
1984     }
1985 
1986     /*
1987      * Redraw the parts of the screen that need refreshing, and redraw the
1988      * cursor if necessary.
1989      */
1990     if (screen_changed) {
1991 	bool was_on = false;
1992 	bool xwo = false;
1993 
1994 	/* Draw the new screen image into "temp_image" */
1995 	if (erasing) {
1996 	    crosshair_enabled = false;
1997 	}
1998 	draw_fields(temp_image, first_changed, last_changed);
1999 	if (erasing) {
2000 	    crosshair_enabled = true;
2001 	}
2002 
2003 	/* Set "cursor_changed" if the text under it has changed. */
2004 	if (ss->image[fl_baddr(cursor_addr)].u.word !=
2005 		temp_image[fl_baddr(cursor_addr)].u.word) {
2006 	    cursor_changed = true;
2007 	}
2008 
2009 	/* Undraw the cursor, if necessary. */
2010 	if (cursor_changed) {
2011 	    was_on = cursor_off("cursorChanged", true, &xwo);
2012 	}
2013 
2014 	/* Intelligently update the X display with the new text. */
2015 	resync_display(temp_image, first_changed, last_changed);
2016 
2017 	/* Redraw the cursor. */
2018 	if (was_on) {
2019 	    cursor_on("cursorChanged");
2020 	}
2021 	if (xwo && !erasing) {
2022 	    redraw_lower_crosshair();
2023 	}
2024 
2025 	screen_changed = false;
2026 	first_changed = -1;
2027 	last_changed = -1;
2028     }
2029 
2030     if (!xappres.active_icon || !iconic) {
2031 	/* Refresh the status line. */
2032 	status_disp();
2033 
2034 	/* Refresh the line across the bottom of the screen. */
2035 	if (line_changed) {
2036 	    draw_oia_line();
2037 	    line_changed = false;
2038 	}
2039     }
2040     draw_aicon_label();
2041 }
2042 
2043 /*
2044  * Render a blank rectangle on the X display.
2045  */
2046 static void
render_blanks(int baddr,int height,struct sp * buffer)2047 render_blanks(int baddr, int height, struct sp *buffer)
2048 {
2049     int x, y;
2050 
2051 #if defined(_ST) /*[*/
2052     printf("render_blanks(baddr=%s, height=%d)\n", rcba(baddr), height);
2053 #endif /*]*/
2054 
2055     x = ssCOL_TO_X(BA_TO_COL(baddr));
2056     y = ssROW_TO_Y(BA_TO_ROW(baddr));
2057 
2058     XFillRectangle(display, ss->window,
2059 	get_gc(ss, INVERT_COLOR(0)),
2060 	x, y - ss->ascent,
2061 	(ss->char_width * COLS) + 1, (ss->char_height * height));
2062 
2063     memmove(&ss->image[baddr], &buffer[baddr],
2064 	    COLS * height *sizeof(struct sp));
2065 }
2066 
2067 /*
2068  * Check if a character position is blank.
2069  */
2070 static bool
bkm_isset(struct sp * buffer)2071 bkm_isset(struct sp *buffer)
2072 {
2073     if (buffer->u.bits.cs != CS_BASE) {
2074 	return false;
2075     }
2076     if (buffer->ucs4 != 0) {
2077 	return buffer->ucs4 == ' ' || buffer->ucs4 == 0xa0;
2078     }
2079     return BKM_ISSET(buffer->u.bits.ec);
2080 }
2081 
2082 /*
2083  * Check if a region of the screen is effectively empty: all blanks or nulls
2084  * with no extended highlighting attributes and not selected.
2085  *
2086  * Works _only_ with non-debug fonts.
2087  */
2088 static bool
empty_space(register struct sp * buffer,int len)2089 empty_space(register struct sp *buffer, int len)
2090 {
2091     int i;
2092 
2093     for (i = 0; i < len; i++, buffer++) {
2094 	if (buffer->u.bits.gr ||
2095 	    buffer->u.bits.sel ||
2096 	    (buffer->u.bits.fg & INVERT_MASK) ||
2097 	    (buffer->u.bits.cs != CS_BASE) ||
2098 	    !bkm_isset(buffer)) {
2099 	    return false;
2100 	}
2101     }
2102     return true;
2103 }
2104 
2105 
2106 /*
2107  * Reconcile the differences between a region of 'buffer' (what we want on
2108  * the X display) and ss->image[] (what is on the X display now).  The region
2109  * must not span lines.
2110  */
2111 static void
resync_text(int baddr,int len,struct sp * buffer)2112 resync_text(int baddr, int len, struct sp *buffer)
2113 {
2114     static bool ever = false;
2115     static unsigned long cmask = 0L;
2116     static unsigned long gmask = 0L;
2117 
2118 #if defined(_ST) /*[*/
2119     printf("resync_text(baddr=%s, len=%d)\n", rcba(baddr), len);
2120 #endif /*]*/
2121 
2122     /*
2123      * If the region begins on the right half of a DBCS character, back
2124      * up one.
2125      */
2126     if (baddr % COLS) {
2127 	enum dbcs_state d;
2128 
2129 	d = ctlr_dbcs_state(baddr);
2130 	if (IS_RIGHT(d)) {
2131 	    baddr--;
2132 	    len++;
2133 	}
2134     }
2135 
2136     if (!ever) {
2137 	struct sp b;
2138 
2139 	/* Create masks for the "important" fields in an sp. */
2140 	b.u.word = 0L;
2141 	b.u.bits.fg = COLOR_MASK | INVERT_MASK;
2142 	b.u.bits.sel = 1;
2143 	b.u.bits.gr = GR_UNDERLINE | GR_INTENSIFY;
2144 	cmask = b.u.word;
2145 
2146 	b.u.word = 0L;
2147 	b.u.bits.fg = INVERT_MASK;
2148 	b.u.bits.sel = 1;
2149 	b.u.bits.gr = 0xf;
2150 	gmask = b.u.word;
2151 
2152 	ever = true;
2153     }
2154 
2155     if (!visible_control && len > 1 && empty_space(&buffer[baddr], len)) {
2156 	int x, y;
2157 
2158 	x = ssCOL_TO_X(BA_TO_COL(baddr));
2159 	y = ssROW_TO_Y(BA_TO_ROW(baddr));
2160 
2161 	/* All empty, fill a rectangle */
2162 #if defined(_ST) /*[*/
2163 	printf("FillRectangle(baddr=%s, len=%d)\n", rcba(baddr), len);
2164 #endif /*]*/
2165 	XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)), x,
2166 		y - ss->ascent, (ss->char_width * len) + 1, ss->char_height);
2167     } else {
2168 	unsigned long attrs, attrs2;
2169 	bool has_gr, has_gr2;
2170 	bool empty, empty2;
2171 	struct sp ra;
2172 	int i;
2173 	int i0 = 0;
2174 
2175 	ra = buffer[baddr];
2176 
2177 	/* Note the characteristics of the beginning of the region. */
2178 	attrs = buffer[baddr].u.word & cmask;
2179 	has_gr = (buffer[baddr].u.word & gmask) != 0;
2180 	empty = !has_gr && bkm_isset(&buffer[baddr]);
2181 
2182 	for (i = 0; i < len; i++) {
2183 	    /* Note the characteristics of this character. */
2184 	    attrs2 = buffer[baddr+i].u.word & cmask;
2185 	    has_gr2 = (buffer[baddr+i].u.word & gmask) != 0;
2186 	    empty2 = !has_gr2 && bkm_isset(&buffer[baddr+i]);
2187 
2188 	    /* If this character has exactly the same attributes
2189 	       as the current region, simply add it, noting that
2190 	       the region might now not be empty. */
2191 	    if (attrs2 == attrs) {
2192 		if (!empty2) {
2193 		    empty = 0;
2194 		}
2195 		continue;
2196 	    }
2197 
2198 	    /* If this character is empty, and the current region
2199 	       has no GR attributes, pretend it matches. */
2200 	    if (empty2 && !has_gr) {
2201 		continue;
2202 	    }
2203 
2204 	    /* If the current region is empty, this character
2205 	       isn't empty, and this character has no GR
2206 	       attributes, change the current region's attributes
2207 	       to this character's attributes and add it. */
2208 	    if (empty && !empty2 && !has_gr2) {
2209 		attrs = attrs2;
2210 		has_gr = has_gr2;
2211 		empty = empty2;
2212 		ra = buffer[baddr+i];
2213 		continue;
2214 	    }
2215 
2216 	    /* Dump the region and start a new one with this character. */
2217 #if defined(_ST) /*[*/
2218 	    printf("%s:%d: rt%s\n", __FUNCTION__, __LINE__, rcba(baddr+i0));
2219 #endif /*]*/
2220 	    render_text(&buffer[baddr+i0], baddr+i0, i - i0, false, &ra);
2221 	    attrs = attrs2;
2222 	    has_gr = has_gr2;
2223 	    empty = empty2;
2224 	    i0 = i;
2225 	    ra = buffer[baddr+i];
2226 	}
2227 
2228 	/* Dump the remainder of the region. */
2229 #if defined(_ST) /*[*/
2230 	printf("%s:%d: rt%s\n", __FUNCTION__, __LINE__, rcba(baddr+i0));
2231 #endif /*]*/
2232 	render_text(&buffer[baddr+i0], baddr+i0, len - i0, false, &ra);
2233     }
2234 
2235     /* The X display is now correct; update ss->image[]. */
2236     memmove(&ss->image[baddr], &buffer[baddr], len*sizeof(struct sp));
2237 }
2238 
2239 /*
2240  * Get a font index for an EBCDIC character.
2241  * Returns a blank if there is no mapping.
2242  *
2243  * Note that the EBCDIC character can be 16 bits (DBCS), and the output might
2244  * be 16 bits as well.
2245  */
2246 static unsigned short
font_index(ebc_t ebc,int d8_ix,bool upper)2247 font_index(ebc_t ebc, int d8_ix, bool upper)
2248 {
2249     ucs4_t ucs4;
2250     int d;
2251 
2252     ucs4 = ebcdic_base_to_unicode(ebc, EUO_BLANK_UNDEF | EUO_UPRIV);
2253     if (upper) {
2254 	ucs4 = u_toupper(ucs4);
2255     }
2256     d = display8_lookup(d8_ix, ucs4);
2257     if (d < 0) {
2258 	d = display8_lookup(d8_ix, ' ');
2259     }
2260     return d;
2261 }
2262 
2263 /*
2264  * Attempt to map an APL character to a DEC line-drawing character in the
2265  * first 32 bytes of an old 8-bit X11 font.
2266  */
2267 static int
apl_to_linedraw(ebc_t c)2268 apl_to_linedraw(ebc_t c)
2269 {
2270     switch (c) {
2271     case 0xaf:	/* degree */
2272 	return 0x7;
2273     case 0xd4:	/* LR corner */
2274 	return 0xb;
2275     case 0xd5:	/* UR corner */
2276 	return 0xc;
2277     case 0xc5:	/* UL corner */
2278 	return 0xd;
2279     case 0xc4:	/* LL corner */
2280 	return 0xe;
2281     case 0xd3:	/* plus */
2282 	return 0xf;
2283     case 0xa2:	/* middle horizontal */
2284 	return 0x12;
2285     case 0xc6:	/* left tee */
2286 	return 0x15;
2287     case 0xd6:	/* right tee */
2288 	return 0x16;
2289     case 0xc7:	/* bottom tee */
2290 	return 0x17;
2291     case 0xd7:	/* top tee */
2292 	return 0x18;
2293     case 0xbf:	/* stile */
2294     case 0x85:	/* vertical line */
2295 	return 0x19;
2296     case 0x8c:	/* less or equal */
2297 	return 0x1a;
2298     case 0xae:	/* greater or equal */
2299 	return 0x1b;
2300     case 0xbe:	/* not equal */
2301 	return 0x1d;
2302     case 0xa3:	/* bullet */
2303 	return 0x1f;
2304     case 0xad:
2305 	return '[';
2306     case 0xbd:
2307 	return ']';
2308     default:
2309 	return -1;
2310     }
2311 }
2312 
2313 /* Map an APL character to the current display character set. */
2314 static XChar2b
apl_to_udisplay(int d8_ix,unsigned char c)2315 apl_to_udisplay(int d8_ix, unsigned char c)
2316 {
2317     XChar2b x;
2318     int u = -1;
2319     int d = 0;
2320 
2321     /* Look it up. */
2322     u = apl_to_unicode(c, EUO_NONE |
2323 	    xappres.apl_circled_alpha? EUO_APL_CIRCLED: 0);
2324     if (u != -1) {
2325 	d = display8_lookup(d8_ix, u);
2326     }
2327 
2328     /* Default to a space. */
2329     if (d == 0) {
2330 	d = display8_lookup(d8_ix, ' ');
2331     }
2332 
2333     /* Return it. */
2334     x.byte1 = (d >> 8) & 0xff;
2335     x.byte2 = d & 0xff;
2336     return x;
2337 }
2338 
2339 /* Map an APL character to the old first-32 8-bit X11 display character set. */
2340 static XChar2b
apl_to_ldisplay(unsigned char c)2341 apl_to_ldisplay(unsigned char c)
2342 {
2343     XChar2b x;
2344     int u = -1;
2345 
2346     /* Look it up, defaulting to a space. */
2347     u = apl_to_linedraw(c);
2348     if (u == -1) {
2349 	u = ' ';
2350     }
2351 
2352     /* Return it. */
2353     x.byte1 = 0;
2354     x.byte2 = u;
2355     return x;
2356 }
2357 
2358 /* Map a line-drawing character to the current display character set. */
2359 static XChar2b
linedraw_to_udisplay(int d8_ix,unsigned char c)2360 linedraw_to_udisplay(int d8_ix, unsigned char c)
2361 {
2362     XChar2b x;
2363     int d;
2364 
2365     /* Look it up. */
2366     d = display8_lookup(d8_ix, linedraw_to_unicode(c, false));
2367 
2368     /* Default to a space. */
2369     if (d == 0) {
2370 	d = display8_lookup(d8_ix, ' ');
2371     }
2372 
2373     /* Return it. */
2374     x.byte1 = (d >> 8) & 0xff;
2375     x.byte2 = d & 0xff;
2376     return x;
2377 }
2378 
2379 /*
2380  * Render text onto the X display.  The region must not span lines.
2381  */
2382 static void
render_text(struct sp * buffer,int baddr,int len,bool block_cursor,struct sp * attrs)2383 render_text(struct sp *buffer, int baddr, int len, bool block_cursor,
2384 	struct sp *attrs)
2385 {
2386     int color;
2387     int x, y;
2388     GC dgc = (GC)None;	/* drawing text */
2389     GC cleargc = (GC)None;	/* clearing under undersized characters */
2390     int sel = attrs->u.bits.sel;
2391     register int i, j;
2392     bool one_at_a_time = false;
2393     int d8_ix = ss->d8_ix;
2394     XTextItem16 text[64]; /* fixed size is a hack */
2395     int n_texts = -1;
2396     bool in_dbcs = false;
2397     int clear_len = 0;
2398     int n_sbcs = 0;
2399     int n_dbcs = 0;
2400 
2401 #if defined(_ST) /*[*/
2402     printf("render_text(baddr=%s, len=%d)\n", rcba(baddr), len);
2403 #endif /*]*/
2404 
2405     /*
2406      * If the region starts with the right-hand side of a DBCS, back off
2407      * one column.
2408      */
2409     switch (ctlr_dbcs_state(baddr)) {
2410     case DBCS_RIGHT:
2411 	/*
2412 	 * Lots of assumptions -- the buffer really does go back one byte,
2413 	 * and baddr is greater than zero.
2414 	 */
2415 #if defined(_ST) /*[*/
2416 	printf("render_text: backing off\n");
2417 #endif /*]*/
2418 	buffer--;
2419 	baddr--;
2420 	len++;
2421 	break;
2422     default:
2423 	break;
2424     }
2425 
2426     for (i = 0, j = 0; i < len; i++) {
2427 	if (buffer[i].u.bits.cs != CS_DBCS || !dbcs || iconic) {
2428 	    if (n_texts < 0 || in_dbcs) {
2429 		/* Switch from nothing or DBCS, to SBCS. */
2430 #if defined(_ST) /*[*/
2431 		fprintf(stderr, "SBCS starts at %s\n", rcba(baddr + i));
2432 #endif /*]*/
2433 		in_dbcs = false;
2434 		n_texts++;
2435 		text[n_texts].chars = &rt_buf[j];
2436 		text[n_texts].nchars = 0;
2437 		text[n_texts].delta = 0;
2438 		text[n_texts].font = ss->fid;
2439 		n_sbcs++;
2440 	    }
2441 	    /* In SBCS. */
2442 	    clear_len += ss->char_width;
2443 	} else {
2444 	    if (n_texts < 0 || !in_dbcs) {
2445 		/* Switch from nothing or SBCS, to DBCS. */
2446 #if defined(_ST) /*[*/
2447 		fprintf(stderr, "DBCS starts at %s\n", rcba(baddr + i));
2448 #endif /*]*/
2449 		in_dbcs = true;
2450 		n_texts++;
2451 		text[n_texts].chars = &rt_buf[j];
2452 		text[n_texts].nchars = 0;
2453 		text[n_texts].delta = 0;
2454 		text[n_texts].font = dbcs_font.font;
2455 		n_dbcs++;
2456 	    }
2457 	    /* In DBCS. */
2458 	    clear_len += 2 * ss->char_width;
2459 	}
2460 
2461 	switch (buffer[i].u.bits.cs) {
2462 	case CS_BASE:	/* latin-1 */
2463 	    if (buffer[i].ucs4) {
2464 		/*
2465 		 * NVT-mode text. With a Unicode font, we can just display it
2466 		 * as-is. Otherwise, we need to map to EBCDIC and display it
2467 		 * only if there is a mapping.
2468 		 */
2469 		if (ss->unicode_font) {
2470 		    ucs4_t u = buffer[i].ucs4;
2471 
2472 		    if (toggled(MONOCASE)) {
2473 			u = u_toupper(u);
2474 		    }
2475 		    rt_buf[j].byte1 = (u >> 8) & 0xff;
2476 		    rt_buf[j].byte2 = u & 0xff;
2477 		} else {
2478 		    /* Only draw if there is an EBCDIC mapping. */
2479 		    ebc_t e = unicode_to_ebcdic(buffer[i].ucs4);
2480 
2481 		    rt_buf[j].byte1 = 0;
2482 		    if (e != 0) {
2483 			rt_buf[j].byte2 = font_index(e, d8_ix,
2484 				toggled(MONOCASE));
2485 		    } else {
2486 			rt_buf[j].byte2 = font_index(EBC_space, d8_ix, false);
2487 		    }
2488 		}
2489 	    } else {
2490 		rt_buf[j].byte1 = 0;
2491 		if (toggled(MONOCASE)) {
2492 		    rt_buf[j].byte2 = font_index(buffer[i].u.bits.ec, d8_ix,
2493 			    true);
2494 		} else {
2495 		    if (visible_control) {
2496 			if (buffer[i].u.bits.ec == EBC_so) {
2497 			    rt_buf[j].byte1 = 0;
2498 			    rt_buf[j].byte2 = font_index(EBC_less, d8_ix,
2499 				    false);
2500 			} else if (buffer[i].u.bits.ec == EBC_si) {
2501 			    rt_buf[j].byte1 = 0;
2502 			    rt_buf[j].byte2 = font_index(EBC_greater, d8_ix,
2503 				    false);
2504 			} else {
2505 			    unsigned short c = font_index(buffer[i].u.bits.ec,
2506 				    d8_ix, false);
2507 
2508 			    rt_buf[j].byte1 = (c >> 8) & 0xff;
2509 			    rt_buf[j].byte2 = c & 0xff;
2510 			}
2511 		    } else {
2512 			unsigned short c = font_index(buffer[i].u.bits.ec,
2513 				d8_ix, false);
2514 
2515 			rt_buf[j].byte1 = (c >> 8) & 0xff;
2516 			rt_buf[j].byte2 = c & 0xff;
2517 		    }
2518 		}
2519 	    }
2520 	    j++;
2521 	    break;
2522 	case CS_APL:	/* GE (apl) */
2523 	case CS_BASE | CS_GE:
2524 	    if (ss->extended_3270font) {
2525 		rt_buf[j].byte1 = 1;
2526 		rt_buf[j].byte2 = ebc2cg0[buffer[i].u.bits.ec];
2527 	    } else {
2528 		if (ss->font_16bit) {
2529 		    rt_buf[j] = apl_to_udisplay(d8_ix, buffer[i].u.bits.ec);
2530 		} else {
2531 		    rt_buf[j] = apl_to_ldisplay(buffer[i].u.bits.ec);
2532 		}
2533 	    }
2534 	    j++;
2535 	    break;
2536 	case CS_LINEDRAW:	/* DEC line drawing */
2537 	    if (ss->standard_font) {
2538 		if (ss->font_16bit) {
2539 		    rt_buf[j] = linedraw_to_udisplay(d8_ix, buffer[i].ucs4);
2540 		} else {
2541 		    /* Assume the first 32 characters are line-drawing. */
2542 		    rt_buf[j].byte1 = 0;
2543 		    rt_buf[j].byte2 = buffer[i].u.bits.ec;
2544 		}
2545 	    } else {
2546 		if (ss->extended_3270font) {
2547 		    rt_buf[j].byte1 = 2;
2548 		    rt_buf[j].byte2 = buffer[i].ucs4;
2549 		} else {
2550 		    rt_buf[j].byte1 = 0;
2551 		    rt_buf[j].byte2 = 0;
2552 		}
2553 	    }
2554 	    j++;
2555 	    break;
2556 	case CS_DBCS:	/* DBCS */
2557 	    if (dbcs) {
2558 		if (buffer[i].ucs4 /* && dbcs_font.unicode*/) {
2559 		    xlate_dbcs_unicode(buffer[i].ucs4, &rt_buf[j]);
2560 		} else {
2561 		    xlate_dbcs(buffer[i].u.bits.ec, buffer[i+1].u.bits.ec,
2562 			    &rt_buf[j]);
2563 		}
2564 		/* Skip the next byte as well. */
2565 		i++;
2566 	    } else {
2567 		rt_buf[j].byte1 = 0;
2568 		rt_buf[j].byte2 = font_index(EBC_space, d8_ix, false);
2569 	    }
2570 	    j++;
2571 	    break;
2572 	}
2573 	text[n_texts].nchars++;
2574     }
2575     n_texts++;
2576 
2577     /* Check for one-at-a-time mode. */
2578     if (ss->funky_font) {
2579 	for (i = 0; i < len; i++) {
2580 	    if (!rt_buf[i].byte1 &&
2581 		(IS_ODD(ss->odd_width, rt_buf[i].byte2) ||
2582 		 IS_ODD(ss->odd_lbearing, rt_buf[i].byte2))) {
2583 		one_at_a_time = true;
2584 		break;
2585 	    }
2586 	}
2587     }
2588 
2589     x = ssCOL_TO_X(BA_TO_COL(baddr));
2590     y = ssROW_TO_Y(BA_TO_ROW(baddr));
2591     color = attrs->u.bits.fg;
2592 
2593     /* Select the GCs. */
2594     if (sel && !block_cursor) {
2595 	/* Selected, but not a block cursor. */
2596 	if (!appres.interactive.mono) {
2597 	    /* Color: Use the special select GCs. */
2598 	    dgc = get_selgc(ss, color);
2599 	    cleargc = ss->clrselgc;
2600 	} else {
2601 	    /* Mono: Invert the color. */
2602 	    dgc = get_gc(ss, INVERT_COLOR(color));
2603 	    cleargc = get_gc(ss, color);
2604 	}
2605     } else if (block_cursor && !(appres.interactive.mono && sel)) {
2606 	/* Block cursor, but neither mono nor selected. */
2607 	if (xappres.use_cursor_color) {
2608 	    /* Use the specific-color inverted GC. */
2609 	    dgc = ss->invucgc;
2610 	    cleargc = ss->ucgc;
2611 	} else {
2612 	    /* Just invert the specified color. */
2613 	    dgc = get_gc(ss, INVERT_COLOR(color));
2614 	    cleargc = get_gc(ss, color);
2615 	}
2616     } else {
2617 	/* Ordinary text, or a selected block cursor. */
2618 	dgc = get_gc(ss, color);
2619 	cleargc = get_gc(ss, INVERT_COLOR(color));
2620     }
2621 
2622     /* Draw the text */
2623     XFillRectangle(display, ss->window, cleargc, x, y - ss->ascent, clear_len,
2624 	    ss->char_height);
2625 #if defined(_ST) /*[*/
2626     {
2627 	int k, l;
2628 
2629 	for (k = 0; k < n_texts; k++) {
2630 	    printf("text[%d]: %d chars, %s:", k, text[k].nchars,
2631 		    (text[k].font == dbcs_font.font)? "dbcs": "sbcs");
2632 	    for (l = 0; l < text[k].nchars; l++) {
2633 		printf(" %02x%02x", text[k].chars[l].byte1,
2634 			text[k].chars[l].byte2);
2635 	    }
2636 	    printf("\n");
2637 	}
2638     }
2639 #endif /*]*/
2640     if (one_at_a_time || (n_sbcs && ss->xtra_width) ||
2641 	    (n_dbcs && dbcs_font.xtra_width)) {
2642 	int i, j;
2643 	int xn = x;
2644 	XTextItem16 text1;
2645 
2646 	/* XXX: do overstrike */
2647 	for (i = 0; i < n_texts; i++) {
2648 	    if (one_at_a_time || text[i].font == ss->fid) {
2649 		if (one_at_a_time || ss->xtra_width) {
2650 		    for (j = 0; j < text[i].nchars; j++) {
2651 			text1.chars = &text[i].chars[j];
2652 			text1.nchars = 1;
2653 			text1.delta = 0;
2654 			text1.font = ss->fid;
2655 			XDrawText16(display, ss->window, dgc, xn, y, &text1,
2656 				1);
2657 			xn += ss->char_width;
2658 		    }
2659 		} else {
2660 		    XDrawText16(display, ss->window, dgc, xn, y, &text[i], 1);
2661 			xn += ss->char_width * text[i].nchars;
2662 		}
2663 	    } else {
2664 		if (dbcs_font.xtra_width) {
2665 		    for (j = 0; j < text[i].nchars; j++) {
2666 			text1.chars = &text[i].chars[j];
2667 			text1.nchars = 1;
2668 			text1.delta = 0;
2669 			text1.font = dbcs_font.font;
2670 			XDrawText16(display, ss->window, dgc, xn, y, &text1,
2671 				1);
2672 			xn += dbcs_font.char_width;
2673 		    }
2674 		} else {
2675 		    XDrawText16(display, ss->window, dgc, xn, y, &text[i], 1);
2676 		    xn += dbcs_font.char_width * text[i].nchars;
2677 		}
2678 	    }
2679 	}
2680     } else {
2681 	XDrawText16(display, ss->window, dgc, x, y, text, n_texts);
2682 	if (ss->overstrike && ((attrs->u.bits.gr & GR_INTENSIFY) ||
2683 		    ((appres.interactive.mono ||
2684 		      (!mode.m3279 && highlight_bold)) &&
2685 		     ((color & BASE_MASK) == FA_INT_HIGH_SEL)))) {
2686 	    XDrawText16(display, ss->window, dgc, x+1, y, text, n_texts);
2687 	}
2688     }
2689 
2690     if (attrs->u.bits.gr & GR_UNDERLINE) {
2691 	XDrawLine(display, ss->window, dgc, x,
2692 		y - ss->ascent + ss->char_height - 1, x + clear_len,
2693 		y - ss->ascent + ss->char_height - 1);
2694     }
2695 }
2696 
2697 bool
screen_obscured(void)2698 screen_obscured(void)
2699 {
2700     return ss->obscured;
2701 }
2702 
2703 /*
2704  * Scroll the screen image one row.
2705  *
2706  * This is the optimized path from ctlr_scroll(); it assumes that ea_buf[] has
2707  * already been modified and that the screen can be brought into sync by
2708  * hammering ss->image and the bitmap.
2709  */
2710 void
screen_scroll(unsigned char fg,unsigned char bg)2711 screen_scroll(unsigned char fg, unsigned char bg)
2712 {
2713     bool was_on;
2714     bool xwo;
2715 
2716     if (!ss->exposed_yet) {
2717 	return;
2718     }
2719 
2720     was_on = cursor_off("scroll", true, &xwo);
2721     memmove(&ss->image[0], &ss->image[COLS],
2722 		       (ROWS - 1) * COLS * sizeof(struct sp));
2723     memmove(&temp_image[0], &temp_image[COLS],
2724 		       (ROWS - 1) * COLS * sizeof(struct sp));
2725     memset((char *)&ss->image[(ROWS - 1) * COLS], 0,
2726 		  COLS * sizeof(struct sp));
2727     memset((char *)&temp_image[(ROWS - 1) * COLS], 0,
2728 		  COLS * sizeof(struct sp));
2729     XCopyArea(display, ss->window, ss->window, get_gc(ss, 0),
2730 	ssCOL_TO_X(0),
2731 	ssROW_TO_Y(1) - ss->ascent,
2732 	ss->char_width * COLS,
2733 	ss->char_height * (ROWS - 1),
2734 	ssCOL_TO_X(0),
2735 	ssROW_TO_Y(0) - ss->ascent);
2736     ss->copied = true;
2737     XFillRectangle(display, ss->window, get_gc(ss, INVERT_COLOR(0)),
2738 	ssCOL_TO_X(0),
2739 	ssROW_TO_Y(ROWS - 1) - ss->ascent,
2740 	(ss->char_width * COLS) + 1,
2741 	ss->char_height);
2742     if (was_on) {
2743 	cursor_on("scroll");
2744     }
2745     if (xwo) {
2746 	redraw_lower_crosshair();
2747     }
2748 }
2749 
2750 /*
2751  * Toggle mono-/dual-case mode.
2752  */
2753 static void
toggle_monocase(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)2754 toggle_monocase(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
2755 {
2756     memset((char *)ss->image, 0, (ROWS*COLS) * sizeof(struct sp));
2757     ctlr_changed(0, ROWS*COLS);
2758 }
2759 
2760 /**
2761  * Toggle timing display.
2762  */
2763 static void
toggle_showTiming(toggle_index_t ix _is_unused,enum toggle_type tt _is_unused)2764 toggle_showTiming(toggle_index_t ix _is_unused, enum toggle_type tt _is_unused)
2765 {
2766     if (!toggled(SHOW_TIMING)) {
2767 	status_untiming();
2768     }
2769 }
2770 
2771 /*
2772  * Toggle screen flip
2773  */
2774 void
screen_flip(void)2775 screen_flip(void)
2776 {
2777     /* Flip mode is broken in the DBCS version. */
2778     if (!dbcs) {
2779 	flipped = !flipped;
2780 
2781 	xaction_internal(PA_Expose_xaction, IA_REDRAW, NULL, NULL);
2782     }
2783 }
2784 
2785 bool
screen_flipped(void)2786 screen_flipped(void)
2787 {
2788     return flipped;
2789 }
2790 
2791 /*
2792  * Return a visible control character for a field attribute.
2793  */
2794 static unsigned char
visible_ebcdic(unsigned char fa)2795 visible_ebcdic(unsigned char fa)
2796 {
2797     static unsigned char varr[32] = {
2798 	EBC_0, EBC_1, EBC_2, EBC_3, EBC_4, EBC_5, EBC_6, EBC_7,
2799 	EBC_8, EBC_9, EBC_A, EBC_B, EBC_C, EBC_D, EBC_E, EBC_F,
2800 	EBC_G, EBC_H, EBC_I, EBC_J, EBC_K, EBC_L, EBC_M, EBC_N,
2801 	EBC_O, EBC_P, EBC_Q, EBC_R, EBC_S, EBC_T, EBC_U, EBC_V
2802     };
2803 
2804     unsigned ix;
2805 
2806     /*
2807      * This code knows that:
2808      *  FA_PROTECT is   0b100000, and we map it to 0b010000
2809      *  FA_NUMERIC is   0b010000, and we map it to 0b001000
2810      *  FA_INTENSITY is 0b001100, and we map it to 0b000110
2811      *  FA_MODIFY is    0b000001, and we copy to   0b000001
2812      */
2813     ix = ((fa & (FA_PROTECT | FA_NUMERIC | FA_INTENSITY)) >> 1) |
2814 	(fa & FA_MODIFY);
2815     return varr[ix];
2816 }
2817 
2818 /*
2819  * Map a row and column to a crosshair character.
2820  */
2821 static unsigned char
map_crosshair(int baddr)2822 map_crosshair(int baddr)
2823 {
2824     if (baddr == cursor_addr) {
2825 	/* Cross. */
2826 	return 0xd3;
2827     } else if (baddr / cCOLS == cursor_addr / cCOLS) {
2828 	/* Horizontal. */
2829 	return 0xa2;
2830     } else {
2831 	/* Vertical. */
2832 	return 0xbf;
2833     }
2834 }
2835 
2836 /*
2837  * "Draw" ea_buf into a buffer
2838  */
2839 static void
draw_fields(struct sp * buffer,int first,int last)2840 draw_fields(struct sp *buffer, int first, int last)
2841 {
2842     int	baddr = 0;
2843     int	faddr;
2844     unsigned char	fa;
2845     struct ea       *field_ea;
2846     struct ea	*sbp = ea_buf;
2847     int	field_color;
2848     int	zero;
2849     bool	any_blink = false;
2850     int	crossable = CROSSABLE;
2851     enum dbcs_state d;
2852     int	cursor_col = 0, cursor_row = 0;
2853 
2854     /* Set up cursor_col/cursor_row. */
2855     if (crossable) {
2856 	cursor_col = BA_TO_COL(cursor_addr);
2857 	cursor_row = BA_TO_ROW(cursor_addr);
2858     }
2859 
2860     /* If there is any blinking text, override the suggested boundaries. */
2861     if (text_blinkers_exist) {
2862 	first = -1;
2863 	last = -1;
2864     }
2865 
2866     /* Adjust pointers to start of region. */
2867     if (first > 0) {
2868 	baddr += first;
2869 	sbp += first;
2870 	buffer += first;
2871     }
2872     faddr = find_field_attribute(baddr);
2873     fa = ea_buf[faddr].fa;
2874     field_ea = fa2ea(faddr);
2875 
2876     /* Adjust end of region. */
2877     if (last == -1 || last >= ROWS*COLS) {
2878 	last = 0;
2879     }
2880 
2881     zero = FA_IS_ZERO(fa);
2882     if (field_ea->fg && (!appres.modified_sel || !FA_IS_MODIFIED(fa))) {
2883 	field_color = field_ea->fg & COLOR_MASK;
2884     } else {
2885 	field_color = fa_color(fa);
2886     }
2887 
2888     do {
2889 	unsigned char	c = sbp->ec;
2890 	ucs4_t		u = sbp->ucs4;
2891 	struct sp	b;
2892 	bool		reverse = false;
2893 	bool		is_selected = false;
2894 
2895 	b.u.word = 0;	/* clear out all fields */
2896 	b.ucs4 = 0;
2897 
2898 	if (ea_buf[baddr].fa) {
2899 	    fa = ea_buf[baddr].fa;
2900 	    field_ea = sbp;
2901 	    zero = FA_IS_ZERO(fa);
2902 	    if (field_ea->fg && (!appres.modified_sel || !FA_IS_MODIFIED(fa))) {
2903 		field_color = field_ea->fg & COLOR_MASK;
2904 	    } else {
2905 		field_color = fa_color(fa);
2906 	    }
2907 	    if (visible_control) {
2908 		b.u.bits.ec = visible_ebcdic(fa);
2909 		b.u.bits.gr = GR_UNDERLINE;
2910 		b.u.bits.fg = mode.m3279? (GC_NONDEFAULT | HOST_COLOR_YELLOW):
2911 		    FA_INT_HIGH_SEL;
2912 	    } else if (crossable && CROSSED(baddr)) {
2913 		b.u.bits.cs = CS_APL;
2914 		b.u.bits.ec = map_crosshair(baddr);
2915 		b.u.bits.fg = CROSS_COLOR;
2916 		b.u.bits.gr = 0;
2917 	    }
2918 	} else {
2919 	    unsigned short gr;
2920 	    int e_color;
2921 	    bool is_vc = false;
2922 
2923 	    /* Find the right graphic rendition. */
2924 	    if (zero) {
2925 		gr = 0;
2926 	    } else {
2927 		gr = sbp->gr;
2928 		if (!gr) {
2929 		    gr = field_ea->gr;
2930 		}
2931 		if (gr & GR_BLINK) {
2932 		    any_blink = true;
2933 		}
2934 		if (highlight_bold && FA_IS_HIGH(fa)) {
2935 		    gr |= GR_INTENSIFY;
2936 		}
2937 	    }
2938 
2939 	    /* Find the right color. */
2940 	    if (zero) {
2941 		e_color = fa_color(FA_INT_HIGH_SEL);
2942 	    } else {
2943 		if (sbp->fg) {
2944 		    e_color = sbp->fg & COLOR_MASK;
2945 		} else if (appres.interactive.mono && (gr & GR_INTENSIFY)) {
2946 		    e_color = fa_color(FA_INT_HIGH_SEL);
2947 		} else {
2948 		    e_color = field_color;
2949 		}
2950 		if (gr & GR_REVERSE) {
2951 		    e_color = INVERT_COLOR(e_color);
2952 		    reverse = true;
2953 		}
2954 	    }
2955 	    if (!appres.interactive.mono) {
2956 		b.u.bits.fg = e_color;
2957 	    }
2958 
2959 	    /* Find the right character and character set. */
2960 	    d = ctlr_dbcs_state(baddr);
2961 	    if (zero) {
2962 		if (visible_control) {
2963 		    b.u.bits.ec = EBC_space;
2964 		} else if (crossable && CROSSED(baddr)) {
2965 		    b.u.bits.cs = CS_APL;
2966 		    b.u.bits.ec = map_crosshair(baddr);
2967 		    b.u.bits.fg = CROSS_COLOR;
2968 		    b.u.bits.gr = 0;
2969 		}
2970 	    } else if (((!visible_control || (u || c != EBC_null)) &&
2971 			((!u && c != EBC_space) || (u && u != ' ') || d != DBCS_NONE)) ||
2972 		       (gr & (GR_REVERSE | GR_UNDERLINE)) ||
2973 		       visible_control) {
2974 
2975 		b.u.bits.fg = e_color;
2976 
2977 		/*
2978 		 * Replace blanked-out blinking text with
2979 		 * spaces.
2980 		 */
2981 		if (!text_blinking_on && (gr & GR_BLINK)) {
2982 		    if (!crossable || !CROSSED(baddr)) {
2983 			b.u.bits.ec = EBC_space;
2984 		    } else {
2985 			b.u.bits.cs = CS_APL;
2986 			b.u.bits.ec = map_crosshair(baddr);
2987 			b.u.bits.fg = CROSS_COLOR;
2988 			b.u.bits.gr = 0;
2989 		    }
2990 		} else {
2991 		    if (visible_control && !u && c == EBC_null) {
2992 			b.u.bits.ec = EBC_period;
2993 			is_vc = true;
2994 		    } else if (visible_control &&
2995 			(c == EBC_so || c == EBC_si)) {
2996 			b.u.bits.ec = (c == EBC_so)? EBC_less: EBC_greater;
2997 			is_vc = true;
2998 		    } else {
2999 			b.u.bits.ec = c;
3000 			b.ucs4 = u;
3001 		    }
3002 		    if (sbp->cs) {
3003 			b.u.bits.cs = sbp->cs;
3004 		    } else {
3005 			b.u.bits.cs = field_ea->cs;
3006 		    }
3007 		    if (b.u.bits.cs & CS_GE) {
3008 			b.u.bits.cs = CS_APL;
3009 		    } else if ((b.u.bits.cs & CS_MASK) != CS_DBCS ||
3010 			     d != DBCS_NONE) {
3011 			b.u.bits.cs &= CS_MASK;
3012 		    } else {
3013 			b.u.bits.cs = CS_BASE;
3014 		    }
3015 		}
3016 
3017 	    } /* otherwise, EBC_null */
3018 
3019 	    if (visible_control) {
3020 		if (is_vc) {
3021 		    b.u.bits.gr = GR_UNDERLINE;
3022 		}
3023 	    } else {
3024 		b.u.bits.gr = gr & (GR_UNDERLINE | GR_INTENSIFY);
3025 	    }
3026 
3027 	    /* Check for SI/SO. */
3028 	    if (d == DBCS_LEFT || d == DBCS_RIGHT) {
3029 		b.u.bits.cs = CS_DBCS;
3030 	    }
3031 
3032 	    /* Check for blanks. */
3033 	    if (crossable && CROSSED(baddr) &&
3034 		    b.u.bits.cs == CS_BASE && bkm_isset(&b)) {
3035 		b.u.bits.cs = CS_APL;
3036 		b.u.bits.ec = map_crosshair(baddr);
3037 		b.u.bits.fg = CROSS_COLOR;
3038 		b.u.bits.gr = 0;
3039 	    }
3040 	}
3041 
3042 	/*
3043 	 * Compute selection state.
3044 	 *
3045 	 * DBCS characters always act as a unit, with the state
3046 	 * determined by the selection status and crosshair
3047 	 * intersection of either half.
3048 	 * - If either half is selected, both are considered selected.
3049 	 * - If either half lies in the crosshair, neither is
3050 	 *   considered selected.
3051 	 */
3052 
3053 	is_selected = (SELECTED(baddr) != 0);
3054 	switch (ctlr_dbcs_state(baddr)) {
3055 	case DBCS_NONE:
3056 	case DBCS_DEAD:
3057 	case DBCS_LEFT_WRAP:
3058 	case DBCS_RIGHT_WRAP:
3059 	    break;
3060 	case DBCS_LEFT:
3061 	case DBCS_SI:
3062 	    if ((baddr % COLS) != (COLS - 1) && SELECTED(baddr + 1)) {
3063 		is_selected = true;
3064 	    }
3065 	    break;
3066 	case DBCS_RIGHT:
3067 	case DBCS_SB: /* XXX */
3068 	    if ((baddr % COLS) && SELECTED(baddr - 1)) {
3069 		is_selected = true;
3070 	    }
3071 	    break;
3072 	}
3073 
3074 	if (crossable && !reverse) {
3075 	    switch (ctlr_dbcs_state(baddr)) {
3076 	    case DBCS_NONE:
3077 	    case DBCS_DEAD:
3078 	    case DBCS_LEFT_WRAP:
3079 	    case DBCS_RIGHT_WRAP:
3080 		break;
3081 	    case DBCS_LEFT:
3082 	    case DBCS_SI:
3083 		break;
3084 	    case DBCS_RIGHT:
3085 	    case DBCS_SB: /* XXX */
3086 		break;
3087 	    }
3088 	}
3089 
3090 	/*
3091 	 * XOR the crosshair cursor with selections.
3092 	 */
3093 	if (is_selected) {
3094 	    b.u.bits.sel = 1;
3095 	}
3096 
3097 	if (!flipped) {
3098 	    *buffer++ = b;
3099 	} else {
3100 	    *(buffer + fl_baddr(baddr)) = b;
3101 	}
3102 	sbp++;
3103 	INC_BA(baddr);
3104     } while (baddr != last);
3105 
3106     /* Cancel blink timeouts if none were seen this pass. */
3107     if (!any_blink) {
3108 	text_blinkers_exist = false;
3109     }
3110 }
3111 
3112 
3113 /*
3114  * Resync the X display with the contents of 'buffer'
3115  */
3116 static void
resync_display(struct sp * buffer,int first,int last)3117 resync_display(struct sp *buffer, int first, int last)
3118 {
3119     int		i, j;
3120     int		b = 0;
3121     int		i0 = -1;
3122     bool	ccheck;
3123     int		fca = fl_baddr(cursor_addr);
3124     int		first_row, last_row;
3125 #   define SPREAD	10
3126 
3127     if (first < 0) {
3128 	first_row = 0;
3129 	last_row = ROWS;
3130     } else {
3131 	first_row = first / COLS;
3132 	b = first_row * COLS;
3133 	last_row = (last + (COLS-1)) / COLS;
3134     }
3135 
3136     for (i = first_row; i < last_row; b += COLS, i++) {
3137 	int d0 = -1;
3138 	int s0 = -1;
3139 
3140 	/* Has the line changed? */
3141 	if (!memcmp((char *) &ss->image[b], (char *) &buffer[b],
3142 	    COLS*sizeof(struct sp))) {
3143 	    if (i0 >= 0) {
3144 		render_blanks(i0 * COLS, i - i0, buffer);
3145 		i0 = -1;
3146 	    }
3147 	    continue;
3148 	}
3149 
3150 	/* Is the new value empty? */
3151 	if (!visible_control && !(fca >= b && fca < (b+COLS)) &&
3152 		empty_space(&buffer[b], COLS)) {
3153 	    if (i0 < 0) {
3154 		i0 = i;
3155 	    }
3156 	    continue;
3157 	}
3158 
3159 	/* Yes, it changed, and it isn't blank.
3160 	   Dump any pending blank lines. */
3161 	if (i0 >= 0) {
3162 	    render_blanks(i0 * COLS, i - i0, buffer);
3163 	    i0 = -1;
3164 	}
3165 
3166 	/* New text.  Scan it. */
3167 	ccheck = cursor_displayed && fca >= b && fca < (b+COLS);
3168 	for (j = 0; j < COLS; j++) {
3169 	    if (ccheck && b+j == fca) {
3170 		/* Don't repaint over the cursor. */
3171 
3172 		/* Dump any pending "different" characters. */
3173 		if (d0 >= 0) {
3174 		    resync_text(b+d0, j-d0, buffer);
3175 		}
3176 
3177 		/* Start over. */
3178 		d0 = -1;
3179 		s0 = -1;
3180 		continue;
3181 	    }
3182 	    if (ss->image[b+j].u.word == buffer[b+j].u.word
3183 		    && ss->image[b+j].ucs4 == buffer[b+j].ucs4) {
3184 
3185 		/* Character is the same. */
3186 
3187 		if (d0 >= 0) {
3188 		    /* Something is pending... */
3189 		    if (s0 < 0) {
3190 			/* Start of "same" area */
3191 			s0 = j;
3192 		    } else {
3193 			/* nth matching character */
3194 			if (j - s0 > SPREAD) {
3195 			    /* too many */
3196 			    resync_text(b+d0, s0-d0, buffer);
3197 			    d0 = -1;
3198 			    s0 = -1;
3199 			}
3200 		    }
3201 		}
3202 	    } else {
3203 
3204 		/* Character is different. */
3205 
3206 		/* Forget intermediate matches. */
3207 		s0 = -1;
3208 
3209 		if (d0 < 0) {
3210 		    /* Mark the start. */
3211 		    d0 = j;
3212 		}
3213 	    }
3214 	}
3215 
3216 	/* Dump any pending "different" characters. */
3217 	if (d0 >= 0) {
3218 	    resync_text(b+d0, COLS-d0, buffer);
3219 	}
3220     }
3221     if (i0 >= 0) {
3222 	render_blanks(i0 * COLS, last_row - i0, buffer);
3223     }
3224 }
3225 
3226 /*
3227  * Support code for cursor redraw.
3228  */
3229 
3230 /*
3231  * Calculate a flipped baddr.
3232  */
3233 static int
fl_baddr(int baddr)3234 fl_baddr(int baddr)
3235 {
3236     if (!flipped) {
3237 	return baddr;
3238     }
3239     return ((baddr / COLS) * COLS) + (COLS - (baddr % COLS) - 1);
3240 }
3241 
3242 /*
3243  * Return the proper foreground color for a character position.
3244  */
3245 
3246 static int
char_color(int baddr)3247 char_color(int baddr)
3248 {
3249     int faddr;
3250     unsigned char fa;
3251     int color;
3252 
3253     faddr = find_field_attribute(baddr);
3254     fa = ea_buf[faddr].fa;
3255 
3256     /*
3257      * For non-display fields, we ignore gr and fg.
3258      */
3259     if (FA_IS_ZERO(fa)) {
3260 	color = fa_color(fa);
3261 	if (appres.interactive.mono && SELECTED(baddr)) {
3262 	    color = INVERT_COLOR(color);
3263 	}
3264 	return color;
3265     }
3266 
3267     /*
3268      * Find the color of the character or the field.
3269      */
3270     if (ea_buf[baddr].fg) {
3271 	color = ea_buf[baddr].fg & COLOR_MASK;
3272     } else if (fa2ea(faddr)->fg && (!appres.modified_sel ||
3273 				  !FA_IS_MODIFIED(fa))) {
3274 	color = fa2ea(faddr)->fg & COLOR_MASK;
3275     } else {
3276 	color = fa_color(fa);
3277     }
3278 
3279     /*
3280      * Now apply reverse video.
3281      *
3282      * One bit of strangeness:
3283      *  If the buffer is a field attribute and we aren't using the
3284      *  debug font, it's displayed as a blank; don't invert.
3285      */
3286     if (!((ea_buf[baddr].fa && !visible_control)) &&
3287 	((ea_buf[baddr].gr & GR_REVERSE) ||
3288 	 (fa2ea(faddr)->gr & GR_REVERSE))) {
3289 	color = INVERT_COLOR(color);
3290     }
3291 
3292     /*
3293      * In monochrome, apply selection status as well.
3294      */
3295     if (appres.interactive.mono && SELECTED(baddr)) {
3296 	color = INVERT_COLOR(color);
3297     }
3298 
3299     return color;
3300 }
3301 
3302 
3303 /*
3304  * Select a GC for drawing a hollow or underscore cursor.
3305  */
3306 static GC
cursor_gc(int baddr)3307 cursor_gc(int baddr)
3308 {
3309     /*
3310      * If they say use a particular color, use it.
3311      */
3312     if (xappres.use_cursor_color) {
3313 	return ss->ucgc;
3314     } else {
3315 	return get_gc(ss, char_color(baddr));
3316     }
3317 }
3318 
3319 /*
3320  * Redraw one character.
3321  * If 'invert' is true, invert the foreground and background colors.
3322  */
3323 static void
redraw_char(int baddr,bool invert)3324 redraw_char(int baddr, bool invert)
3325 {
3326     enum dbcs_state d;
3327     struct sp buffer[2];
3328     int faddr;
3329     unsigned char fa;
3330     int gr;
3331     int blank_it = 0;
3332     int baddr2;
3333     int len = 1;
3334     int cursor_col = BA_TO_COL(cursor_addr);
3335     int cursor_row = BA_TO_ROW(cursor_addr);
3336 
3337     /*
3338      * Figure out the DBCS state of this position.  If it's the right-hand
3339      * side of a DBCS character, repaint the left side instead.
3340      */
3341     switch ((d = ctlr_dbcs_state(baddr))) {
3342     case DBCS_LEFT:
3343     case DBCS_SI:
3344 	len = 2;
3345 	break;
3346     case DBCS_RIGHT:
3347 	len = 2;
3348 	DEC_BA(baddr);
3349 	break;
3350     default:
3351 	break;
3352     }
3353 
3354     if (!invert) {
3355 	int flb = fl_baddr(baddr);
3356 
3357 	/*
3358 	 * Put back what belongs there.
3359 	 * Note that the cursor may have been covering a DBCS character
3360 	 * that is no longer DBCS, so if we're not at the right margin,
3361 	 * we should redraw two positions.
3362 	 */
3363 #if defined(_ST) /*[*/
3364 	printf("%s:%d: rt%s\n", __FUNCTION__, __LINE__, rcba(flb));
3365 #endif /*]*/
3366 	if (dbcs && ((baddr % COLS) != (COLS - 1)) && len == 1) {
3367 	    len = 2;
3368 	}
3369 	render_text(&ss->image[flb], flb, len, false, &ss->image[flb]);
3370 	return;
3371     }
3372 
3373     baddr2 = baddr;
3374     INC_BA(baddr2);
3375 
3376     /*
3377      * Fabricate the right thing.
3378      * ss->image isn't going to help, because it may contain shortcuts
3379      *  for faster display, so we have to construct a buffer to use.
3380      */
3381     buffer[0].u.word = 0L;
3382     buffer[0].ucs4 = 0L;
3383     buffer[0].u.bits.ec = ea_buf[baddr].ec;
3384     buffer[0].u.bits.cs = ea_buf[baddr].cs;
3385     if (buffer[0].u.bits.cs & CS_GE) {
3386 	buffer[0].u.bits.cs = CS_APL;
3387     } else {
3388 	buffer[0].u.bits.cs &= CS_MASK;
3389     }
3390     buffer[0].ucs4 = ea_buf[baddr].ucs4;
3391 
3392     faddr = find_field_attribute(baddr);
3393     if (d == DBCS_LEFT || d == DBCS_RIGHT) {
3394 	buffer[0].u.bits.cs = CS_DBCS;
3395     }
3396     fa = ea_buf[faddr].fa;
3397     if (FA_IS_ZERO(fa)) {
3398 	gr = 0;
3399     } else {
3400 	gr = ea_buf[baddr].gr;
3401 	if (!gr) {
3402 	    gr = fa2ea(faddr)->gr;
3403 	}
3404     }
3405     if (ea_buf[baddr].fa) {
3406 	if (!visible_control) {
3407 	    blank_it = 1;
3408 	}
3409     } else if (FA_IS_ZERO(fa)) {
3410 	blank_it = 1;
3411     } else if (text_blinkers_exist && !text_blinking_on) {
3412 	if (gr & GR_BLINK) {
3413 	    blank_it = 1;
3414 	}
3415     }
3416     if (buffer[0].u.bits.cs == CS_BASE && bkm_isset(&buffer[0])) {
3417 	blank_it = true;
3418     }
3419     if (blank_it) {
3420 	if (CROSSABLE && CROSSED(baddr)) {
3421 	    buffer[0].u.bits.cs = CS_APL;
3422 	    buffer[0].u.bits.ec = map_crosshair(baddr);
3423 	    buffer[0].u.bits.fg = CROSS_COLOR;
3424 	    buffer[0].u.bits.gr = 0;
3425 	} else {
3426 	    buffer[0].u.bits.ec = EBC_space;
3427 	    buffer[0].u.bits.cs = 0;
3428 	}
3429     }
3430     buffer[0].u.bits.fg = char_color(baddr);
3431     buffer[0].u.bits.gr |= (gr & GR_INTENSIFY);
3432     if (len == 2) {
3433 	buffer[1].u.word = buffer[0].u.word;
3434 	if (!blank_it) {
3435 	    buffer[1].u.bits.ec = ea_buf[baddr2].ec;
3436 	    buffer[1].ucs4 = ea_buf[baddr2].ucs4;
3437 	}
3438     }
3439     render_text(buffer, fl_baddr(baddr), len, true, buffer);
3440 }
3441 
3442 /*
3443  * Draw a hollow cursor.
3444  */
3445 static void
hollow_cursor(int baddr)3446 hollow_cursor(int baddr)
3447 {
3448     Dimension cwidth;
3449     enum dbcs_state d;
3450 
3451     d = ctlr_dbcs_state(baddr);
3452 
3453     switch (d) {
3454     case DBCS_RIGHT:
3455 	DEC_BA(baddr);
3456 	/* fall through... */
3457     case DBCS_LEFT:
3458     case DBCS_SI:
3459 	cwidth = (2 * ss->char_width) - 1;
3460 	break;
3461     default:
3462 	cwidth = ss->char_width - 1;
3463 	break;
3464     }
3465 
3466     XDrawRectangle(display,
3467 	    ss->window,
3468 	    cursor_gc(baddr),
3469 	    ssCOL_TO_X(BA_TO_COL(fl_baddr(baddr))),
3470 	    ssROW_TO_Y(BA_TO_ROW(baddr)) - ss->ascent +
3471 		(appres.interactive.mono ? 1 : 0),
3472 	    cwidth,
3473 	    ss->char_height - (appres.interactive.mono ? 2 : 1));
3474 }
3475 
3476 /*
3477  * Draw an underscore cursor.
3478  */
3479 static void
underscore_cursor(int baddr)3480 underscore_cursor(int baddr)
3481 {
3482     Dimension cwidth;
3483     enum dbcs_state d;
3484 
3485     d = ctlr_dbcs_state(baddr);
3486 
3487     switch (d) {
3488     case DBCS_RIGHT:
3489 	DEC_BA(baddr);
3490 	/* fall through... */
3491     case DBCS_LEFT:
3492     case DBCS_SI:
3493 	cwidth = (2 * ss->char_width) - 1;
3494 	break;
3495     default:
3496 	cwidth = ss->char_width - 1;
3497 	break;
3498     }
3499 
3500     XDrawRectangle(display,
3501 	    ss->window,
3502 	    cursor_gc(baddr),
3503 	    ssCOL_TO_X(BA_TO_COL(fl_baddr(baddr))),
3504 	    ssROW_TO_Y(BA_TO_ROW(baddr)) - ss->ascent +
3505 		ss->char_height - 2,
3506 	    cwidth,
3507 	    1);
3508 }
3509 
3510 /*
3511  * Invert a square over a character.
3512  */
3513 static void
small_inv_cursor(int baddr)3514 small_inv_cursor(int baddr)
3515 {
3516     /* XXX: DBCS? */
3517 
3518     XFillRectangle(display,
3519 	    ss->window,
3520 	    ss->mcgc,
3521 	    ssCOL_TO_X(BA_TO_COL(fl_baddr(baddr))),
3522 	    ssROW_TO_Y(BA_TO_ROW(baddr)) - ss->ascent + 1,
3523 	    ss->char_width,
3524 	    (ss->char_height > 2) ? (ss->char_height - 2) : 1);
3525 }
3526 
3527 /*
3528  * Draw or remove the cursor.
3529  */
3530 static void
put_cursor(int baddr,bool on)3531 put_cursor(int baddr, bool on)
3532 {
3533     /*
3534      * If the cursor is being turned off, simply redraw the text under it.
3535      */
3536     if (!on) {
3537 	redraw_char(baddr, false);
3538 	return;
3539     }
3540 
3541     /*
3542      * If underscore cursor, redraw the character and draw the underscore.
3543      */
3544     if (toggled(ALT_CURSOR)) {
3545 	redraw_char(baddr, false);
3546 	underscore_cursor(baddr);
3547 	return;
3548     }
3549 
3550     /*
3551      * On, and not an underscore.
3552      *
3553      * If out of focus, either draw an empty box in its place (if block
3554      * cursor) or redraw the underscore (if underscore).
3555      */
3556     if (!in_focus) {
3557 	hollow_cursor(baddr);
3558 	return;
3559     }
3560 
3561     /*
3562      * If monochrome, invert a small square over the characters.
3563      */
3564     if (appres.interactive.mono) {
3565 	small_inv_cursor(baddr);
3566 	return;
3567     }
3568 
3569     /*
3570      * Color: redraw the character in reverse video.
3571      */
3572     redraw_char(baddr, true);
3573 }
3574 
3575 /* Allocate a named color. */
3576 static bool
alloc_color(char * name,enum fallback_color fb_color,Pixel * pixel)3577 alloc_color(char *name, enum fallback_color fb_color, Pixel *pixel)
3578 {
3579     XColor cell, db;
3580     Screen *s;
3581 
3582     s = XtScreen(toplevel);
3583 
3584     if (name[0] == '#') {
3585 	unsigned long rgb;
3586 	char *endptr;
3587 
3588 	rgb = strtoul(name + 1, &endptr, 16);
3589 	if (endptr != name + 1 && !*endptr && !(rgb & ~0xffffff)) {
3590 	    memset(&db, '\0', sizeof(db));
3591 	    db.red = (rgb >> 16) & 0xff;
3592 	    db.red |= (db.red << 8);
3593 	    db.green = (rgb >> 8) & 0xff;
3594 	    db.green |= (db.green << 8);
3595 	    db.blue = rgb & 0xff;
3596 	    db.blue |= (db.blue << 8);
3597 	    if (XAllocColor(display, XDefaultColormapOfScreen(s), &db) != 0) {
3598 		*pixel = db.pixel;
3599 		return true;
3600 	    }
3601 	}
3602     } else {
3603 	if (XAllocNamedColor(display, XDefaultColormapOfScreen(s), name, &cell,
3604 		    &db) != 0) {
3605 	    *pixel = db.pixel;
3606 	    return true;
3607 	}
3608     }
3609     switch (fb_color) {
3610     case FB_WHITE:
3611 	*pixel = XWhitePixelOfScreen(s);
3612 	break;
3613     case FB_BLACK:
3614 	*pixel = XBlackPixelOfScreen(s);
3615 	break;
3616     }
3617     return false;
3618 }
3619 
3620 /* Spell out a fallback color. */
3621 static const char *
fb_name(enum fallback_color fb_color)3622 fb_name(enum fallback_color fb_color)
3623 {
3624     switch (fb_color) {
3625 	case FB_WHITE:
3626 	    return "white";
3627 	case FB_BLACK:
3628 	    return "black";
3629     }
3630     return "chartreuse";	/* to keep Gcc -Wall happy */
3631 }
3632 
3633 /* Allocate color pixels. */
3634 static void
allocate_pixels(void)3635 allocate_pixels(void)
3636 {
3637     if (appres.interactive.mono) {
3638 	return;
3639     }
3640 
3641     /* Allocate constant elements. */
3642     if (!alloc_color(xappres.colorbg_name, FB_BLACK, &colorbg_pixel)) {
3643 	popup_an_error("Cannot allocate colormap \"%s\" for screen "
3644 		"background, using \"black\"", xappres.colorbg_name);
3645     }
3646     if (!alloc_color(xappres.selbg_name, FB_BLACK, &selbg_pixel)) {
3647 	popup_an_error("Cannot allocate colormap \"%s\" for select "
3648 		"background, using \"black\"", xappres.selbg_name);
3649     }
3650     if (!alloc_color(xappres.keypadbg_name, FB_WHITE, &keypadbg_pixel)) {
3651 	popup_an_error("Cannot allocate colormap \"%s\" for keypad "
3652 		"background, using \"white\"", xappres.keypadbg_name);
3653     }
3654     if (xappres.use_cursor_color &&
3655 	    !alloc_color(xappres.cursor_color_name, FB_WHITE,
3656 		&cursor_pixel)) {
3657 	popup_an_error("Cannot allocate colormap \"%s\" for cursor color, "
3658 		"using \"white\"", xappres.cursor_color_name);
3659     }
3660 
3661     /* Allocate pseudocolors. */
3662     if (!mode.m3279) {
3663 	if (!alloc_color(xappres.normal_name, FB_WHITE, &normal_pixel)) {
3664 	    popup_an_error("Cannot allocate colormap \"%s\" for text, "
3665 		    "using \"white\"", xappres.normal_name);
3666 	}
3667 	if (!alloc_color(xappres.select_name, FB_WHITE, &select_pixel)) {
3668 	    popup_an_error("Cannot allocate colormap \"%s\" for selectable "
3669 		    "text, using \"white\"", xappres.select_name);
3670 	}
3671 	if (!alloc_color(xappres.bold_name, FB_WHITE, &bold_pixel)) {
3672 	    popup_an_error("Cannot allocate colormap \"%s\" for bold text, "
3673 		    "using \"white\"", xappres.bold_name);
3674 	}
3675     }
3676 }
3677 
3678 /* Deallocate pixels. */
3679 static void
destroy_pixels(void)3680 destroy_pixels(void)
3681 {
3682     int i;
3683 
3684     /*
3685      * It would make sense to deallocate many of the pixels here, but
3686      * the only available call (XFreeColors) would deallocate cells
3687      * that may be in use by other Xt widgets.  Occh.
3688      */
3689 
3690     for (i = 0; i < 16; i++) {
3691 	cpx_done[i] = false;
3692     }
3693 }
3694 
3695 /*
3696  * Create graphics contexts.
3697  */
3698 static void
make_gcs(struct sstate * s)3699 make_gcs(struct sstate *s)
3700 {
3701     XGCValues xgcv;
3702 
3703     if (mode.m3279) {
3704 	int i;
3705 
3706 	for (i = 0; i < NGCS; i++) {
3707 	    if (s->gc[i] != (GC)None) {
3708 		XtReleaseGC(toplevel, s->gc[i]);
3709 		s->gc[i] = (GC)None;
3710 	    }
3711 	    if (s->gc[i + NGCS] != (GC)None) {
3712 		XtReleaseGC(toplevel, s->gc[i + NGCS]);
3713 		s->gc[i + NGCS] = (GC)None;
3714 	    }
3715 	    if (s->selgc[i] != (GC)None) {
3716 		XtReleaseGC(toplevel, s->selgc[i]);
3717 		s->selgc[i] = (GC)None;
3718 	    }
3719 	}
3720     } else {
3721 	if (!appres.interactive.mono) {
3722 	    make_gc_set(s, FA_INT_NORM_NSEL, normal_pixel, colorbg_pixel);
3723 	    make_gc_set(s, FA_INT_NORM_SEL,  select_pixel, colorbg_pixel);
3724 	    make_gc_set(s, FA_INT_HIGH_SEL,  bold_pixel, colorbg_pixel);
3725 	} else {
3726 	    make_gc_set(s, FA_INT_NORM_NSEL, xappres.foreground,
3727 		    xappres.background);
3728 	    make_gc_set(s, FA_INT_NORM_SEL,  xappres.foreground,
3729 		    xappres.background);
3730 	    make_gc_set(s, FA_INT_HIGH_SEL,  xappres.foreground,
3731 		    xappres.background);
3732 	}
3733     }
3734     if (s->clrselgc != (GC)None) {
3735 	XtReleaseGC(toplevel, s->clrselgc);
3736 	s->clrselgc = (GC)None;
3737     }
3738     xgcv.foreground = selbg_pixel;
3739     s->clrselgc = XtGetGC(toplevel, GCForeground, &xgcv);
3740 
3741     /* Create monochrome block cursor GC. */
3742     if (appres.interactive.mono && s->mcgc == (GC)None) {
3743 	if (screen_depth > 1) {
3744 	    xgcv.function = GXinvert;
3745 	} else {
3746 	    xgcv.function = GXxor;
3747 	}
3748 	xgcv.foreground = 1L;
3749 	s->mcgc = XtGetGC(toplevel, GCForeground|GCFunction, &xgcv);
3750     }
3751 
3752     /* Create explicit cursor color cursor GCs. */
3753     if (xappres.use_cursor_color) {
3754 	if (s->ucgc != (GC)None) {
3755 	    XtReleaseGC(toplevel, s->ucgc);
3756 	    s->ucgc = (GC)None;
3757 	}
3758 	xgcv.foreground = cursor_pixel;
3759 	s->ucgc = XtGetGC(toplevel, GCForeground, &xgcv);
3760 
3761 	if (s->invucgc != (GC)None) {
3762 	    XtReleaseGC(toplevel, s->invucgc);
3763 	    s->invucgc = (GC)None;
3764 	}
3765 	xgcv.foreground = colorbg_pixel;
3766 	xgcv.background = cursor_pixel;
3767 	xgcv.font = s->fid;
3768 	s->invucgc = XtGetGC(toplevel, GCForeground|GCBackground|GCFont,
3769 		&xgcv);
3770     }
3771 
3772     /* Set the flag for overstriking bold. */
3773     s->overstrike = (s->char_width > 1);
3774 }
3775 
3776 /* Set up a default color scheme. */
3777 static void
default_color_scheme(void)3778 default_color_scheme(void)
3779 {
3780     static int default_attrib_colors[4] = {
3781 	GC_NONDEFAULT | HOST_COLOR_GREEN,	/* default */
3782 	GC_NONDEFAULT | HOST_COLOR_RED,		/* intensified */
3783 	GC_NONDEFAULT | HOST_COLOR_BLUE,	/* protected */
3784 	GC_NONDEFAULT | HOST_COLOR_NEUTRAL_WHITE /* protected, intensified */
3785     };
3786     int i;
3787 
3788     ibm_fb = FB_WHITE;
3789     for (i = 0; i < 16; i++) {
3790 	XtFree(color_name[i]);
3791 	color_name[i] = XtNewString("white");
3792     }
3793     for (i = 0; i < 4; i++) {
3794 	field_colors[i] = default_attrib_colors[i];
3795     }
3796 }
3797 
3798 /* Transfer the colorScheme resource into arrays. */
3799 static bool
xfer_color_scheme(char * cs,bool do_popup)3800 xfer_color_scheme(char *cs, bool do_popup)
3801 {
3802     int i;
3803     char *scheme_name = NULL;
3804     char *s0 = NULL, *scheme = NULL;
3805     char *tk;
3806 
3807     char *tmp_color_name[16];
3808     enum fallback_color tmp_ibm_fb = FB_WHITE;
3809     char *tmp_colorbg_name = NULL;
3810     char *tmp_selbg_name = NULL;
3811     int tmp_field_colors[4];
3812 
3813     if (cs == NULL) {
3814 	goto failure;
3815     }
3816     scheme_name = xs_buffer("%s.%s", ResColorScheme, cs);
3817     s0 = get_resource(scheme_name);
3818     if (s0 == NULL) {
3819 	if (do_popup) {
3820 	    popup_an_error("Can't find resource %s", scheme_name);
3821 	} else {
3822 	    xs_warning("Can't find resource %s", scheme_name);
3823 	}
3824 	goto failure;
3825     }
3826     scheme = s0 = XtNewString(s0);
3827     for (i = 0; (tk = strtok(scheme, " \t\n")) != NULL; i++) {
3828 	scheme = NULL;
3829 	if (i > 22) {
3830 	    popup_an_error("Ignoring excess data in %s resource", scheme_name);
3831 	    break;
3832 	}
3833 	switch (i) {
3834 	case  0: case  1: case  2: case  3:
3835 	case  4: case  5: case  6: case  7:
3836 	case  8: case  9: case 10: case 11:
3837 	case 12: case 13: case 14: case 15:	/* IBM color name */
3838 	    tmp_color_name[i] = tk;
3839 	    break;
3840 	case 16:	/* default for IBM colors */
3841 	    if (!strcmp(tk, "white")) {
3842 		tmp_ibm_fb = FB_WHITE;
3843 	    } else if (!strcmp(tk, "black")) {
3844 		tmp_ibm_fb = FB_BLACK;
3845 	    } else {
3846 		if (do_popup) {
3847 		    popup_an_error("Invalid default color");
3848 		} else {
3849 		    xs_warning("Invalid default color");
3850 		}
3851 		goto failure;
3852 	    }
3853 	    break;
3854 	case 17:	/* screen background */
3855 	    tmp_colorbg_name = tk;
3856 	    break;
3857 	case 18:	/* select background */
3858 	    tmp_selbg_name = tk;
3859 	    break;
3860 	case 19: case 20: case 21: case 22:	/* attribute colors */
3861 	    tmp_field_colors[i-19] = atoi(tk);
3862 	    if (tmp_field_colors[i-19] < 0 ||
3863 		tmp_field_colors[i-19] > 0x0f) {
3864 		if (do_popup) {
3865 		    popup_an_error("Invalid %s resource, ignoring",
3866 			    scheme_name);
3867 		} else {
3868 		    xs_warning("Invalid %s resource, ignoring", scheme_name);
3869 		}
3870 		goto failure;
3871 	    }
3872 	    tmp_field_colors[i-19] |= GC_NONDEFAULT;
3873 	}
3874     }
3875     if (i < 23) {
3876 	if (do_popup) {
3877 	    popup_an_error("Insufficient data in %s resource",
3878 		scheme_name);
3879 	} else {
3880 	    xs_warning("Insufficient data in %s resource",
3881 		scheme_name);
3882 	}
3883 	goto failure;
3884     }
3885 
3886     /* Success: transfer to live variables. */
3887     for (i = 0; i < 16; i++) {
3888 	XtFree(color_name[i]);
3889 	color_name[i] = XtNewString(tmp_color_name[i]);
3890     }
3891     ibm_fb = tmp_ibm_fb;
3892     xappres.colorbg_name = XtNewString(tmp_colorbg_name);
3893     xappres.selbg_name = XtNewString(tmp_selbg_name);
3894     for (i = 0; i < 4; i++) {
3895 	field_colors[i] = tmp_field_colors[i];
3896     }
3897 
3898     /* Clean up and exit. */
3899     XtFree(scheme_name);
3900     XtFree(s0);
3901     return true;
3902 
3903 failure:
3904     XtFree(scheme_name);
3905     XtFree(s0);
3906     return false;
3907 }
3908 
3909 /* Look up a GC, allocating it if necessary. */
3910 static GC
get_gc(struct sstate * s,int color)3911 get_gc(struct sstate *s, int color)
3912 {
3913     int pixel_index;
3914     XGCValues xgcv;
3915     GC r;
3916     static bool in_gc_error = false;
3917 
3918     if (color & GC_NONDEFAULT) {
3919 	color &= ~GC_NONDEFAULT;
3920     } else {
3921 	color = (color & INVERT_MASK) | DEFAULT_PIXEL;
3922     }
3923 
3924     if ((r = s->gc[color]) != (GC)None) {
3925 	return r;
3926     }
3927 
3928     /* Allocate the pixel. */
3929     pixel_index = PIXEL_INDEX(color);
3930     if (!cpx_done[pixel_index]) {
3931 	if (!alloc_color(color_name[pixel_index], ibm_fb, &cpx[pixel_index])) {
3932 	    if (!in_gc_error) {
3933 		in_gc_error = true;
3934 		popup_an_error("Cannot allocate colormap \"%s\" for 3279 "
3935 			"color %d (%s), using \"%s\"",
3936 			color_name[pixel_index],
3937 			pixel_index,
3938 			see_color((unsigned char)(pixel_index + 0xf0)),
3939 			fb_name(ibm_fb));
3940 		in_gc_error = false;
3941 	    }
3942 	}
3943 	cpx_done[pixel_index] = true;
3944     }
3945 
3946     /* Allocate the GC. */
3947     xgcv.font = s->fid;
3948     if (!(color & INVERT_MASK)) {
3949 	xgcv.foreground = cpx[pixel_index];
3950 	xgcv.background = colorbg_pixel;
3951     } else {
3952 	xgcv.foreground = colorbg_pixel;
3953 	xgcv.background = cpx[pixel_index];
3954     }
3955     if (s == &nss && pixel_index == DEFAULT_PIXEL) {
3956 	xgcv.graphics_exposures = true;
3957 	r = XtGetGC(toplevel,
3958 		GCForeground|GCBackground|GCFont|GCGraphicsExposures,
3959 		&xgcv);
3960     } else {
3961 	r = XtGetGC(toplevel,
3962 		GCForeground|GCBackground|GCFont,
3963 		&xgcv);
3964     }
3965     return s->gc[color] = r;
3966 }
3967 
3968 /* Look up a selection GC, allocating it if necessary. */
3969 static GC
get_selgc(struct sstate * s,int color)3970 get_selgc(struct sstate *s, int color)
3971 {
3972     XGCValues xgcv;
3973     GC r;
3974 
3975     if (color & GC_NONDEFAULT) {
3976 	color = PIXEL_INDEX(color);
3977     } else {
3978 	color = DEFAULT_PIXEL;
3979     }
3980 
3981     if ((r = s->selgc[color]) != (GC)None) {
3982 	return r;
3983     }
3984 
3985     /* Allocate the pixel. */
3986     if (!cpx_done[color]) {
3987 	if (!alloc_color(color_name[color], FB_WHITE, &cpx[color])) {
3988 	    popup_an_error("Cannot allocate colormap \"%s\" for 3279 color "
3989 		    "%d (%s), using \"white\"",
3990 		    color_name[color], color,
3991 		    see_color((unsigned char)(color + 0xf0)));
3992 	    }
3993 	cpx_done[color] = true;
3994     }
3995 
3996     /* Allocate the GC. */
3997     xgcv.font = s->fid;
3998     xgcv.foreground = cpx[color];
3999     xgcv.background = selbg_pixel;
4000     return s->selgc[color] =
4001 	XtGetGC(toplevel, GCForeground|GCBackground|GCFont, &xgcv);
4002 }
4003 
4004 /* External entry points for GC allocation. */
4005 
4006 GC
screen_gc(int color)4007 screen_gc(int color)
4008 {
4009     return get_gc(ss, color | GC_NONDEFAULT);
4010 }
4011 
4012 GC
screen_invgc(int color)4013 screen_invgc(int color)
4014 {
4015     return get_gc(ss, INVERT_COLOR(color | GC_NONDEFAULT));
4016 }
4017 
4018 /*
4019  * Preallocate a set of graphics contexts for a given color.
4020  *
4021  * This logic is used only in pseudo-color mode.  In full color mode,
4022  * GCs are allocated dynamically by get_gc().
4023  */
4024 static void
make_gc_set(struct sstate * s,int i,Pixel fg,Pixel bg)4025 make_gc_set(struct sstate *s, int i, Pixel fg, Pixel bg)
4026 {
4027     XGCValues xgcv;
4028 
4029     if (s->gc[i] != (GC)None) {
4030 	XtReleaseGC(toplevel, s->gc[i]);
4031     }
4032     xgcv.foreground = fg;
4033     xgcv.background = bg;
4034     xgcv.graphics_exposures = true;
4035     xgcv.font = s->fid;
4036     if (s == &nss && !i) {
4037 	s->gc[i] = XtGetGC(toplevel,
4038 		GCForeground|GCBackground|GCFont|GCGraphicsExposures,
4039 		&xgcv);
4040     } else {
4041 	s->gc[i] = XtGetGC(toplevel, GCForeground|GCBackground|GCFont, &xgcv);
4042     }
4043     if (s->gc[NGCS + i] != (GC)None) {
4044 	XtReleaseGC(toplevel, s->gc[NGCS + i]);
4045     }
4046     xgcv.foreground = bg;
4047     xgcv.background = fg;
4048     s->gc[NGCS + i] = XtGetGC(toplevel, GCForeground|GCBackground|GCFont,
4049 	    &xgcv);
4050     if (!appres.interactive.mono) {
4051 	if (s->selgc[i] != (GC)None) {
4052 	    XtReleaseGC(toplevel, s->selgc[i]);
4053 	}
4054 	xgcv.foreground = fg;
4055 	xgcv.background = selbg_pixel;
4056 	s->selgc[i] = XtGetGC(toplevel, GCForeground|GCBackground|GCFont,
4057 		&xgcv);
4058     }
4059 }
4060 
4061 /*
4062  * Convert an attribute to a color index.
4063  */
4064 static int
fa_color(unsigned char fa)4065 fa_color(unsigned char fa)
4066 {
4067 #   define DEFCOLOR_MAP(f) \
4068 		((((f) & FA_PROTECT) >> 4) | (((f) & FA_INT_HIGH_SEL) >> 3))
4069 
4070     if (mode.m3279) {
4071 	/*
4072 	 * Color indices are the low-order 4 bits of a 3279 color
4073 	 * identifier (0 through 15)
4074 	 */
4075 	if (appres.modified_sel && FA_IS_MODIFIED(fa)) {
4076 	    return GC_NONDEFAULT | (xappres.modified_sel_color & 0xf);
4077 	} else if (xappres.visual_select &&
4078 		 FA_IS_SELECTABLE(fa) &&
4079 		 !FA_IS_INTENSE(fa)) {
4080 	    return GC_NONDEFAULT | (xappres.visual_select_color & 0xf);
4081 	} else {
4082 	    return field_colors[DEFCOLOR_MAP(fa)];
4083 	}
4084     } else {
4085 	/*
4086 	 * Color indices are the intensity bits (0 through 2)
4087 	 */
4088 	if (FA_IS_ZERO(fa) || (appres.modified_sel && FA_IS_MODIFIED(fa))) {
4089 	    return GC_NONDEFAULT | FA_INT_NORM_SEL;
4090 	} else {
4091 	    return GC_NONDEFAULT | (fa & 0x0c);
4092 	}
4093     }
4094 }
4095 
4096 
4097 /*
4098  * Event handlers for toplevel FocusIn, FocusOut, KeymapNotify and
4099  * PropertyChanged events.
4100  */
4101 
4102 static bool toplevel_focused = false;
4103 static bool keypad_entered = false;
4104 
4105 void
PA_Focus_xaction(Widget w _is_unused,XEvent * event,String * params _is_unused,Cardinal * num_params _is_unused)4106 PA_Focus_xaction(Widget w _is_unused, XEvent *event, String *params _is_unused,
4107 	Cardinal *num_params _is_unused)
4108 {
4109     XFocusChangeEvent *fe = (XFocusChangeEvent *)event;
4110 
4111 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
4112     xaction_debug(PA_Focus_xaction, event, params, num_params);
4113 #endif /*]*/
4114     switch (fe->type) {
4115     case FocusIn:
4116 	if (fe->detail != NotifyPointer) {
4117 	    toplevel_focused = true;
4118 	    screen_focus(true);
4119 	}
4120 	break;
4121     case FocusOut:
4122 	toplevel_focused = false;
4123 	if (!toplevel_focused && !keypad_entered) {
4124 	    screen_focus(false);
4125 	}
4126 	break;
4127     }
4128 }
4129 
4130 void
PA_EnterLeave_xaction(Widget w _is_unused,XEvent * event _is_unused,String * params _is_unused,Cardinal * num_params _is_unused)4131 PA_EnterLeave_xaction(Widget w _is_unused, XEvent *event _is_unused,
4132 	String *params _is_unused, Cardinal *num_params _is_unused)
4133 {
4134     XCrossingEvent *ce = (XCrossingEvent *)event;
4135 
4136 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
4137     xaction_debug(PA_EnterLeave_xaction, event, params, num_params);
4138 #endif /*]*/
4139     switch (ce->type) {
4140     case EnterNotify:
4141 	keypad_entered = true;
4142 	screen_focus(true);
4143 	break;
4144     case LeaveNotify:
4145 	keypad_entered = false;
4146 	if (!toplevel_focused && !keypad_entered) {
4147 	    screen_focus(false);
4148 	}
4149 	break;
4150     }
4151 }
4152 
4153 void
PA_KeymapNotify_xaction(Widget w _is_unused,XEvent * event,String * params _is_unused,Cardinal * num_params _is_unused)4154 PA_KeymapNotify_xaction(Widget w _is_unused, XEvent *event,
4155 	String *params _is_unused, Cardinal *num_params _is_unused)
4156 {
4157     XKeymapEvent *k = (XKeymapEvent *)event;
4158 
4159 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
4160     xaction_debug(PA_KeymapNotify_xaction, event, params, num_params);
4161 #endif /*]*/
4162     shift_event(state_from_keymap(k->key_vector));
4163 }
4164 
4165 static void
query_window_state(void)4166 query_window_state(void)
4167 {
4168     Atom actual_type;
4169     int actual_format;
4170     unsigned long nitems;
4171     unsigned long leftover;
4172     unsigned char *data = NULL;
4173     static bool was_up = false;
4174     bool maximized_horz = false;
4175     bool maximized_vert = false;
4176     bool was_iconic = iconic;
4177     bool was_maximized = maximized;
4178 
4179     /* Get WM_STATE to see if we're iconified. */
4180     if (XGetWindowProperty(display, XtWindow(toplevel), a_state, 0L,
4181 		(long)BUFSIZ, false, AnyPropertyType, &actual_type,
4182 		&actual_format, &nitems, &leftover, &data) == Success) {
4183 	if (actual_type == a_state && actual_format == 32) {
4184 	    if (*(unsigned long *)data == IconicState) {
4185 		iconic = true;
4186 		keypad_popdown(&was_up);
4187 	    } else {
4188 		iconic = false;
4189 		invert_icon(false);
4190 		keypad_first_up();
4191 		if (was_up) {
4192 		    keypad_popup();
4193 		    was_up = false;
4194 		}
4195 	    }
4196 	}
4197 	XFree(data);
4198     }
4199     if (iconic != was_iconic)
4200     {
4201 	vtrace("%s\n", iconic? "Iconified": "Not iconified");
4202     }
4203 
4204     /* Get _NET_WM_STATE to see if we're maximized. */
4205     data = NULL;
4206     if (XGetWindowProperty(display, XtWindow(toplevel), a_net_wm_state, 0L,
4207 		(long)BUFSIZ, false, AnyPropertyType, &actual_type,
4208 		&actual_format, &nitems, &leftover, &data) == Success) {
4209 	if (actual_type == a_atom && actual_format == 32) {
4210 	    unsigned long item;
4211 	    Atom *prop = (Atom *)data;
4212 	    for (item = 0; item < nitems; item++)
4213 	    {
4214 		if (prop[item] == a_net_wm_state_maximized_horz) {
4215 		    maximized_horz = true;
4216 		}
4217 		if (prop[item] == a_net_wm_state_maximized_vert) {
4218 		    maximized_vert = true;
4219 		}
4220 	    }
4221 	}
4222 	XFree(data);
4223 
4224 	maximized = (maximized_horz && maximized_vert);
4225     }
4226     if (maximized != was_maximized)
4227     {
4228 	vtrace("%s\n", maximized? "Maximized": "Not maximized");
4229 	menubar_snap_enable(!maximized);
4230 
4231 	/*
4232 	 * If the integral keypad is on when we are maximized, then it is okay
4233 	 * to toggle it on and off. Otherwise, no.
4234 	 */
4235 	menubar_keypad_sensitive(!maximized ||
4236 		kp_placement != kp_integral ||
4237 		xappres.keypad_on);
4238     }
4239 }
4240 
4241 void
PA_StateChanged_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)4242 PA_StateChanged_xaction(Widget w _is_unused, XEvent *event, String *params,
4243 	Cardinal *num_params)
4244 {
4245 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
4246     xaction_debug(PA_StateChanged_xaction, event, params, num_params);
4247 #endif /*]*/
4248     query_window_state();
4249 }
4250 
4251 /*
4252  * Handle Shift events (KeyPress and KeyRelease events, or KeymapNotify events
4253  * that occur when the mouse enters the window).
4254  */
4255 
4256 void
shift_event(int event_state)4257 shift_event(int event_state)
4258 {
4259     static int old_state;
4260     bool shifted_now =
4261 	(event_state & (ShiftKeyDown | MetaKeyDown | AltKeyDown)) != 0;
4262 
4263     if (event_state != old_state) {
4264 	old_state = event_state;
4265 	status_shift_mode(event_state);
4266 	if (shifted != shifted_now) {
4267 	    shifted = shifted_now;
4268 	    keypad_shift();
4269 	}
4270     }
4271 }
4272 
4273 /*
4274  * Handle the mouse entering and leaving the window.
4275  */
4276 static void
screen_focus(bool in)4277 screen_focus(bool in)
4278 {
4279     /*
4280      * Update the input context focus.
4281      */
4282     if (ic != NULL) {
4283 	if (in) {
4284 	    XSetICFocus(ic);
4285 	} else {
4286 	    XUnsetICFocus(ic);
4287 	}
4288     }
4289 
4290     /*
4291      * Cancel any pending cursor blink.  If we just came into focus and
4292      * have a blinking cursor, we will start a fresh blink cycle below, so
4293      * the filled-in cursor is visible for a full turn.
4294      */
4295     cancel_blink();
4296 
4297     /*
4298      * If the cursor is disabled, simply change internal state.
4299      */
4300     if (!CONNECTED) {
4301 	in_focus = in;
4302 	return;
4303     }
4304 
4305     /*
4306      * Change the appearance of the cursor.  Make it hollow out or fill in
4307      * instantly, even if it was blinked off originally.
4308      */
4309     cursor_off("focus", true, NULL);
4310     in_focus = in;
4311     cursor_on("focus");
4312 
4313     /*
4314      * Slight kludge: If the crosshair cursor is enabled, redraw the whole
4315      * screen, to draw or erase it.
4316      */
4317     if (toggled(CROSSHAIR)) {
4318 	screen_changed = true;
4319 	first_changed = 0;
4320 	last_changed = ROWS*COLS;
4321 	screen_disp(false);
4322     }
4323 
4324     /*
4325      * If we just came into focus and we're supposed to have a blinking
4326      * cursor, schedule a blink.
4327      */
4328     if (in_focus && toggled(CURSOR_BLINK)) {
4329 	schedule_cursor_blink();
4330     }
4331 }
4332 
4333 /*
4334  * Change fonts.
4335  */
4336 static bool
SetFont_action(ia_t ia,unsigned argc,const char ** argv)4337 SetFont_action(ia_t ia, unsigned argc, const char **argv)
4338 {
4339     action_debug(AnSetFont, ia, argc, argv);
4340     if (check_argc(AnSetFont, argc, 1, 1) < 0) {
4341 	return false;
4342     }
4343 
4344     screen_newfont(argv[0], true, false);
4345     return true;
4346 }
4347 
4348 /*
4349  * Split an emulatorFontList resource entry, which looks like:
4350  *  [menu-name:] [#noauto] [#resize] font-name
4351  * Modifies the input string.
4352  */
4353 static void
split_font_list_entry(char * entry,char ** menu_name,bool * noauto,bool * resize,char ** font_name)4354 split_font_list_entry(char *entry, char **menu_name, bool *noauto,
4355 	bool *resize, char **font_name)
4356 {
4357     char *colon;
4358     char *s;
4359     bool any = false;
4360 
4361     if (menu_name != NULL) {
4362 	*menu_name = NULL;
4363     }
4364     if (noauto != NULL) {
4365 	*noauto = false;
4366     }
4367     if (resize != NULL) {
4368 	*resize = false;
4369     }
4370 
4371     colon = strchr(entry, ':');
4372     if (colon != NULL) {
4373 	if (menu_name != NULL) {
4374 	    *menu_name = entry;
4375 	}
4376 	*colon = '\0';
4377 	s = colon + 1;
4378     } else {
4379 	s = entry;
4380     }
4381 
4382     do {
4383 	any = false;
4384 	while (isspace((unsigned char)*s)) {
4385 	    s++;
4386 	}
4387 	if (!strncmp(s, "#noauto", 7) && (!s[7] || isspace((unsigned char)s[7]))) {
4388 	    if (noauto != NULL) {
4389 		*noauto = true;
4390 	    }
4391 	    s += 7;
4392 	    any = true;
4393 	} else if (!strncmp(s, "#resize", 7) && (!s[7] || isspace((unsigned char)s[7]))) {
4394 	    if (resize != NULL) {
4395 		*resize = true;
4396 	    }
4397 	    s += 7;
4398 	    any = true;
4399 	}
4400     } while (any);
4401 
4402     *font_name = s;
4403 }
4404 
4405 /*
4406  * Load a font with a display character set required by a charset.
4407  * Returns true for success, false for failure.
4408  * If it succeeds, the caller is responsible for calling:
4409  *	screen_reinit(FONT_CHANGE)
4410  */
4411 bool
screen_new_display_charsets(const char * realname,const char * csnames)4412 screen_new_display_charsets(const char *realname, const char *csnames)
4413 {
4414     char *rl;
4415     char *s0, *s;
4416     char *fontname = NULL;
4417     char *lff;
4418     bool font_found = false;
4419     const char *display_charsets;
4420     const char *dbcs_display_charsets;
4421 
4422     /* Handle the default. */
4423     if (realname == NULL) {
4424 	/* Handle the default. */
4425 	display_charsets = default_display_charset;
4426     } else {
4427 	/* Look up the display character set(s). */
4428 	display_charsets = lookup_display_charset(realname);
4429 	assert(display_charsets != NULL);
4430 	dbcs_display_charsets = lookup_display_charset_dbcs(realname);
4431 	if (dbcs_display_charsets != NULL) {
4432 	    display_charsets = lazyaf("%s+%s", display_charsets,
4433 		    dbcs_display_charsets);
4434 	}
4435     }
4436 
4437     /*
4438      * If the emulator fonts already implement those charsets, we're done.
4439      */
4440     if (efont_charset != NULL && !strcmp(display_charsets, efont_charset)) {
4441 	goto done;
4442     }
4443 
4444     /*
4445      * If the user chose an emulator font, but we haven't tried it yet,
4446      * see if it implements the right charset.
4447      */
4448     if (efontname == NULL && xappres.efontname != NULL) {
4449 	lff = load_fixed_font(xappres.efontname, display_charsets);
4450 	if (lff != NULL) {
4451 	    if (strcmp(xappres.efontname, "3270")) {
4452 		popup_an_error("%s", lff);
4453 	    }
4454 	    Free(lff);
4455 	} else {
4456 	    fontname = xappres.efontname;
4457 	}
4458     }
4459 
4460     /*
4461      * Otherwise, try to get a font from the resize lists.
4462      */
4463     if (fontname == NULL) {
4464 	rl = get_fresource("%s.%s", ResEmulatorFontList, display_charsets);
4465 	if (rl != NULL) {
4466 	    s0 = s = NewString(rl);
4467 	    while (!font_found && split_lresource(&s, &fontname) == 1) {
4468 		bool noauto = false;
4469 		char *fn = NULL;
4470 
4471 		split_font_list_entry(fontname, NULL, &noauto, NULL, &fn);
4472 		if (noauto || !*fn) {
4473 		    continue;
4474 		}
4475 
4476 		lff = load_fixed_font(fn, display_charsets);
4477 		if (lff != NULL) {
4478 		    Free(lff);
4479 		} else {
4480 		    font_found = true;
4481 		}
4482 	    }
4483 	    Free(s0);
4484 	}
4485 
4486 	if (!font_found &&
4487 	    (!strcasecmp(display_charsets, default_display_charset) ||
4488 	     !strcasecmp(display_charsets, "iso8859-1"))) {
4489 	    /* Try "fixed". */
4490 	    if ((lff = load_fixed_font("!fixed", display_charsets)) == NULL) {
4491 		font_found = true;
4492 	    } else {
4493 		/* Fatal. */
4494 		xs_error("%s", lff);
4495 		Free(lff);
4496 		/*NOTREACHED*/
4497 		return false;
4498 	    }
4499 	}
4500 
4501 	if (!font_found) {
4502 	    char *cs_dup;
4503 	    char *cs;
4504 	    char *buf;
4505 	    char *lasts = NULL;
4506 
4507 	    if (strchr(display_charsets, '+') != NULL) {
4508 		/*
4509 		 * Despite what the code below appears to be
4510 		 * able to do, we don't know how to search for a
4511 		 * DBCS font.  Bail here.
4512 		 */
4513 		return false;
4514 	    }
4515 
4516 	    buf = cs_dup = NewString(display_charsets);
4517 	    while (!font_found &&
4518 		   (cs = strtok_r(buf, ",", &lasts)) != NULL) {
4519 		char *part1 = NULL, *part2 = NULL;
4520 		int n_parts = 1;
4521 
4522 		buf = NULL;
4523 		n_parts = split_dbcs_resource(cs, '+', &part1, &part2);
4524 
4525 		if (n_parts == 1 && !strncasecmp(cs, "3270cg", 6)) {
4526 		    free(part1);
4527 		    continue;
4528 		}
4529 
4530 		lff = load_fixed_font(NULL, cs);
4531 		if (lff != NULL) {
4532 		    Free(lff);
4533 		} else {
4534 		    font_found = true;
4535 		}
4536 		if (part1 != NULL) {
4537 		    Free(part1);
4538 		}
4539 		if (part2 != NULL) {
4540 		    Free(part2);
4541 		}
4542 	    }
4543 	    Free(cs_dup);
4544 	}
4545 
4546 	if (!font_found) {
4547 	    char *xs = expand_cslist(display_charsets);
4548 
4549 	    popup_an_error("No %s fonts found", xs);
4550 	    Free(xs);
4551 	    return false;
4552 	}
4553     }
4554     allow_resize = xappres.allow_resize;
4555 
4556 done:
4557     /* Set the appropriate global. */
4558     Replace(required_display_charsets,
4559 	    display_charsets? NewString(display_charsets): NULL);
4560     init_rsfonts(required_display_charsets);
4561 
4562     return true;
4563 }
4564 
4565 void
screen_newfont(const char * fontnames,bool do_popup,bool is_cs)4566 screen_newfont(const char *fontnames, bool do_popup, bool is_cs)
4567 {
4568     char *old_font;
4569     char *lff;
4570 
4571     /* Do nothing, successfully. */
4572     if (!is_cs && efontname && !strcmp(fontnames, efontname)) {
4573 	return;
4574     }
4575 
4576     /* Save the old font before trying the new one. */
4577     old_font = XtNewString(efontname);
4578 
4579     /* Try the new one. */
4580     if ((lff = load_fixed_font(fontnames, required_display_charsets)) != NULL) {
4581 	if (do_popup) {
4582 	    popup_an_error("%s", lff);
4583 	}
4584 	Free(lff);
4585 	XtFree(old_font);
4586 	return;
4587     }
4588 
4589     /* Store the old name away, in case we have to go back to it. */
4590     Replace(redo_old_font, old_font);
4591     screen_redo = REDO_FONT;
4592 
4593     screen_reinit(FONT_CHANGE);
4594     efont_changed = true;
4595 }
4596 
4597 /*
4598  * Expand a character set list into English.
4599  */
4600 static char *
expand_cslist(const char * s)4601 expand_cslist(const char *s)
4602 {
4603     int commas = 0;
4604     const char *t;
4605     char *comma;
4606     char *r;
4607 
4608     /* Count the commas. */
4609     for (t = s; (comma = strchr(t, ',')) != NULL; t = comma + 1) {
4610 	commas++;
4611     }
4612 
4613     /* If there aren't any, there isn't any work to do. */
4614     if (!commas) {
4615 	return NewString(s);
4616     }
4617 
4618     /* Allocate enough space for "a, b, c or d". */
4619     r = Malloc(strlen(s) + (commas * 2) + 2 + 1);
4620     *r = '\0';
4621 
4622     /* Copy and expand. */
4623     for (t = s; (comma = strchr(t, ',')) != NULL; t = comma + 1) {
4624 	int wl = comma - t;
4625 
4626 	if (*r) {
4627 	    strcat(r, ", ");
4628 	}
4629 	strncat(r, t, wl);
4630     }
4631     return strcat(strcat(r, " or "), t);
4632 }
4633 
4634 /*
4635  * Load and query a font.
4636  * Returns NULL (okay) or an error message.
4637  */
4638 static char *
load_fixed_font(const char * names,const char * reqd_display_charsets)4639 load_fixed_font(const char *names, const char *reqd_display_charsets)
4640 {
4641     int num_names = 1, num_cs = 1;
4642     char *name1 = NULL, *name2 = NULL;
4643     char *charset1 = NULL, *charset2 = NULL;
4644     char *r;
4645 
4646 #if defined(DEBUG_FONTPICK) /*[*/
4647     fprintf(stderr, "load_fixed_font(%s, %s)\n",
4648 	    names? names: "(wild)", reqd_display_charsets);
4649 #endif /*]*/
4650 
4651     /* Split out the names and character sets. */
4652     if (names) {
4653 	num_names = split_dbcs_resource(names, '+', &name1, &name2);
4654     }
4655     num_cs = split_dbcs_resource(reqd_display_charsets, '+', &charset1,
4656 	    &charset2);
4657     if (!names) {
4658 	num_names = num_cs;
4659     }
4660     if (num_names == 1 && num_cs >= 2) {
4661 	Free(name1);
4662 	Free(name2);
4663 	Free(charset1);
4664 	Free(charset2);
4665 	return NewString("Must specify two font names (SBCS+DBCS)");
4666     }
4667     if (num_names == 2 && num_cs < 2) {
4668 	Free(name2);
4669 	name2 = NULL;
4670     }
4671 
4672     /* If there's a DBCS font, load that first. */
4673     if (name2 != NULL) {
4674 	/* Load the second font. */
4675 	r = lff_single(name2, charset2, true);
4676 	if (r != NULL) {
4677 	    Free(name1);
4678 	    Free(charset1);
4679 	    return r;
4680 	}
4681     } else {
4682 	dbcs_font.font_struct = NULL;
4683 	dbcs_font.font = None;
4684 	dbcs = false;
4685     }
4686 
4687     /* Load the SBCS font. */
4688     r = lff_single(name1, charset1, false);
4689 
4690     /* Free the split-out names and return the final result. */
4691     Free(name1);
4692     Free(name2);
4693     Free(charset1);
4694     Free(charset2);
4695     return r;
4696 }
4697 
4698 static bool
charset_in_reqd(const char * charset,const char * reqd)4699 charset_in_reqd(const char *charset, const char *reqd)
4700 {
4701     char *r = NewString(reqd);
4702     char *str = r;
4703     char *tok;
4704     bool rv = false;
4705 
4706     while ((tok = strtok(str, ",")) != NULL) {
4707 	str = NULL;
4708 	if (!strcasecmp(charset, tok)) {
4709 	    rv = true;
4710 	    break;
4711 	}
4712     }
4713     Free(r);
4714     return rv;
4715 }
4716 
4717 /*
4718  * Load and query one font.
4719  * Returns NULL (okay) or an error message.
4720  */
4721 static char *
lff_single(const char * name,const char * reqd_display_charset,bool is_dbcs)4722 lff_single(const char *name, const char *reqd_display_charset, bool is_dbcs)
4723 {
4724     XFontStruct *g;
4725     const char *best = NULL;
4726 
4727 #if defined(DEBUG_FONTPICK) /*[*/
4728     fprintf(stderr, "lff_single: name %s, cs %s, %s\n",
4729 	    name? name: "(wild)",
4730 	    reqd_display_charset, is_dbcs? "dbcs": "sbcs");
4731 #endif /*]*/
4732 
4733     if (name && *name == '!') {
4734 	name++;
4735     }
4736 
4737     if (name) {
4738 	char **names;
4739 	int count;
4740 	XFontStruct *f;
4741 	unsigned long svalue;
4742 	char *spacing, *family_name, *font_encoding, *fe, *charset;
4743 
4744 	/* Check the character set */
4745 	names = XListFontsWithInfo(display, name, 1, &count, &f);
4746 	if (names == NULL) {
4747 	    return xs_buffer("Font %s\nnot found", name);
4748 	}
4749 	if (XGetFontProperty(f, a_spacing, &svalue)) {
4750 	    spacing = XGetAtomName(display, svalue);
4751 	} else {
4752 	    XFreeFontInfo(names, f, count);
4753 	    return xs_buffer("Font %s\nhas no spacing property", name);
4754 	}
4755 	if (strcasecmp(spacing, "c") && strcasecmp(spacing, "m")) {
4756 	    XFreeFontInfo(names, f, count);
4757 	    return xs_buffer("Font %s\nhas invalid spacing property '%s'",
4758 		    name, spacing);
4759 	}
4760 	if (XGetFontProperty(f, a_registry, &svalue)) {
4761 	    family_name = XGetAtomName(display, svalue);
4762 	} else {
4763 	    XFreeFontInfo(names, f, count);
4764 	    return xs_buffer("Font %s\nhas no registry property", name);
4765 	}
4766 	if (XGetFontProperty(f, a_encoding, &svalue)) {
4767 	    font_encoding = XGetAtomName(display, svalue);
4768 	} else {
4769 	    XFreeFontInfo(names, f, count);
4770 	    return xs_buffer("Font %s\nhas no encoding property", name);
4771 	}
4772 	if (font_encoding[0] == '-') {
4773 	    fe = font_encoding + 1;
4774 	} else {
4775 	    fe = font_encoding;
4776 	}
4777 	XFreeFontInfo(names, f, count);
4778 	charset = xs_buffer("%s-%s", family_name, fe);
4779 	Free(family_name);
4780 	Free(font_encoding);
4781 	if (!charset_in_reqd(charset, reqd_display_charset)) {
4782 	    char *r = xs_buffer("Font %s\nimplements %s, not %s\n", name,
4783 		    charset, reqd_display_charset);
4784 
4785 	    Free(charset);
4786 	    return r;
4787 	}
4788 	Free(charset);
4789 
4790 	best = name;
4791     } else {
4792 	void *cookie;
4793 	dfc_t *d;
4794 	int best_pixel_size = 0;
4795 	char *best_weight = NULL;
4796 
4797 	cookie = NULL;
4798 	while (dfc_search_family(reqd_display_charset, &d, &cookie)) {
4799 	    if (best == NULL ||
4800 		(labs(d->points - 14) <
4801 		 labs(best_pixel_size - 14)) ||
4802 		(best_weight == NULL ||
4803 		 (!strcasecmp(best_weight, "bold") &&
4804 		  strcasecmp(d->weight, "bold")))) {
4805 		best = d->name;
4806 		best_weight = d->weight;
4807 		best_pixel_size = d->points;
4808 	    }
4809 	}
4810 	if (best == NULL) {
4811 	    return xs_buffer("No %s fonts found", reqd_display_charset);
4812 	}
4813     }
4814 
4815     g = XLoadQueryFont(display, best);
4816     if (g == NULL) {
4817 	return xs_buffer("Font %s could not be loaded", best);
4818     }
4819     set_font_globals(g, best, best, g->fid, is_dbcs);
4820     return NULL;
4821 }
4822 
4823 /*
4824  * Figure out what sort of registry and encoding we want.
4825  */
4826 char *
display_charset(void)4827 display_charset(void)
4828 {
4829     return (required_display_charsets != NULL)? required_display_charsets:
4830 					        default_display_charset;
4831 }
4832 
4833 /*
4834  * Set globals based on font name and info
4835  */
4836 static void
set_font_globals(XFontStruct * f,const char * ef,const char * fef,Font ff,bool is_dbcs)4837 set_font_globals(XFontStruct *f, const char *ef, const char *fef, Font ff,
4838 	bool is_dbcs)
4839 {
4840     unsigned long svalue;
4841     unsigned i;
4842     char *family_name = NULL;
4843     char *font_encoding = NULL;
4844     char *fe = NULL;
4845     char *font_charset = NULL;
4846 
4847     if (XGetFontProperty(f, a_registry, &svalue)) {
4848 	family_name = XGetAtomName(display, svalue);
4849     }
4850     if (family_name == NULL) {
4851 	Error("Cannot get font family_name");
4852     }
4853     if (XGetFontProperty(f, a_encoding, &svalue)) {
4854 	font_encoding = XGetAtomName(display, svalue);
4855     }
4856     if (font_encoding == NULL) {
4857 	Error("Cannot get font encoding");
4858     }
4859     if (font_encoding[0] == '-') {
4860 	fe = font_encoding + 1;
4861     } else {
4862 	fe = font_encoding;
4863     }
4864 
4865     font_charset = xs_buffer("%s-%s", family_name, fe);
4866     Free(font_encoding);
4867 
4868     if (is_dbcs) {
4869 	/* Hack. */
4870 	dbcs_font.font_struct = f;
4871 	dbcs_font.font = f->fid;
4872 	dbcs_font.unicode = !strcasecmp(family_name, "iso10646");
4873 	dbcs_font.ascent = f->max_bounds.ascent;
4874 	dbcs_font.descent = f->max_bounds.descent;
4875 	dbcs_font.char_width  = fCHAR_WIDTH(f);
4876 	dbcs_font.char_height = dbcs_font.ascent + dbcs_font.descent;
4877 	dbcs_font.d16_ix = display16_init(font_charset);
4878 	dbcs = true;
4879 	Replace(full_efontname_dbcs, XtNewString(fef));
4880 	Replace(efont_charset_dbcs, font_charset);
4881 
4882 	Free(family_name);
4883 	return;
4884     }
4885     Replace(efontname, XtNewString(ef));
4886     Replace(full_efontname, XtNewString(fef));
4887     Replace(efont_charset, font_charset);
4888 
4889     /* Set the dimensions. */
4890     nss.char_width  = fCHAR_WIDTH(f);
4891     nss.char_height = fCHAR_HEIGHT(f);
4892     nss.fid = ff;
4893     if (nss.font != NULL) {
4894 	XFreeFontInfo(NULL, nss.font, 1);
4895     }
4896     nss.font = f;
4897     nss.ascent = f->ascent;
4898     nss.descent = f->descent;
4899 
4900     /* Figure out if this is a 3270 font, or a standard X font. */
4901     if (XGetFontProperty(f, XA_FAMILY_NAME, &svalue)) {
4902 	nss.standard_font = (Atom) svalue != a_3270;
4903     } else if (!strncmp(efontname, "3270", 4)) {
4904 	nss.standard_font = false;
4905     } else {
4906 	nss.standard_font = true;
4907     }
4908 
4909     /* Set other globals. */
4910     if (nss.standard_font) {
4911 	nss.extended_3270font = false;
4912 	nss.full_apl_font = false;
4913 	nss.font_8bit = efont_matches;
4914 	nss.font_16bit = (f->max_byte1 > 0);
4915 	nss.d8_ix = display8_init(nss.font_8bit? font_charset: "ascii-7");
4916     } else {
4917 #if defined(BROKEN_MACH32)
4918 	nss.extended_3270font = false;
4919 #else
4920 	nss.extended_3270font = f->max_byte1 > 0 || f->max_char_or_byte2 > 255;
4921 #endif
4922 	nss.full_apl_font = !strcmp(ef, "3270"); /* hack! */
4923 	nss.font_8bit = false;
4924 	nss.font_16bit = false;
4925 	nss.d8_ix = display8_init(font_charset);
4926     }
4927     nss.unicode_font = !strcasecmp(family_name, "iso10646");
4928     Free(family_name);
4929 
4930     /* See if this font has any unusually-shaped characters. */
4931     INIT_ODD(nss.odd_width);
4932     INIT_ODD(nss.odd_lbearing);
4933     nss.funky_font = false;
4934     if (!nss.extended_3270font && f->per_char != NULL) {
4935 	for (i = 0; i < 256; i++) {
4936 	    if (PER_CHAR(f, i).width == 0 &&
4937 		(PER_CHAR(f, i).rbearing |
4938 		 PER_CHAR(f, i).lbearing |
4939 		 PER_CHAR(f, i).ascent |
4940 		 PER_CHAR(f, i).descent) == 0) {
4941 		/* Missing character. */
4942 		continue;
4943 	    }
4944 
4945 	    if (PER_CHAR(f, i).width != f->max_bounds.width) {
4946 		SET_ODD(nss.odd_width, i);
4947 		nss.funky_font = true;
4948 	    }
4949 	    if (PER_CHAR(f, i).lbearing < 0) {
4950 		SET_ODD(nss.odd_lbearing, i);
4951 		nss.funky_font = true;
4952 	    }
4953 	}
4954     }
4955 
4956     /*
4957      * If we've changed the rules for resizing, let the window manager
4958      * know.
4959      */
4960     if (container != NULL) {
4961 	vtrace("set_font_globals(%s)\n", ef);
4962     }
4963 }
4964 
4965 /*
4966  * Font initialization.
4967  */
4968 void
font_init(void)4969 font_init(void)
4970 {
4971 }
4972 
4973 /*
4974  * Change models.
4975  */
4976 void
screen_change_model(int mn,int ovc,int ovr)4977 screen_change_model(int mn, int ovc, int ovr)
4978 {
4979     if (CONNECTED ||
4980 	(model_num == mn && ovc == ov_cols && ovr == ov_rows)) {
4981 	    return;
4982     }
4983 
4984     redo_old_model = model_num;
4985     redo_old_ov_cols = ov_cols;
4986     redo_old_ov_rows = ov_rows;
4987     screen_redo = REDO_MODEL;
4988 
4989     model_changed = true;
4990     if (ov_cols != ovc || ov_rows != ovr) {
4991 	oversize_changed = true;
4992     }
4993     set_rows_cols(mn, ovc, ovr);
4994     screen_reinit(MODEL_CHANGE);
4995 
4996     /* Redo the terminal type. */
4997 	net_set_default_termtype();
4998 }
4999 
5000 /*
5001  * Change emulation modes.
5002  */
5003 void
screen_extended(bool extended _is_unused)5004 screen_extended(bool extended _is_unused)
5005 {
5006     set_rows_cols(model_num, ov_cols, ov_rows);
5007     model_changed = true;
5008 }
5009 
5010 void
screen_m3279(bool m3279 _is_unused)5011 screen_m3279(bool m3279 _is_unused)
5012 {
5013     destroy_pixels();
5014     screen_reinit(COLOR_CHANGE);
5015     set_rows_cols(model_num, ov_cols, ov_rows);
5016     model_changed = true;
5017 }
5018 
5019 /*
5020  * Change color schemes.  Alas, this is destructive if it fails.
5021  */
5022 void
screen_newscheme(char * s)5023 screen_newscheme(char *s)
5024 {
5025     bool xferred;
5026 
5027     if (!mode.m3279) {
5028 	return;
5029     }
5030 
5031     destroy_pixels();
5032     xferred = xfer_color_scheme(s, true);
5033     if (xferred) {
5034 	xappres.color_scheme = s;
5035     }
5036     screen_reinit(COLOR_CHANGE);
5037     scheme_changed = true;
5038 }
5039 
5040 /*
5041  * Change host code pages.
5042  */
5043 void
screen_newcodepage(char * cpname)5044 screen_newcodepage(char *cpname)
5045 {
5046     char *old_codepage = NewString(get_codepage_name());
5047 
5048     switch (codepage_init(cpname)) {
5049     case CS_OKAY:
5050 	/* Success. */
5051 	Free(old_codepage);
5052 	st_changed(ST_CODEPAGE, true);
5053 	codepage_changed = true;
5054 	break;
5055     case CS_NOTFOUND:
5056 	Free(old_codepage);
5057 	popup_an_error("Cannot find definition of host code page \"%s\"",
5058 		cpname);
5059 	break;
5060     case CS_BAD:
5061 	Free(old_codepage);
5062 	popup_an_error("Invalid code page definition for \"%s\"", cpname);
5063 	break;
5064     case CS_PREREQ:
5065 	Free(old_codepage);
5066 	popup_an_error("No fonts for host code page \"%s\"", cpname);
5067 	break;
5068     case CS_ILLEGAL:
5069 	/* Error already popped up. */
5070 	Free(old_codepage);
5071 	break;
5072 
5073     }
5074 }
5075 
5076 /*
5077  * Visual or not-so-visual bell
5078  */
5079 void
ring_bell(void)5080 ring_bell(void)
5081 {
5082     static XGCValues xgcv;
5083     static GC bgc;
5084     static int initted;
5085     struct timeval tv;
5086 
5087     /* Ring the real display's bell. */
5088     if (!appres.interactive.visual_bell) {
5089 	XBell(display, xappres.bell_volume);
5090     }
5091 
5092     /* If we're iconic, invert the icon and return. */
5093     if (!xappres.active_icon) {
5094 	query_window_state();
5095 	if (iconic) {
5096 	    invert_icon(true);
5097 	    return;
5098 	}
5099     }
5100 
5101     if (!appres.interactive.visual_bell || !ss->exposed_yet) {
5102 	return;
5103     }
5104 
5105     /* Do a screen flash. */
5106 
5107     if (!initted) {
5108 	xgcv.function = GXinvert;
5109 	bgc = XtGetGC(toplevel, GCFunction, &xgcv);
5110 	initted = 1;
5111     }
5112     screen_disp(false);
5113     XFillRectangle(display, ss->window, bgc,
5114 	0, 0, ss->screen_width, ss->screen_height);
5115     XSync(display, 0);
5116     tv.tv_sec = 0;
5117     tv.tv_usec = 125000;
5118     select(0, NULL, NULL, NULL, &tv);
5119     XFillRectangle(display, ss->window, bgc,
5120 	0, 0, ss->screen_width, ss->screen_height);
5121     XSync(display, 0);
5122 }
5123 
5124 /*
5125  * Window deletion
5126  */
5127 void
PA_WMProtocols_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)5128 PA_WMProtocols_xaction(Widget w, XEvent *event, String *params,
5129 	Cardinal *num_params)
5130 {
5131     XClientMessageEvent *cme = (XClientMessageEvent *)event;
5132 
5133 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
5134     xaction_debug(PA_WMProtocols_xaction, event, params, num_params);
5135 #endif /*]*/
5136     if ((Atom)cme->data.l[0] == a_delete_me) {
5137 	if (w == toplevel) {
5138 	    x3270_exit(0);
5139 	} else {
5140 	    XtPopdown(w);
5141 	}
5142     } else if ((Atom)cme->data.l[0] == a_save_yourself && w == toplevel) {
5143 	save_yourself();
5144     }
5145 }
5146 
5147 /* Initialize the icon. */
5148 void
icon_init(void)5149 icon_init(void)
5150 {
5151     x3270_icon = XCreateBitmapFromData(display, root_window,
5152 	    (char *)x3270_bits, x3270_width, x3270_height);
5153 
5154     if (xappres.active_icon) {
5155 	Dimension iw, ih;
5156 
5157 	aicon_font_init();
5158 	aicon_size(&iw, &ih);
5159 	icon_shell =  XtVaAppCreateShell(
5160 		"x3270icon",
5161 		"X3270",
5162 		overrideShellWidgetClass,
5163 		display,
5164 		XtNwidth, iw,
5165 		XtNheight, ih,
5166 	    XtNmappedWhenManaged, False,
5167 	    NULL);
5168 	XtRealizeWidget(icon_shell);
5169 	XtVaSetValues(toplevel,
5170 	    XtNiconWindow, XtWindow(icon_shell),
5171 	    NULL);
5172 	if (xappres.active_icon) {
5173 	    XtVaSetValues(icon_shell,
5174 		    XtNbackground, appres.interactive.mono?
5175 			xappres.background: colorbg_pixel,
5176 		    NULL);
5177 	}
5178     } else {
5179 	unsigned i;
5180 
5181 	for (i = 0; i < sizeof(x3270_bits); i++) {
5182 	    x3270_bits[i] = ~x3270_bits[i];
5183 	}
5184 	inv_icon = XCreateBitmapFromData(display, root_window,
5185 		(char *)x3270_bits, x3270_width, x3270_height);
5186 	wait_icon = XCreateBitmapFromData(display, root_window,
5187 		(char *)wait_bits, wait_width, wait_height);
5188 	for (i = 0; i < sizeof(wait_bits); i++) {
5189 	    wait_bits[i] = ~wait_bits[i];
5190 	}
5191 	inv_wait_icon = XCreateBitmapFromData(display, root_window,
5192 		(char *)wait_bits, wait_width, wait_height);
5193 	XtVaSetValues(toplevel,
5194 		XtNiconPixmap, x3270_icon,
5195 		XtNiconMask, x3270_icon,
5196 		NULL);
5197     }
5198 }
5199 
5200 /*
5201  * Initialize the active icon font information.
5202  */
5203 static void
aicon_font_init(void)5204 aicon_font_init(void)
5205 {
5206     XFontStruct *f;
5207     Font ff;
5208     char **matches;
5209     int count;
5210 
5211     if (!xappres.active_icon) {
5212 	xappres.label_icon = False;
5213 	return;
5214     }
5215 
5216     matches = XListFontsWithInfo(display, xappres.icon_font, 1, &count,
5217 	    &f);
5218     if (matches == NULL) {
5219 	popup_an_error("No font %s \"%s\"\nactiveIcon will not work",
5220 		ResIconFont, xappres.icon_font);
5221 	xappres.active_icon = False;
5222 	return;
5223     }
5224     ff = XLoadFont(display, matches[0]);
5225     iss.char_width = fCHAR_WIDTH(f);
5226     iss.char_height = fCHAR_HEIGHT(f);
5227     iss.fid = ff;
5228     iss.font = f;
5229     iss.ascent = f->ascent;
5230     iss.overstrike = false;
5231     iss.standard_font = true;
5232     iss.extended_3270font = false;
5233     iss.font_8bit = false;
5234     iss.obscured = true;
5235     iss.d8_ix = display8_init("ascii-7");
5236     if (xappres.label_icon) {
5237 	matches = XListFontsWithInfo(display, xappres.icon_label_font, 1,
5238 		&count, &ailabel_font);
5239 	if (matches == NULL) {
5240 	    popup_an_error("Cannot load %s \"%s\" font\nlabelIcon will not "
5241 		    "work", ResIconLabelFont, xappres.icon_label_font);
5242 	    xappres.label_icon = False;
5243 	    return;
5244 	}
5245 	ailabel_font->fid = XLoadFont(display, matches[0]);
5246 	aicon_label_height = fCHAR_HEIGHT(ailabel_font) + 2;
5247     }
5248     INIT_ODD(iss.odd_width);
5249     INIT_ODD(iss.odd_lbearing);
5250     iss.funky_font = false;
5251 }
5252 
5253 /*
5254  * Determine the current size of the active icon.
5255  */
5256 static void
aicon_size(Dimension * iw,Dimension * ih)5257 aicon_size(Dimension *iw, Dimension *ih)
5258 {
5259     XIconSize *is;
5260     int count;
5261 
5262     *iw = maxCOLS*iss.char_width + 2*VHALO;
5263     *ih = maxROWS*iss.char_height + 2*HHALO + aicon_label_height;
5264     if (XGetIconSizes(display, root_window, &is, &count)) {
5265 	if (*iw > (unsigned) is[0].max_width) {
5266 	    *iw = is[0].max_width;
5267 	}
5268 	if (*ih > (unsigned) is[0].max_height) {
5269 	    *ih = is[0].max_height;
5270 	}
5271     }
5272 }
5273 
5274 /*
5275  * Initialize the active icon.  Assumes that aicon_font_init has already been
5276  * called.
5277  */
5278 static void
aicon_init(void)5279 aicon_init(void)
5280 {
5281     if (!xappres.active_icon) {
5282 	return;
5283     }
5284 
5285     iss.widget = icon_shell;
5286     iss.window = XtWindow(iss.widget);
5287     iss.cursor_daddr = 0;
5288     iss.exposed_yet = false;
5289     if (xappres.label_icon) {
5290 	XGCValues xgcv;
5291 
5292 	xgcv.font = ailabel_font->fid;
5293 	xgcv.foreground = xappres.foreground;
5294 	xgcv.background = xappres.background;
5295 	ailabel_gc = XtGetGC(toplevel, GCFont|GCForeground|GCBackground,
5296 		&xgcv);
5297     }
5298 }
5299 
5300 /*
5301  * Reinitialize the active icon.
5302  */
5303 static void
aicon_reinit(unsigned cmask)5304 aicon_reinit(unsigned cmask)
5305 {
5306     if (!xappres.active_icon) {
5307 	return;
5308     }
5309 
5310     if (cmask & (FONT_CHANGE | COLOR_CHANGE)) {
5311 	make_gcs(&iss);
5312     }
5313 
5314     if (cmask & MODEL_CHANGE) {
5315 	aicon_size(&iss.screen_width, &iss.screen_height);
5316 	Replace(iss.image, (struct sp *)XtMalloc(sizeof(struct sp) * maxROWS *
5317 		    maxCOLS));
5318 	XtVaSetValues(iss.widget,
5319 		XtNwidth, iss.screen_width,
5320 		XtNheight, iss.screen_height,
5321 		NULL);
5322     }
5323     if (cmask & (MODEL_CHANGE | FONT_CHANGE | COLOR_CHANGE)) {
5324 	memset((char *)iss.image, 0, sizeof(struct sp) * maxROWS * maxCOLS);
5325     }
5326 }
5327 
5328 /* Draw the aicon label */
5329 static void
draw_aicon_label(void)5330 draw_aicon_label(void)
5331 {
5332     int len;
5333     Position x;
5334 
5335     if (!xappres.label_icon || !iconic) {
5336 	return;
5337     }
5338 
5339     XFillRectangle(display, iss.window,
5340 	get_gc(&iss, INVERT_COLOR(0)),
5341 	0, iss.screen_height - aicon_label_height,
5342 	iss.screen_width, aicon_label_height);
5343     len = strlen(aicon_text);
5344     x = ((int)iss.screen_width - XTextWidth(ailabel_font, aicon_text, len)) / 2;
5345     if (x < 0) {
5346 	x = 2;
5347     }
5348     XDrawImageString(display, iss.window, ailabel_gc,
5349 	    x,
5350 	    iss.screen_height - aicon_label_height + ailabel_font->ascent,
5351 	    aicon_text, len);
5352 }
5353 
5354 /* Set the aicon label */
5355 void
set_aicon_label(char * l)5356 set_aicon_label(char *l)
5357 {
5358     Replace(aicon_text, XtNewString(l));
5359     draw_aicon_label();
5360 }
5361 
5362 /* Change the bitmap icon. */
5363 static void
flip_icon(bool inverted,enum mcursor_state mstate)5364 flip_icon(bool inverted, enum mcursor_state mstate)
5365 {
5366     Pixmap p = x3270_icon;
5367 
5368     if (mstate == LOCKED) {
5369 	mstate = NORMAL;
5370     }
5371     if (xappres.active_icon
5372 	|| (inverted == icon_inverted && mstate == icon_cstate)) {
5373 	return;
5374     }
5375     switch (mstate) {
5376     case WAIT:
5377 	if (inverted) {
5378 	    p = inv_wait_icon;
5379 	} else {
5380 	    p = wait_icon;
5381 	}
5382 	break;
5383     case LOCKED:
5384     case NORMAL:
5385 	if (inverted) {
5386 	    p = inv_icon;
5387 	} else {
5388 	    p = x3270_icon;
5389 	}
5390 	break;
5391     }
5392     XtVaSetValues(toplevel,
5393 	    XtNiconPixmap, p,
5394 	    XtNiconMask, p,
5395 	    NULL);
5396     icon_inverted = inverted;
5397     icon_cstate = mstate;
5398 }
5399 
5400 /*
5401  * Invert the icon.
5402  */
5403 static void
invert_icon(bool inverted)5404 invert_icon(bool inverted)
5405 {
5406     flip_icon(inverted, icon_cstate);
5407 }
5408 
5409 /*
5410  * Change to the lock icon.
5411  */
5412 static void
lock_icon(enum mcursor_state state)5413 lock_icon(enum mcursor_state state)
5414 {
5415     flip_icon(icon_inverted, state);
5416 }
5417 
5418 /* Check the font menu for an existing name. */
5419 static bool
font_in_menu(const char * font)5420 font_in_menu(const char *font)
5421 {
5422     struct font_list *g;
5423 
5424     for (g = font_list; g != NULL; g = g->next) {
5425 	if (!strcasecmp(NO_BANG(font), NO_BANG(g->font))) {
5426 	    return true;
5427 	}
5428     }
5429     return false;
5430 }
5431 
5432 /* Add a font to the font menu. */
5433 static bool
add_font_to_menu(char * label,const char * font)5434 add_font_to_menu(char *label, const char *font)
5435 {
5436     struct font_list *f;
5437 
5438     label = NewString(label);
5439     f = (struct font_list *)XtMalloc(sizeof(*f));
5440     if (!split_hier(label, &f->label, &f->parents)) {
5441 	Free((XtPointer)f);
5442 	return false;
5443     }
5444     f->font = NewString(font);
5445     f->next = NULL;
5446     f->mlabel = label;
5447     if (font_list) {
5448 	font_last->next = f;
5449     } else {
5450 	font_list = f;
5451     }
5452     font_last = f;
5453     font_count++;
5454     return true;
5455 }
5456 
5457 /*
5458  * Resize font list parser.
5459  */
5460 static void
init_rsfonts(char * charset_name)5461 init_rsfonts(char *charset_name)
5462 {
5463     char *ms;
5464     struct rsfont *r;
5465     struct font_list *f;
5466     char *dupcsn, *csn, *buf;
5467     char *lasts = NULL;
5468     XFontStruct *fs;
5469     char *hier_name;
5470 
5471     /* Clear the old lists. */
5472     while (rsfonts != NULL) {
5473 	r = rsfonts->next;
5474 	Free(rsfonts);
5475 	rsfonts = r;
5476     }
5477     while (font_list != NULL) {
5478 	f = font_list->next;
5479 	if (font_list->parents) {
5480 	    Free(font_list->parents);
5481 	}
5482 	Free(font_list->mlabel);
5483 	Free(font_list->font);
5484 	Free(font_list);
5485 	font_list = f;
5486     }
5487     font_last = NULL;
5488     font_count = 0;
5489 
5490     /* If there's no character set, we're done. */
5491     if (charset_name == NULL) {
5492 	return;
5493     }
5494 
5495     /* Get the emulatorFontList resource. */
5496     ms = get_fresource("%s.%s", ResEmulatorFontList, charset_name);
5497     if (ms != NULL) {
5498 	char *ns;
5499 	char *line;
5500 	char *label;
5501 	char *font;
5502 	bool resize;
5503 	char **matches;
5504 	int count;
5505 
5506 	ns = ms = NewString(ms);
5507 	while (split_lresource(&ms, &line) == 1) {
5508 
5509 	    /* Figure out what it's about. */
5510 	    split_font_list_entry(line, &label, NULL, &resize, &font);
5511 	    if (!*font) {
5512 		continue;
5513 	    }
5514 
5515 	    /* Search for duplicates. */
5516 	    if (font_in_menu(font)) {
5517 		continue;
5518 	    }
5519 
5520 	    /* Add it to the font_list (menu). */
5521 	    if (!add_font_to_menu((label != NULL)? label: NO_BANG(font),
5522 			font)) {
5523 		continue;
5524 	    }
5525 
5526 	    /* Add it to the resize menu, if possible. */
5527 	    if (!resize) {
5528 		continue;
5529 	    }
5530 	    matches = XListFontsWithInfo(display, NO_BANG(font), 1, &count,
5531 		    &fs);
5532 	    if (matches == NULL) {
5533 		continue;
5534 	    }
5535 	    r = (struct rsfont *)XtMalloc(sizeof(*r));
5536 	    r->name = XtNewString(font);
5537 	    r->width = fCHAR_WIDTH(fs);
5538 	    r->height = fCHAR_HEIGHT(fs);
5539 	    r->descent = fs->max_bounds.descent;
5540 	    XFreeFontInfo(matches, fs, count);
5541 	    r->next = rsfonts;
5542 	    rsfonts = r;
5543 	}
5544 	free(ns);
5545     }
5546 
5547     /*
5548      * In DBCS mode, if we've found at least one appropriate font from the
5549      * list, we're done.
5550      */
5551     if (dbcs) {
5552 	return;
5553     }
5554 
5555     /* Add 'fixed' to the menu, so there's at least one alternative. */
5556     add_font_to_menu("fixed", "!fixed");
5557 
5558     /* Expand out wild-cards based on the display character set names. */
5559     buf = dupcsn = NewString(charset_name);
5560     while ((csn = strtok_r(buf, ",", &lasts)) != NULL) {
5561 	void *cookie;
5562 	const char *name;
5563 
5564 	buf = NULL;
5565 	if (!strncasecmp(csn, "3270cg", 6)) {
5566 	    continue;
5567 	}
5568 
5569 	cookie = NULL;
5570 	while ((name = dfc_search_family(csn, NULL, &cookie)) != NULL) {
5571 	    if (!font_in_menu(name)) {
5572 		char *dash1 = NULL, *dash2 = NULL;
5573 
5574 		if (name[0] == '-') {
5575 		    dash1 = strchr(name + 1, '-');
5576 		    if (dash1 != NULL) {
5577 			dash2 = strchr(dash1 + 1, '-');
5578 		    }
5579 		}
5580 		if (dash2 != NULL) {
5581 		    hier_name = xs_buffer("%s>%.*s>%s",
5582 			    csn, (int)(dash2 - name - 1), name + 1, dash2 + 1);
5583 		} else
5584 		    hier_name = xs_buffer("%s>%s", csn, name);
5585 		add_font_to_menu(hier_name, name);
5586 		Free(hier_name);
5587 	    }
5588 	}
5589     }
5590     Free(dupcsn);
5591 }
5592 
5593 /*
5594  * Handle ConfigureNotify events.
5595  */
5596 static struct {
5597     bool ticking;
5598     Dimension width, height;
5599     Position x, y;
5600     XtIntervalId id;
5601 } cn = {
5602     false, 0, 0, 0, 0, 0
5603 };
5604 static Position main_x = 0, main_y = 0;
5605 
5606 /*
5607  * Timeout routine called 0.5 sec after x3270 sets new screen dimensions.
5608  * We assume that if this happens, the window manager is happy with our new
5609  * size.
5610  */
5611 static void
configure_stable(XtPointer closure _is_unused,XtIntervalId * id _is_unused)5612 configure_stable(XtPointer closure _is_unused, XtIntervalId *id _is_unused)
5613 {
5614     vtrace("Reconfigure timer expired\n");
5615     configure_ticking = false;
5616     if (!cn.ticking) {
5617 	screen_redo = REDO_NONE;
5618     }
5619 }
5620 
5621 /* Perform a resize operation. */
5622 static void
do_resize(void)5623 do_resize(void)
5624 {
5625     struct rsfont *r;
5626     struct rsfont *best = (struct rsfont *) NULL;
5627 
5628     /* What we're doing now is irreversible. */
5629     screen_redo = REDO_RESIZE;
5630 
5631     if (rsfonts == NULL || !allow_resize) {
5632 	/* Illegal or impossible. */
5633 	if (rsfonts == NULL) {
5634 	    vtrace("  no fonts available for resize\n");
5635 	} else {
5636 	    vtrace("  resize prohibited by resource\n");
5637 	}
5638 	return;
5639     }
5640 
5641     /*
5642      * Recompute the resulting screen area for each font, based on the
5643      * current keypad, model, and scrollbar settings, and snapped to the
5644      * minimum size.
5645      */
5646     for (r = rsfonts; r != (struct rsfont *) NULL; r = r->next) {
5647 	Dimension cw, ch;	/* container_width, container_height */
5648 	Dimension mkw;
5649 
5650 	cw = SCREEN_WIDTH(r->width, HHALO)+2 + scrollbar_width;
5651 	mkw = min_keypad_width();
5652 	if (kp_placement == kp_integral && xappres.keypad_on
5653 		&& cw < mkw) {
5654 	    cw = mkw;
5655 	}
5656 
5657 	ch = SCREEN_HEIGHT(r->height, r->descent, VHALO)+2 +
5658 	    menubar_qheight(cw);
5659 	if (kp_placement == kp_integral && xappres.keypad_on) {
5660 	    ch += keypad_qheight();
5661 	}
5662 	r->total_width = cw;
5663 	r->total_height = ch;
5664 	r->area = cw * ch;
5665     }
5666 
5667     /*
5668      * Find the font with the largest area that fits within the requested
5669      * dimensions.
5670      */
5671     for (r = rsfonts; r != (struct rsfont *) NULL; r = r->next) {
5672 	if (r->total_width <= cn.width &&
5673 	    r->total_height <= cn.height &&
5674 	    (best == NULL || r->area > best->area)) {
5675 	    best = r;
5676 	}
5677     }
5678 
5679     /*
5680      * If the screen got smaller, but none of the fonts is small enough,
5681      * switch to the smallest.
5682      */
5683     if (!best && cn.width <= main_width && cn.height <= main_height) {
5684 	for (r = rsfonts; r != (struct rsfont *) NULL; r = r->next) {
5685 	    if (best == NULL || r->area < best->area) {
5686 		best = r;
5687 	    }
5688 	}
5689     }
5690 
5691     if (!best || (efontname && !strcmp(best->name, efontname))) {
5692 	/* Accept the change and float inside the new size. */
5693 	vtrace("  no better font available\n");
5694 	vtrace("setting fixed_width and fixed_height\n");
5695 	fixed_width = cn.width;
5696 	fixed_height = cn.height;
5697 	screen_reinit(FONT_CHANGE);
5698 	clear_fixed();
5699     } else {
5700 	/* Change fonts. */
5701 	vtrace("    switching to font '%s', snap size %dx%d\n",
5702 		best->name, best->total_width, best->total_height);
5703 	vtrace("setting fixed_width and fixed_height\n");
5704 	fixed_width = cn.width;
5705 	fixed_height = cn.height;
5706 	screen_newfont(best->name, false, false);
5707 
5708 	/* screen_newfont() sets screen_redo to REDO_FONT. */
5709 	screen_redo = REDO_RESIZE;
5710     }
5711 }
5712 
5713 static void
revert_screen(void)5714 revert_screen(void)
5715 {
5716     const char *revert = NULL;
5717 
5718     /*
5719      * If we took a ConfigureNotify as new screen dimensions, ignore that now.
5720      */
5721     clear_fixed();
5722 
5723     /* If there's a reconfiguration pending, try to undo it. */
5724     switch (screen_redo) {
5725     case REDO_FONT:
5726 	revert = "font";
5727 	screen_newfont(redo_old_font, false, false);
5728 	break;
5729     case REDO_MODEL:
5730 	revert = "model number";
5731 	screen_change_model(redo_old_model,
5732 	    redo_old_ov_cols, redo_old_ov_rows);
5733 	break;
5734     case REDO_KEYPAD:
5735 	revert = "keypad configuration";
5736 	xappres.keypad_on = False;
5737 	screen_showikeypad(xappres.keypad_on);
5738 	break;
5739     case REDO_SCROLLBAR:
5740 	revert = "scrollbar configuration";
5741 	if (toggled(SCROLL_BAR)) {
5742 	    toggle_toggle(SCROLL_BAR);
5743 	    toggle_scrollBar(SCROLL_BAR, TT_INTERACTIVE);
5744 	}
5745 	break;
5746     case REDO_RESIZE:
5747 	screen_redo = REDO_NONE;
5748 	/* fall through... */
5749     case REDO_NONE:
5750 	/* Initial configuration, or user-generated resize. */
5751 	do_resize();
5752 	return;
5753     default:
5754 	break;
5755     }
5756 
5757     /* Tell the user what we did. */
5758     if (revert != NULL) {
5759 	popup_an_error("Main window does not fit on the X display\n"
5760 		"Reverting to previous %s", revert);
5761     }
5762 
5763     screen_redo = REDO_NONE;
5764 }
5765 
5766 /*
5767  * Timeout routine called 0.5 sec after x3270 receives the last ConfigureNotify
5768  * message.  This is for window managers that use 'continuous' move or resize
5769  * actions, so we don't do anything until they stop sending us events.
5770  */
5771 
5772 static void
stream_end(XtPointer closure _is_unused,XtIntervalId * id _is_unused)5773 stream_end(XtPointer closure _is_unused, XtIntervalId *id _is_unused)
5774 {
5775     bool needs_moving = false;
5776 
5777     vtrace("Stream timer expired %hux%hu+%hd+%hd\n",
5778 	    cn.width, cn.height, cn.x, cn.y);
5779 
5780     /* Not ticking any more. */
5781     cn.ticking = false;
5782 
5783     /* Save the new coordinates in globals for next time. */
5784     if (cn.x != main_x || cn.y != main_y) {
5785 	main_x = cn.x;
5786 	main_y = cn.y;
5787 	needs_moving = true;
5788     }
5789 
5790     /*
5791      * If the dimensions are correct, do nothing, forget about any
5792      * reconfig we may need to revert, and get out.
5793      */
5794     if (cn.width == main_width && cn.height == main_height) {
5795 	vtrace("  width and height match, done\n");
5796 	screen_redo = REDO_NONE;
5797 	clear_fixed();
5798 	goto done;
5799     }
5800 
5801     /* The desired dimensions are different. Revert the screen. */
5802     if (cn.width >= main_width && cn.height >= main_height) {
5803 	vtrace("  bigger\n");
5804     } else {
5805 	vtrace("  smaller\n");
5806     }
5807     revert_screen();
5808 
5809 done:
5810     if (needs_moving && !iconic) {
5811 	keypad_move();
5812 	popups_move();
5813     }
5814 }
5815 
5816 void
PA_ConfigureNotify_xaction(Widget w _is_unused,XEvent * event,String * params _is_unused,Cardinal * num_params _is_unused)5817 PA_ConfigureNotify_xaction(Widget w _is_unused, XEvent *event,
5818 	String *params _is_unused, Cardinal *num_params _is_unused)
5819 {
5820     XConfigureEvent *re = (XConfigureEvent *) event;
5821     Position xx, yy;
5822 
5823 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
5824     xaction_debug(PA_ConfigureNotify_xaction, event, params, num_params);
5825 #endif /*]*/
5826 
5827     /*
5828      * Get the new window coordinates.  If the configure event reports it
5829      * as (0,0), ask for it explicitly.
5830      */
5831     if (re->x || re->y) {
5832 	xx = re->x;
5833 	yy = re->y;
5834     } else {
5835 	XtVaGetValues(toplevel, XtNx, &xx, XtNy, &yy, NULL);
5836     }
5837     vtrace("ConfigureNotify %dx%d+%hd+%hd\n", re->width, re->height, xx, yy);
5838 
5839     /* Save the latest values. */
5840     cn.x = xx;
5841     cn.y = yy;
5842     cn.width = re->width;
5843     cn.height = re->height;
5844 
5845     /* See if we're maximized. */
5846     query_window_state();
5847     if (user_resize_allowed) {
5848 	/* Take the current dimensions as fixed. */
5849 	vtrace("setting fixed_width and fixed_height\n");
5850 	fixed_width = cn.width;
5851 	fixed_height = cn.height;
5852     }
5853 
5854     /* Set the stream timer for 0.5 sec from now. */
5855     if (cn.ticking) {
5856 	XtRemoveTimeOut(cn.id);
5857     }
5858     cn.id = XtAppAddTimeOut(appcontext, 500, stream_end, 0);
5859     cn.ticking = true;
5860 }
5861 
5862 /*
5863  * Process a VisibilityNotify event, setting the 'visibile' flag in nss.
5864  * This will switch the behavior of screen scrolling.
5865  */
5866 void
PA_VisibilityNotify_xaction(Widget w _is_unused,XEvent * event _is_unused,String * params _is_unused,Cardinal * num_params _is_unused)5867 PA_VisibilityNotify_xaction(Widget w _is_unused, XEvent *event _is_unused,
5868 	String *params _is_unused, Cardinal *num_params _is_unused)
5869 {
5870     XVisibilityEvent *e;
5871 
5872 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
5873     xaction_debug(PA_VisibilityNotify_xaction, event, params, num_params);
5874 #endif /*]*/
5875     e = (XVisibilityEvent *)event;
5876     nss.obscured = (e->state != VisibilityUnobscured);
5877 }
5878 
5879 /*
5880  * Process a GraphicsExpose event, refreshing the screen if we have done
5881  * one or more failed XCopyArea calls.
5882  */
5883 void
PA_GraphicsExpose_xaction(Widget w _is_unused,XEvent * event _is_unused,String * params _is_unused,Cardinal * num_params _is_unused)5884 PA_GraphicsExpose_xaction(Widget w _is_unused, XEvent *event _is_unused,
5885 	String *params _is_unused, Cardinal *num_params _is_unused)
5886 {
5887     int i;
5888 
5889 #if defined(INTERNAL_ACTION_DEBUG) /*[*/
5890     xaction_debug(PA_GraphicsExpose_xaction, event, params, num_params);
5891 #endif /*]*/
5892 
5893     if (nss.copied) {
5894 	/*
5895 	 * Force a screen redraw.
5896 	 */
5897 	memset((char *) ss->image, 0,
5898 		(maxROWS*maxCOLS) * sizeof(struct sp));
5899 	if (visible_control) {
5900 	    for (i = 0; i < maxROWS*maxCOLS; i++) {
5901 		ss->image[i].u.bits.ec = EBC_space;
5902 	    }
5903 	}
5904 	ctlr_changed(0, ROWS*COLS);
5905 	cursor_changed = true;
5906 
5907 	nss.copied = false;
5908     }
5909 }
5910 
5911 /* Display size functions. */
5912 unsigned
display_width(void)5913 display_width(void)
5914 {
5915     return XDisplayWidth(display, default_screen);
5916 }
5917 
5918 unsigned
display_widthMM(void)5919 display_widthMM(void)
5920 {
5921     return XDisplayWidthMM(display, default_screen);
5922 }
5923 
5924 unsigned
display_height(void)5925 display_height(void)
5926 {
5927     return XDisplayHeight(display, default_screen);
5928 }
5929 
5930 unsigned
display_heightMM(void)5931 display_heightMM(void)
5932 {
5933     return XDisplayHeightMM(display, default_screen);
5934 }
5935 
5936 /* Translate an EBCDIC DBCS character to a display character. */
5937 static void
xlate_dbcs(unsigned char c0,unsigned char c1,XChar2b * r)5938 xlate_dbcs(unsigned char c0, unsigned char c1, XChar2b *r)
5939 {
5940     unsigned long u;
5941     int d;
5942 
5943     /* Translate NULLs to spaces. */
5944     if (c0 == EBC_null && c1 == EBC_null) {
5945 	c0 = EBC_space;
5946 	c1 = EBC_space;
5947     }
5948     /* Then handle special cases. */
5949     if ((c0 < 0x41 && (c0 != EBC_space && c1 != EBC_space)) || c0 == 0xff) {
5950 	/* Junk. */
5951 	r->byte1 = 0;
5952 	r->byte2 = 0;
5953     }
5954     u = ebcdic_dbcs_to_unicode((c0 << 8) | c1, EUO_BLANK_UNDEF);
5955     d = display16_lookup(dbcs_font.d16_ix, u);
5956     if (d >= 0) {
5957 	r->byte1 = (d >> 8) & 0xff;
5958 	r->byte2 = d & 0xff;
5959     } else {
5960 	r->byte1 = 0;
5961 	r->byte2 = 0;
5962     }
5963 
5964 #if defined(_ST) /*[*/
5965     printf("EBC %02x%02x -> X11 font %02x%02x\n", c0, c1, r->byte1, r->byte2);
5966 #endif /*]*/
5967 }
5968 
5969 /* Translate a Unicode character to a display character. */
5970 static void
xlate_dbcs_unicode(ucs4_t ucs,XChar2b * r)5971 xlate_dbcs_unicode(ucs4_t ucs, XChar2b *r)
5972 {
5973     int d = display16_lookup(dbcs_font.d16_ix, ucs);
5974 
5975     if (d >= 0) {
5976 	r->byte1 = (d >> 8) & 0xff;
5977 	r->byte2 = d & 0xff;
5978     } else {
5979 	r->byte1 = 0;
5980 	r->byte2 = 0;
5981     }
5982 
5983 #if defined(_ST) /*[*/
5984     printf("UCS4 %04x -> X11 font %02x%02x\n", ucs4, r->byte1, r->byte2);
5985 #endif /*]*/
5986 }
5987 
5988 static void
destroy_callback_func(XIM current_ic,XPointer client_data,XPointer call_data)5989 destroy_callback_func(XIM current_ic, XPointer client_data, XPointer call_data)
5990 {
5991     ic = NULL;
5992     im = NULL;
5993     ic_focus = 0;
5994 
5995 #if defined(_ST) /*[*/
5996     printf("destroy_callback_func\n");
5997 #endif /*]*/
5998 }
5999 
6000 #define OTS_LEN		(sizeof(PT_OVER_THE_SPOT) - 1)
6001 
6002 static void
im_callback(Display * display,XPointer client_data,XPointer call_data)6003 im_callback(Display *display, XPointer client_data, XPointer call_data)
6004 {
6005     XIMStyles *xim_styles = NULL;
6006     XIMCallback destroy;
6007     int i, j;
6008     XVaNestedList preedit_attr = NULL;
6009     XPoint spot;
6010     XRectangle local_win_rect;
6011     static im_style_t im_styles[] = {
6012 	{ XIMPreeditNothing  | XIMStatusNothing,    PT_ROOT },
6013 	{ XIMPreeditPosition | XIMStatusNothing,    PT_OVER_THE_SPOT },
6014 	{ XIMPreeditArea     | XIMStatusArea,       PT_OFF_THE_SPOT },
6015 	{ XIMPreeditCallbacks| XIMStatusCallbacks,  PT_ON_THE_SPOT },
6016 	{ (XIMStyle)0,                              NULL }
6017     };
6018     char *im_style = (xappres.preedit_type != NULL)?
6019 	strip_whitespace(xappres.preedit_type): PT_OVER_THE_SPOT;
6020     char c;
6021 
6022 #if defined(_ST) /*[*/
6023     printf("im_callback\n");
6024 #endif /*]*/
6025 
6026     if (!strcasecmp(im_style, "None")) {
6027 	return;
6028     }
6029 
6030     /* Parse the offset value for OverTheSpot. */
6031     if (!strncasecmp(im_style, PT_OVER_THE_SPOT, OTS_LEN) &&
6032 	((c = im_style[OTS_LEN]) == '+' ||
6033 	 c == '-')) {
6034 	ovs_offset = atoi(im_style + OTS_LEN);
6035 	im_style = NewString(im_style);
6036 	im_style[OTS_LEN] = '\0';
6037     }
6038 
6039     /* Open connection to IM server. */
6040     if ((im = XOpenIM(display, NULL, NULL, NULL)) == NULL) {
6041 	popup_an_error("XOpenIM failed\nXIM-based input disabled");
6042 	goto error_return;
6043     }
6044 
6045     destroy.callback = (XIMProc)destroy_callback_func;
6046     destroy.client_data = NULL;
6047     XSetIMValues(im, XNDestroyCallback, &destroy, NULL);
6048 
6049     /* Detect the input style supported by XIM server. */
6050     if (XGetIMValues(im, XNQueryInputStyle, &xim_styles, NULL) != NULL ||
6051 	    xim_styles == NULL) {
6052 	popup_an_error("Input method doesn't support any styles\n"
6053 		       "XIM-based input disabled");
6054 	goto error_return;
6055     }
6056     for (i = 0; i < xim_styles->count_styles; i++) {
6057 	for (j = 0; im_styles[j].description != NULL; j++) {
6058 	    if (im_styles[j].style == xim_styles->supported_styles[i]) {
6059 #if defined(_ST) /*[*/
6060 		printf("XIM server supports input_style %s\n",
6061 			im_styles[j].description);
6062 #endif /*]*/
6063 		break;
6064 	    }
6065 	}
6066 #if defined(_ST) /*[*/
6067 	if (im_styles[j].description == NULL) {
6068 	    printf("XIM server supports unknown input style %x\n",
6069 		    (unsigned)(xim_styles->supported_styles[i]));
6070 	}
6071 #endif /*]*/
6072     }
6073 
6074     /* Set my preferred style. */
6075     for (j = 0; im_styles[j].description != NULL; j++) {
6076 	if (!strcasecmp(im_styles[j].description, im_style)) {
6077 	    style = im_styles[j].style;
6078 	    break;
6079 	}
6080     }
6081     if (im_styles[j].description == NULL) {
6082 	popup_an_error("Input style '%s' not supported\n"
6083 		       "XIM-based input disabled", im_style);
6084 	goto error_return;
6085     }
6086 
6087     if (style == (XIMPreeditPosition | XIMStatusNothing)) {
6088 	char *fsname;
6089 	XFontSet fontset;
6090 	char **charset_list;
6091 	int charset_count;
6092 	char *def_string;
6093 
6094 	fsname = xs_buffer("-*-%s,-*-iso8859-1", efont_charset_dbcs);
6095 	for (;;) {
6096 #if defined(_ST) /*[*/
6097 	    printf("trying fsname: %s\n", fsname);
6098 #endif /*]*/
6099 	    fontset = XCreateFontSet(display, fsname, &charset_list,
6100 		    &charset_count, &def_string);
6101 	    if (charset_count || fontset == NULL) {
6102 		if (charset_count > 0) {
6103 		    int i;
6104 
6105 		    for (i = 0; i < charset_count; i++) {
6106 #if defined(_ST) /*[*/
6107 			printf("missing: %s\n", charset_list[0]);
6108 #endif /*]*/
6109 			fsname = xs_buffer("%s,-*-%s", fsname,
6110 				charset_list[i]);
6111 		    }
6112 		    continue;
6113 
6114 		}
6115 		popup_an_error("Cannot create fontset '%s' "
6116 			"for input context\n"
6117 			"XIM-based input disabled",
6118 			fsname);
6119 		goto error_return;
6120 	    } else {
6121 		break;
6122 	    }
6123 	}
6124 
6125 	spot.x = 0;
6126 	spot.y = ovs_offset * nss.char_height;
6127 	local_win_rect.x = 1;
6128 	local_win_rect.y = 1;
6129 	local_win_rect.width  = main_width;
6130 	local_win_rect.height = main_height;
6131 	preedit_attr = XVaCreateNestedList(0, XNArea, &local_win_rect,
6132 		XNSpotLocation, &spot, XNFontSet, fontset, NULL);
6133     }
6134 
6135     /* Create IC. */
6136     ic = XCreateIC(im, XNInputStyle, style, XNClientWindow, nss.window,
6137 	    XNFocusWindow, nss.window,
6138 	    (preedit_attr) ? XNPreeditAttributes : NULL, preedit_attr, NULL);
6139     if (ic == NULL) {
6140 	popup_an_error("Cannot create input context\n"
6141 		       "XIM-based input disabled");
6142 	goto error_return;
6143     }
6144     return;
6145 
6146 error_return:
6147     if (im != NULL) {
6148 	XCloseIM(im);
6149 	im = NULL;
6150 	xim_error = true;
6151     }
6152 }
6153 
6154 static void
cleanup_xim(bool b _is_unused)6155 cleanup_xim(bool b _is_unused)
6156 {
6157     if (ic != NULL) {
6158 	XDestroyIC(ic);
6159     }
6160     if (im != NULL) {
6161 	XCloseIM(im);
6162     }
6163 }
6164 
6165 static void
xim_init(void)6166 xim_init(void)
6167 {
6168     char *buf = "";
6169     static bool xim_initted = false;
6170     char *s;
6171 
6172     if (!dbcs || xim_initted) {
6173 	return;
6174     }
6175 
6176     xim_initted = true;
6177 
6178     s = setlocale(LC_CTYPE, "");
6179     if (s != NULL) {
6180 	s = NewString(s);
6181     }
6182     Replace(locale_name, s);
6183     if (s == NULL) {
6184 	popup_an_error("setlocale(LC_CTYPE) failed\nXIM-based input disabled");
6185 	xim_error = true;
6186 	return;
6187     }
6188 
6189     if (xappres.input_method != NULL) {
6190 	buf = lazyaf("@im=%s", xappres.input_method);
6191     }
6192     if (XSetLocaleModifiers(buf) == NULL) {
6193 	popup_an_error("XSetLocaleModifiers failed\nXIM-based input disabled");
6194 	xim_error = true;
6195     } else if (XRegisterIMInstantiateCallback(display, NULL, NULL, NULL,
6196 		im_callback, NULL) != true) {
6197 	popup_an_error("XRegisterIMInstantiateCallback failed\n"
6198 		"XIM-based input disabled");
6199 	xim_error = true;
6200     }
6201     register_schange(ST_EXITING, cleanup_xim);
6202     return;
6203 }
6204 
6205 static void
send_spot_loc(void)6206 send_spot_loc(void)
6207 {
6208     XPoint spot;
6209     XVaNestedList preedit_attr;
6210 
6211     spot.x = (cursor_addr % COLS) * nss.char_width + hhalo;
6212     spot.y = ((cursor_addr / COLS) + ovs_offset) * nss.char_height + vhalo;
6213     preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
6214     XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL);
6215     XFree(preedit_attr);
6216 }
6217 
6218 /* Change the window title. */
6219 static bool
Title_action(ia_t ia,unsigned argc,const char ** argv)6220 Title_action(ia_t ia, unsigned argc, const char **argv)
6221 {
6222     action_debug(AnTitle, ia, argc, argv);
6223     if (check_argc(AnTitle, argc, 1, 1) < 0) {
6224 	return false;
6225     }
6226 
6227     user_title = NewString(argv[0]);
6228     XtVaSetValues(toplevel, XtNtitle, user_title, NULL);
6229     return true;
6230 }
6231 
6232 /* Change the window state. */
6233 static bool
WindowState_action(ia_t ia,unsigned argc,const char ** argv)6234 WindowState_action(ia_t ia, unsigned argc, const char **argv)
6235 {
6236     int state;
6237 
6238     action_debug(AnWindowState, ia, argc, argv);
6239     if (check_argc(AnWindowState, argc, 1, 1) < 0) {
6240 	return false;
6241     }
6242 
6243     if (!strcasecmp(argv[0], KwIconic)) {
6244 	state = true;
6245     } else if (!strcasecmp(argv[0], KwNormal)) {
6246 	state = false;
6247     } else {
6248 	return action_args_are(AnWindowState, KwIconic, KwNormal, NULL);
6249     }
6250     XtVaSetValues(toplevel, XtNiconic, state, NULL);
6251     return true;
6252 }
6253 
6254 static dfc_t *dfc = NULL, *dfc_last = NULL;
6255 
6256 /* Split a font name into parts. */
6257 static int
split_name(const char * name,char res[15][256])6258 split_name(const char *name, char res[15][256])
6259 {
6260     int ns;
6261     const char *dash;
6262     const char *s;
6263 
6264     ns = 0;
6265     s = name;
6266     while (ns < 14 && ((dash = strchr(s, '-')) != NULL)) {
6267 	int nc = dash - s;
6268 
6269 	if (nc >= 256) {
6270 	    nc = 255;
6271 	}
6272 	strncpy(res[ns], s, nc);
6273 	res[ns][nc] = '\0';
6274 	ns++;
6275 	s = dash + 1;
6276     }
6277     if (*s) {
6278 	int nc = strlen(s);
6279 
6280 	if (nc >= 256) {
6281 	    nc = 255;
6282 	}
6283 	strncpy(res[ns], s, nc);
6284 	res[ns][nc] = '\0';
6285 	ns++;
6286     }
6287 
6288     return ns;
6289 }
6290 
6291 /* Initialize the dumb font cache. */
6292 static void
dfc_init(void)6293 dfc_init(void)
6294 {
6295     char **namelist;
6296     int count;
6297     int i;
6298     dfc_t *d, *e;
6299     char nl_arr[15][256];
6300     dfc_t *c_first = NULL;
6301     dfc_t *c_last = NULL;
6302     dfc_t *m_first = NULL;
6303     dfc_t *m_last = NULL;
6304 
6305     /* Get all of the font names. */
6306     namelist = XListFonts(display, "*", MAX_FONTS, &count);
6307     if (namelist == NULL) {
6308 	Error("No fonts");
6309     }
6310     for (i = 0; i < count; i++) {
6311 	/* Pick apart the font names. */
6312 	int nf = split_name(namelist[i], nl_arr);
6313 	int good = true;
6314 
6315 	if ((nf == 1 && strncmp(nl_arr[0], "3270", 4)) ||
6316 	    (nf != 15) ||
6317 	    (strcasecmp(nl_arr[4], "r") ||
6318 	     !strcmp(nl_arr[7], "0") ||
6319 	     !strcmp(nl_arr[8], "0") ||
6320 	     (strcasecmp(nl_arr[11], "c") &&
6321 	      strcasecmp(nl_arr[11], "m")) ||
6322 	     !strcmp(nl_arr[12], "0"))) {
6323 	    good = false;
6324 	}
6325 
6326 	/* Make sure it isn't a dup. */
6327 	for (e = dfc; e != NULL; e = e->next) {
6328 	    if (!strcasecmp(namelist[i], e->name)) {
6329 		break;
6330 	    }
6331 	}
6332 	if (e != NULL) {
6333 	    continue;
6334 	}
6335 
6336 	/* Append this entry to the cache. */
6337 	d = (dfc_t *)Malloc(sizeof(dfc_t));
6338 	d->next = NULL;
6339 	d->name = NewString(namelist[i]);
6340 	d->weight = NewString(nl_arr[3]);
6341 	d->points = atoi(nl_arr[7]);
6342 	d->spacing = NewString(nl_arr[11]);
6343 	d->charset = xs_buffer("%s-%s", nl_arr[13], nl_arr[14]);
6344 	d->good = good;
6345 	if (!d->spacing[0] || !strcasecmp(d->spacing, "c")) {
6346 	    if (c_last) {
6347 		c_last->next = d;
6348 	    } else {
6349 		c_first = d;
6350 	    }
6351 	    c_last = d;
6352 	} else {
6353 	    if (m_last) {
6354 		m_last->next = d;
6355 	    } else {
6356 		m_first = d;
6357 	    }
6358 	    m_last = d;
6359 	}
6360     }
6361 
6362     if (c_first != NULL) {
6363 	c_last->next = m_first;
6364 	dfc = c_first;
6365 	if (m_last != NULL) {
6366 	    dfc_last = m_last;
6367 	} else {
6368 	    dfc_last = c_last;
6369 	}
6370     } else {
6371 	dfc = m_first;
6372 	dfc_last = m_last;
6373     }
6374 }
6375 
6376 /* Search iteratively for fonts whose names specify a given character set. */
6377 static const char *
dfc_search_family(const char * charset,dfc_t ** dp,void ** cookie)6378 dfc_search_family(const char *charset, dfc_t **dp, void **cookie)
6379 {
6380     dfc_t *d;
6381 
6382     if (*cookie == NULL) {
6383 	d = dfc;
6384     } else {
6385 	d = ((dfc_t *)*cookie)->next;
6386 	if (d == NULL) {
6387 	    if (dp) {
6388 		*dp = NULL;
6389 	    }
6390 	    *cookie = NULL;
6391 	    return NULL;
6392 	}
6393     }
6394     while (d != NULL) {
6395 	if (d->good && !strcasecmp(charset, d->charset)) {
6396 	    if (dp) {
6397 		*dp = d;
6398 	    }
6399 	    *cookie = d;
6400 	    return d->name;
6401 	}
6402 	d = d->next;
6403     }
6404     *cookie = NULL;
6405     return NULL;
6406 }
6407 
6408 /* Return the window for the screen. */
6409 unsigned long
screen_window_number(void)6410 screen_window_number(void)
6411 {
6412     return XtWindow(toplevel);
6413 }
6414 
6415 /**
6416  * External interface to the SELECTED macro.
6417  *
6418  * @param[in] baddr	Buffer address.
6419  *
6420  * @return true if cell is selected
6421  */
6422 bool
screen_selected(int baddr)6423 screen_selected(int baddr)
6424 {
6425     return SELECTED(baddr) != 0;
6426 }
6427 
6428 /**
6429  * External interface to the SET_SELECT macro.
6430  *
6431  * @param[in] baddr	Buffer address.
6432  */
6433 void
screen_set_select(int baddr)6434 screen_set_select(int baddr)
6435 {
6436     SET_SELECT(baddr);
6437 }
6438 
6439 /**
6440  * Unselect everything.
6441  */
6442 void
screen_unselect_all(void)6443 screen_unselect_all(void)
6444 {
6445     memset((char *)selected, 0, (ROWS*COLS + 7) / 8);
6446 }
6447 
6448 /**
6449  * Does this display support background color? (No.)
6450  *
6451  * @return true if supported, false if not.
6452  */
6453 bool
screen_has_bg_color(void)6454 screen_has_bg_color(void)
6455 {
6456     return false;
6457 }
6458 
6459 /**
6460  * Snap the screen to the current size.
6461  */
6462 void
screen_snap_size(void)6463 screen_snap_size(void)
6464 {
6465     if (!user_resize_allowed)
6466     {
6467 	return;
6468     }
6469     clear_fixed();
6470     screen_reinit(FONT_CHANGE);
6471 }
6472 
6473 /* State change handler for host code pages. */
6474 static void
screen_codepage_changed(bool ignored _is_unused)6475 screen_codepage_changed(bool ignored _is_unused)
6476 {
6477     screen_reinit(CODEPAGE_CHANGE | FONT_CHANGE);
6478 }
6479 
6480 /**
6481  * Screen module registration.
6482  */
6483 void
screen_register(void)6484 screen_register(void)
6485 {
6486     static toggle_register_t toggles[] = {
6487 	{ MONOCASE,		toggle_monocase,	0 },
6488 	{ ALT_CURSOR,		toggle_altCursor,	0 },
6489 	{ CURSOR_BLINK,		toggle_cursorBlink,	0 },
6490 	{ SHOW_TIMING,		toggle_showTiming,	0 },
6491 	{ CROSSHAIR,		toggle_crosshair,	0 },
6492 	{ VISIBLE_CONTROL,	toggle_visible_control, 0 },
6493 	{ SCROLL_BAR,		toggle_scrollBar,	0 },
6494 	{ MARGINED_PASTE,	NULL,			0 },
6495 	{ OVERLAY_PASTE,	NULL,			0 },
6496 	{ TYPEAHEAD,		NULL,			0 },
6497 	{ APL_MODE,		toggle_aplMode,		0 }
6498     };
6499     static action_table_t screen_actions[] = {
6500 	{ AnSetFont,		SetFont_action,		ACTION_KE },
6501 	{ AnTitle,		Title_action,		ACTION_KE },
6502 	{ AnWindowState,	WindowState_action,	ACTION_KE }
6503     };
6504 
6505     /* Register our toggles. */
6506     register_toggles(toggles, array_count(toggles));
6507 
6508     /* Register our actions. */
6509     register_actions(screen_actions, array_count(screen_actions));
6510 
6511     /* Register state change callbacks. */
6512     register_schange(ST_CONNECT, screen_connect);
6513     register_schange(ST_3270_MODE, screen_connect);
6514     register_schange(ST_CODEPAGE, screen_codepage_changed);
6515 }
6516