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