1 /*	SCCS Id: @(#)winmap.c	3.3	96/04/05	*/
2 /* Copyright (c) Dean Luick, 1992				  */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /*
6  * This file contains:
7  *	+ global functions print_glyph() and cliparound()
8  *	+ the map window routines
9  *	+ the char and pointer input routines
10  *
11  * Notes:
12  *	+ We don't really have a good way to get the compiled ROWNO and
13  *	  COLNO as defaults.  They are hardwired to the current "correct"
14  *	  values in the Window widget.  I am _not_ in favor of including
15  *	  some nethack include file for Window.c.
16  */
17 
18 #ifndef SYSV
19 #define PRESERVE_NO_SYSV	/* X11 include files may define SYSV */
20 #endif
21 
22 #include <X11/Intrinsic.h>
23 #include <X11/StringDefs.h>
24 #include <X11/Shell.h>
25 #include <X11/Xaw/Cardinals.h>
26 #include <X11/Xaw/Scrollbar.h>
27 #include <X11/Xaw/Viewport.h>
28 #include <X11/Xatom.h>
29 
30 #ifdef PRESERVE_NO_SYSV
31 # ifdef SYSV
32 #  undef SYSV
33 # endif
34 # undef PRESERVE_NO_SYSV
35 #endif
36 
37 #include "xwindow.h"	/* map widget declarations */
38 
39 #include "hack.h"
40 #include "dlb.h"
41 #include "winX.h"
42 
43 #ifdef USE_XPM
44 #include <X11/xpm.h>
45 #endif
46 
47 
48 /* from tile.c */
49 extern short glyph2tile[];
50 extern int total_tiles_used;
51 
52 /* Define these if you really want a lot of junk on your screen. */
53 /* #define VERBOSE */		/* print various info & events as they happen */
54 /* #define VERBOSE_UPDATE */	/* print screen update bounds */
55 /* #define VERBOSE_INPUT */	/* print input events */
56 
57 
58 #define USE_WHITE	/* almost always use white as a tile cursor border */
59 
60 
61 static boolean FDECL(init_tiles, (struct xwindow *));
62 static void FDECL(set_button_values, (Widget,int,int,unsigned));
63 static void FDECL(map_check_size_change, (struct xwindow *));
64 static void FDECL(map_update, (struct xwindow *,int,int,int,int,BOOLEAN_P));
65 static void FDECL(init_text, (struct xwindow *));
66 static void FDECL(map_exposed, (Widget,XtPointer,XtPointer));
67 static void FDECL(set_gc, (Widget,Font,char *,Pixel,GC *,GC *));
68 static void FDECL(get_text_gc, (struct xwindow *,Font));
69 static void FDECL(get_char_info, (struct xwindow *));
70 static void FDECL(display_cursor, (struct xwindow *));
71 
72 /* Global functions ======================================================== */
73 
74 void
X11_print_glyph(window,x,y,glyph)75 X11_print_glyph(window, x, y, glyph)
76     winid window;
77     xchar x, y;
78     int glyph;
79 {
80     struct map_info_t *map_info;
81     boolean update_bbox;
82 
83     check_winid(window);
84     if (window_list[window].type != NHW_MAP) {
85 	impossible("print_glyph: can (currently) only print to map windows");
86 	return;
87     }
88     map_info = window_list[window].map_information;
89 
90     if (map_info->is_tile) {
91 	unsigned short *t_ptr;
92 
93 	t_ptr = &map_info->mtype.tile_map->glyphs[y][x];
94 
95 	if (*t_ptr != glyph) {
96 	    *t_ptr = glyph;
97 	    update_bbox = TRUE;
98 	} else
99 	    update_bbox = FALSE;
100 
101     } else {
102 	uchar			ch;
103 	register int		offset;
104 	register unsigned char *ch_ptr;
105 #ifdef TEXTCOLOR
106 	int			color;
107 	register unsigned char *co_ptr;
108 
109 #define zap_color(n)  color = zapcolors[n]
110 #define cmap_color(n) color = defsyms[n].color
111 #define obj_color(n)  color = objects[n].oc_color
112 #define mon_color(n)  color = mons[n].mcolor
113 #define invis_color(n) color = NO_COLOR
114 #define pet_color(n)  color = mons[n].mcolor
115 #define warn_color(n) color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR
116 # else /* no text color */
117 
118 #define zap_color(n)
119 #define cmap_color(n)
120 #define obj_color(n)
121 #define mon_color(n)
122 #define invis_color(n)
123 #define pet_color(n)
124 #define warn_color(n)
125 
126 #endif
127 
128 	/*
129 	 *  Map the glyph back to a character.
130 	 *
131 	 *  Warning:  For speed, this makes an assumption on the order of
132 	 *            offsets.  The order is set in display.h.
133 	 */
134 	if ((offset = (glyph - GLYPH_WARNING_OFF)) >= 0) { 	/* a warning flash */
135 		ch = warnsyms[offset];
136 		warn_color(offset);
137 	} else if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {	/* swallow */
138 	    /* see swallow_to_glyph() in display.c */
139 	    ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)];
140 	    mon_color(offset >> 3);
141 	} else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) {	/* zap beam */
142 	    /* see zapdir_to_glyph() in display.c */
143 	    ch = showsyms[S_vbeam + (offset & 0x3)];
144 	    zap_color((offset >> 2));
145 	} else if ((offset = (glyph - GLYPH_CMAP_OFF)) >= 0) {	/* cmap */
146 	    ch = showsyms[offset];
147 	    cmap_color(offset);
148 	} else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) {	/* object */
149 	    ch = oc_syms[(int) objects[offset].oc_class];
150 	    obj_color(offset);
151 	} else if ((offset = (glyph - GLYPH_RIDDEN_OFF)) >= 0) {/* ridden mon */
152 	    ch = monsyms[(int) mons[offset].mlet];
153 	    mon_color(offset);
154 	} else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) {	/* a corpse */
155 	    ch = oc_syms[(int) objects[CORPSE].oc_class];
156 	    mon_color(offset);
157 	} else if ((offset = (glyph - GLYPH_DETECT_OFF)) >= 0) {
158 	    /* monster detection; should really be inverse */
159 	    ch = monsyms[(int) mons[offset].mlet];
160 	    mon_color(offset);
161 	} else if ((offset = (glyph - GLYPH_INVIS_OFF)) >= 0) {	/* invisible */
162 	    ch = DEF_INVISIBLE;
163 	    invis_color(offset);
164 	} else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) {	/* a pet */
165 	    ch = monsyms[(int) mons[offset].mlet];
166 	    pet_color(offset);
167 	} else {						/* a monster */
168 	    ch = monsyms[(int) mons[glyph].mlet];
169 	    mon_color(glyph);
170 	}
171 
172 	/* Only update if we need to. */
173 	ch_ptr = &map_info->mtype.text_map->text[y][x];
174 
175 #ifdef TEXTCOLOR
176 	co_ptr = &map_info->mtype.text_map->colors[y][x];
177 	if (*ch_ptr != ch || *co_ptr != color)
178 #else
179 	if (*ch_ptr != ch)
180 #endif
181 	{
182 	    *ch_ptr = ch;
183 #ifdef TEXTCOLOR
184 	    *co_ptr = color;
185 #endif
186 	    update_bbox = TRUE;
187 	} else
188 	    update_bbox = FALSE;
189 
190 #undef zap_color
191 #undef cmap_color
192 #undef obj_color
193 #undef mon_color
194 #undef pet_color
195     }
196 
197     if (update_bbox) {		/* update row bbox */
198 	if ((uchar) x < map_info->t_start[y]) map_info->t_start[y] = x;
199 	if ((uchar) x > map_info->t_stop[y])  map_info->t_stop[y]  = x;
200     }
201 }
202 
203 #ifdef CLIPPING
204 /*
205  * The is the tty clip call.  Since X can resize at any time, we can't depend
206  * on this being defined.
207  */
208 /*ARGSUSED*/
X11_cliparound(x,y)209 void X11_cliparound(x, y) int x, y; { }
210 #endif /* CLIPPING */
211 
212 /* End global functions ==================================================== */
213 
214 #include "tile2x11.h"
215 
216 /*
217  * We're expecting to never read more than one tile file per session.
218  * If this is false, then we can make an array of this information,
219  * or just keep it on a per-window basis.
220  */
221 Pixmap tile_pixmap = None;
222 static int tile_width;
223 static int tile_height;
224 static int tile_count;
225 static XImage *tile_image = 0;
226 
227 /*
228  * This structure is used for small bitmaps that are used for annotating
229  * tiles.  For example, a "heart" annotates pets.
230  */
231 struct tile_annotation {
232     Pixmap bitmap;
233     Pixel foreground;
234     unsigned int width, height;
235     int hotx, hoty; /* not currently used */
236 };
237 
238 static struct tile_annotation pet_annotation;
239 
240 static void
init_annotation(annotation,filename,colorpixel)241 init_annotation(annotation, filename, colorpixel)
242 struct tile_annotation *annotation;
243 char *filename;
244 Pixel colorpixel;
245 {
246     Display *dpy = XtDisplay(toplevel);
247 
248     if (0!=XReadBitmapFile(dpy, XtWindow(toplevel), filename,
249 	    &annotation->width, &annotation->height, &annotation->bitmap,
250 	    &annotation->hotx, &annotation->hoty)) {
251 	char buf[BUFSZ];
252 	Sprintf(buf, "Failed to load %s", filename);
253 	X11_raw_print(buf);
254     }
255 
256     annotation->foreground = colorpixel;
257 }
258 
259 /*
260  * Put the tile image on the server.
261  *
262  * We can't send the image to the server until the top level
263  * is realized.  When the tile file is first processed, the top
264  * level is not realized.  This routine is called after we
265  * realize the top level, but before we start resizing the
266  * map viewport.
267  */
268 void
post_process_tiles()269 post_process_tiles()
270 {
271     Display *dpy = XtDisplay(toplevel);
272     unsigned int width, height;
273 
274     height = tile_height * tile_count;
275     width  = tile_width;
276 
277     if (tile_image == 0) return;	/* no tiles */
278 
279     tile_pixmap = XCreatePixmap(dpy, XtWindow(toplevel),
280 			width,
281     			height,
282 			DefaultDepth(dpy, DefaultScreen(dpy)));
283 
284     XPutImage(dpy, tile_pixmap,
285 	DefaultGC(dpy, DefaultScreen(dpy)),
286 	tile_image,
287 	0,0, 0,0,		/* src, dest top left */
288 	width,
289 	height);
290 
291     XDestroyImage(tile_image);	/* data bytes free'd also */
292     tile_image = 0;
293 
294     init_annotation(&pet_annotation,
295 	appResources.pet_mark_bitmap, appResources.pet_mark_color);
296 }
297 
298 
299 /*
300  * Open and read the tile file.  Return TRUE if there were no problems.
301  * Return FALSE otherwise.
302  */
303 static boolean
init_tiles(wp)304 init_tiles(wp)
305     struct xwindow *wp;
306 {
307 #ifndef USE_XPM
308     FILE *fp = (FILE *)0;
309     x11_header header;
310     unsigned char *cp, *colormap = (unsigned char *)0;
311     unsigned char *tb, *tile_bytes = (unsigned char *)0;
312     int size;
313     XColor *colors = (XColor *)0;
314     int i, x, y;
315     int bitmap_pad;
316     unsigned int image_height, image_width;
317     int ddepth;
318 #endif
319     Display *dpy = XtDisplay(toplevel);
320     Screen *screen = DefaultScreenOfDisplay(dpy);
321     struct map_info_t *map_info = (struct map_info_t *)0;
322     struct tile_map_info_t *tile_info = (struct tile_map_info_t *)0;
323     boolean result = TRUE;
324     XGCValues values;
325     XtGCMask mask;
326 
327     /* already have tile information */
328     if (tile_pixmap != None) goto tiledone;
329 
330     map_info = wp->map_information;
331     tile_info = map_info->mtype.tile_map =
332 	    (struct tile_map_info_t *) alloc(sizeof(struct tile_map_info_t));
333     (void) memset((genericptr_t) tile_info, 0,
334 				sizeof(struct tile_map_info_t));
335 
336 #ifdef USE_XPM
337     {
338 	char buf[BUFSZ];
339 	XpmAttributes attributes;
340 	int errorcode;
341 
342 	attributes.valuemask = XpmCloseness;
343 	attributes.closeness = 25000;
344 
345 	errorcode=XpmReadFileToImage(dpy,appResources.tile_file,&tile_image,0,&attributes);
346 
347 	if (errorcode == XpmColorFailed) {
348 	    Sprintf(buf, "Insufficient colors available to load %s.",appResources.tile_file);
349 	    X11_raw_print(buf);
350 	    X11_raw_print("Try closing other colorful applications and restart.");
351 	    X11_raw_print("Attempting to load with inferior colors.");
352 	    attributes.closeness = 50000;
353 	    errorcode=XpmReadFileToImage(dpy,appResources.tile_file,&tile_image,0,&attributes);
354 	}
355 
356 	if (errorcode!=XpmSuccess) {
357 	    if (errorcode == XpmColorFailed) {
358 		Sprintf(buf, "Insufficient colors available to load %s.",appResources.tile_file);
359 		X11_raw_print(buf);
360 	    } else {
361 		Sprintf(buf, "Failed to load %s: %s",appResources.tile_file,
362 			XpmGetErrorString(errorcode));
363 		X11_raw_print(buf);
364 	    }
365 	    result = FALSE;
366 	    X11_raw_print("Switching to text-based mode.");
367 	    goto tiledone;
368 	}
369 
370 	if (tile_image->height%total_tiles_used != 0) {
371 	    Sprintf(buf,
372 		"%s is not a multiple of %d (the number of tiles) pixels high",
373 		appResources.tile_file, total_tiles_used);
374 	    X11_raw_print(buf);
375 	    XDestroyImage(tile_image);
376 	    tile_image = 0;
377 	    result = FALSE;
378 	    goto tiledone;
379 	}
380 
381 	/* infer tile dimensions from image size */
382 	tile_count=total_tiles_used;
383 	tile_width=tile_image->width;
384 	tile_height=tile_image->height/tile_count;
385     }
386 #else
387     /* any less than 16 colours makes tiles useless */
388     ddepth = DefaultDepthOfScreen(screen);
389     if (ddepth < 4) {
390 	X11_raw_print("need a screen depth of at least 4");
391 	result = FALSE;
392 	goto tiledone;
393     }
394 
395     fp = fopen_datafile(appResources.tile_file, RDBMODE, FALSE);
396     if (!fp) {
397 	X11_raw_print("can't open tile file");
398 	result = FALSE;
399 	goto tiledone;
400     }
401 
402     if (fread((char *) &header, sizeof(header), 1, fp) != 1) {
403 	X11_raw_print("read of header failed");
404 	result = FALSE;
405 	goto tiledone;
406     }
407 
408 # ifdef VERBOSE
409     fprintf(stderr, "X11 tile file:\n    version %ld\n    ncolors %ld\n    tile width %ld\n    tile height %ld\n    ntiles %ld\n",
410 	header.version,
411 	header.ncolors,
412 	header.tile_width,
413 	header.tile_height,
414 	header.ntiles);
415 # endif
416 
417     size = 3*header.ncolors;
418     colormap = (unsigned char *) alloc((unsigned)size);
419     if (fread((char *) colormap, 1, size, fp) != size) {
420 	X11_raw_print("read of colormap failed");
421 	result = FALSE;
422 	goto tiledone;
423     }
424 
425 /* defined in decl.h - these are _not_ good defines to have */
426 #undef red
427 #undef green
428 #undef blue
429 
430     colors = (XColor *) alloc(sizeof(XColor) * (unsigned)header.ncolors);
431     for (i = 0; i < header.ncolors; i++) {
432 	cp = colormap + (3 * i);
433 	colors[i].red   = cp[0] * 256;
434 	colors[i].green = cp[1] * 256;
435 	colors[i].blue  = cp[2] * 256;
436 	colors[i].flags = 0;
437 	colors[i].pixel = 0;
438 
439 	if (!XAllocColor(dpy, DefaultColormapOfScreen(screen), &colors[i]) &&
440 	    !nhApproxColor(screen, DefaultColormapOfScreen(screen),
441 			   (char *)0, &colors[i])) {
442 	    char buf[BUFSZ];
443 	    Sprintf(buf, "%dth out of %ld color allocation failed",
444 		i, header.ncolors);
445 	    X11_raw_print(buf);
446 	    result = FALSE;
447 	    goto tiledone;
448 	}
449     }
450 
451     size = header.tile_height * header.tile_width;
452     /*
453      * This alloc() and the one below require 32-bit ints, since tile_bytes
454      * is currently ~200k and alloc() takes an int
455      */
456     tile_bytes = (unsigned char *) alloc((unsigned)header.ntiles*size);
457     tile_count = header.ntiles;
458     if (fread((char *) tile_bytes, size, tile_count, fp) != tile_count) {
459 	X11_raw_print("read of tile bytes failed");
460 	result = FALSE;
461 	goto tiledone;
462     }
463 
464     if (header.ntiles < total_tiles_used) {
465 	char buf[BUFSZ];
466 	Sprintf(buf, "tile file incomplete, expecting %d tiles, found %lu",
467 		total_tiles_used, header.ntiles);
468 	X11_raw_print(buf);
469 	result = FALSE;
470 	goto tiledone;
471     }
472 
473 
474     if (appResources.double_tile_size) {
475 	tile_width  = 2*header.tile_width;
476 	tile_height = 2*header.tile_height;
477     } else {
478 	tile_width  = header.tile_width;
479 	tile_height = header.tile_height;
480     }
481 
482     image_height = tile_height * tile_count;
483     image_width  = tile_width;
484 
485     /* calculate bitmap_pad */
486     if (ddepth > 16)
487 	bitmap_pad = 32;
488     else if (ddepth > 8)
489 	bitmap_pad = 16;
490     else
491 	bitmap_pad = 8;
492 
493     tile_image = XCreateImage(dpy, DefaultVisualOfScreen(screen),
494 		ddepth,			/* depth */
495 		ZPixmap,		/* format */
496 		0,			/* offset */
497 		0,			/* data */
498 		image_width,		/* width */
499 		image_height,		/* height */
500 		bitmap_pad,		/* bit pad */
501 		0);			/* bytes_per_line */
502 
503     if (!tile_image)
504 	impossible("init_tiles: insufficient memory to create image");
505 
506     /* now we know the physical memory requirements, we can allocate space */
507     tile_image->data =
508 	(char *) alloc((unsigned)tile_image->bytes_per_line * image_height);
509 
510     if (appResources.double_tile_size) {
511 	unsigned long *expanded_row =
512 	    (unsigned long *)alloc(sizeof(unsigned long)*(unsigned)tile_width);
513 
514 	tb = tile_bytes;
515 	for (y = 0; y < image_height; y++) {
516 	    for (x = 0; x < header.tile_width; x++)
517 		expanded_row[2*x] =
518 			    expanded_row[(2*x)+1] = colors[*tb++].pixel;
519 
520 	    for (x = 0; x < tile_width; x++)
521 		XPutPixel(tile_image, x, y, expanded_row[x]);
522 
523 	    y++;	/* duplicate row */
524 	    for (x = 0; x < tile_width; x++)
525 		XPutPixel(tile_image, x, y, expanded_row[x]);
526 	}
527 	free((genericptr_t)expanded_row);
528 
529     } else {
530 
531 	for (tb = tile_bytes, y = 0; y < image_height; y++)
532 	    for (x = 0; x < image_width; x++, tb++)
533 		XPutPixel(tile_image, x, y, colors[*tb].pixel);
534     }
535 #endif /* USE_XPM */
536 
537     /* fake an inverted tile by drawing a border around the edges */
538 #ifdef USE_WHITE
539     /* use white or black as the border */
540     mask = GCFunction | GCForeground | GCGraphicsExposures;
541     values.graphics_exposures = False;
542     values.foreground = WhitePixelOfScreen(screen);
543     values.function   = GXcopy;
544     tile_info->white_gc = XtGetGC(wp->w, mask, &values);
545     values.graphics_exposures = False;
546     values.foreground = BlackPixelOfScreen(screen);
547     values.function   = GXcopy;
548     tile_info->black_gc = XtGetGC(wp->w, mask, &values);
549 #else
550     /*
551      * Use xor so we don't have to check for special colors.  Xor white
552      * against the upper left pixel of the corridor so that we have a
553      * white rectangle when in a corridor.
554      */
555     mask = GCFunction | GCForeground | GCGraphicsExposures;
556     values.graphics_exposures = False;
557 #if 1
558     values.foreground = WhitePixelOfScreen(screen) ^
559 	XGetPixel(tile_image, 0, tile_height*glyph2tile[cmap_to_glyph(S_corr)]);
560 #else
561     values.foreground = ~((unsigned long) 0);
562 #endif
563     values.function = GXxor;
564     tile_info->white_gc = XtGetGC(wp->w, mask, &values);
565 
566     mask = GCFunction | GCGraphicsExposures;
567     values.function = GXCopy;
568     values.graphics_exposures = False;
569     tile_info->black_gc = XtGetGC(wp->w, mask, &values);
570 #endif
571 
572 tiledone:
573 #ifndef USE_XPM
574     if (fp) (void) fclose(fp);
575     if (colormap) free((genericptr_t)colormap);
576     if (tile_bytes) free((genericptr_t)tile_bytes);
577     if (colors) free((genericptr_t)colors);
578 #endif
579 
580     if (result) {				/* succeeded */
581 	map_info->square_height = tile_height;
582 	map_info->square_width = tile_width;
583 	map_info->square_ascent = 0;
584 	map_info->square_lbearing = 0;
585     } else {
586 	if (tile_info) free((genericptr_t)tile_info);
587 	tile_info = 0;
588     }
589 
590     return result;
591 }
592 
593 
594 /*
595  * Make sure the map's cursor is always visible.
596  */
597 void
check_cursor_visibility(wp)598 check_cursor_visibility(wp)
599     struct xwindow *wp;
600 {
601     Arg arg[2];
602     Widget viewport, horiz_sb, vert_sb;
603     float top, shown, cursor_middle;
604     Boolean do_call, adjusted = False;
605 #ifdef VERBOSE
606     char *s;
607 #endif
608 
609     viewport = XtParent(wp->w);
610     horiz_sb = XtNameToWidget(viewport, "horizontal");
611     vert_sb  = XtNameToWidget(viewport, "vertical");
612 
613 /* All values are relative to currently visible area */
614 
615 #define V_BORDER 0.3	/* if this far from vert edge, shift */
616 #define H_BORDER 0.3	/* if this from from horiz edge, shift */
617 
618 #define H_DELTA 0.4	/* distance of horiz shift */
619 #define V_DELTA 0.4	/* distance of vert shift */
620 
621     if (horiz_sb) {
622 	XtSetArg(arg[0], XtNshown,	&shown);
623 	XtSetArg(arg[1], XtNtopOfThumb, &top);
624 	XtGetValues(horiz_sb, arg, TWO);
625 
626 	cursor_middle = (((float) wp->cursx) + 0.5) / (float) COLNO;
627 	do_call = True;
628 
629 #ifdef VERBOSE
630 	if (cursor_middle < top) {
631 	    s = " outside left";
632 	} else if (cursor_middle < top + shown*H_BORDER) {
633 	    s = " close to left";
634 	} else if (cursor_middle > (top + shown)) {
635 	    s = " outside right";
636 	} else if (cursor_middle > (top + shown - shown*H_BORDER)) {
637 	    s = " close to right";
638 	} else {
639 	    s = "";
640 	}
641 	printf("Horiz: shown = %3.2f, top = %3.2f%s", shown, top, s);
642 #endif
643 
644 	if (cursor_middle < top) {
645 	    top = cursor_middle - shown*H_DELTA;
646 	    if (top < 0.0) top = 0.0;
647 	} else if (cursor_middle < top + shown*H_BORDER) {
648 	    top -= shown*H_DELTA;
649 	    if (top < 0.0) top = 0.0;
650 	} else if (cursor_middle > (top + shown)) {
651 	    top = cursor_middle - shown*H_DELTA;
652 	    if (top < 0.0) top = 0.0;
653 	    if (top + shown > 1.0) top = 1.0 - shown;
654 	} else if (cursor_middle > (top + shown - shown*H_BORDER)) {
655 	    top += shown*H_DELTA;
656 	    if (top + shown > 1.0) top = 1.0 - shown;
657 	} else {
658 	    do_call = False;
659 	}
660 
661 	if (do_call) {
662 	    XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
663 	    adjusted = True;
664 	}
665     }
666 
667     if (vert_sb) {
668 	XtSetArg(arg[0], XtNshown,      &shown);
669 	XtSetArg(arg[1], XtNtopOfThumb, &top);
670 	XtGetValues(vert_sb, arg, TWO);
671 
672 	cursor_middle = (((float) wp->cursy) + 0.5) / (float) ROWNO;
673 	do_call = True;
674 
675 #ifdef VERBOSE
676 	if (cursor_middle < top) {
677 	    s = " above top";
678 	} else if (cursor_middle < top + shown*V_BORDER) {
679 	    s = " close to top";
680 	} else if (cursor_middle > (top + shown)) {
681 	    s = " below bottom";
682 	} else if (cursor_middle > (top + shown - shown*V_BORDER)) {
683 	    s = " close to bottom";
684 	} else {
685 	    s = "";
686 	}
687 	printf("%sVert: shown = %3.2f, top = %3.2f%s",
688 				    horiz_sb ? ";  " : "", shown, top, s);
689 #endif
690 
691 	if (cursor_middle < top) {
692 	    top = cursor_middle - shown*V_DELTA;
693 	    if (top < 0.0) top = 0.0;
694 	} else if (cursor_middle < top + shown*V_BORDER) {
695 	    top -= shown*V_DELTA;
696 	    if (top < 0.0) top = 0.0;
697 	} else if (cursor_middle > (top + shown)) {
698 	    top = cursor_middle - shown*V_DELTA;
699 	    if (top < 0.0) top = 0.0;
700 	    if (top + shown > 1.0) top = 1.0 - shown;
701 	} else if (cursor_middle > (top + shown - shown*V_BORDER)) {
702 	    top += shown*V_DELTA;
703 	    if (top + shown > 1.0) top = 1.0 - shown;
704 	} else {
705 	    do_call = False;
706 	}
707 
708 	if (do_call) {
709 	    XtCallCallbacks(vert_sb, XtNjumpProc, &top);
710 	    adjusted = True;
711 	}
712     }
713 
714     /* make sure cursor is displayed during dowhatis.. */
715     if (adjusted) display_cursor(wp);
716 
717 #ifdef VERBOSE
718     if (horiz_sb || vert_sb) printf("\n");
719 #endif
720 }
721 
722 
723 /*
724  * Check to see if the viewport has grown smaller.  If so, then we want to make
725  * sure that the cursor is still on the screen.  We do this to keep the cursor
726  * on the screen when the user resizes the nethack window.
727  */
728 static void
map_check_size_change(wp)729 map_check_size_change(wp)
730     struct xwindow *wp;
731 {
732     struct map_info_t *map_info = wp->map_information;
733     Arg arg[2];
734     Dimension new_width, new_height;
735     Widget viewport;
736 
737     viewport = XtParent(wp->w);
738 
739     XtSetArg(arg[0], XtNwidth,  &new_width);
740     XtSetArg(arg[1], XtNheight, &new_height);
741     XtGetValues(viewport, arg, TWO);
742 
743     /* Only do cursor check if new size is smaller. */
744     if (new_width < map_info->viewport_width
745 		    || new_height < map_info->viewport_height) {
746 	check_cursor_visibility(wp);
747     }
748 
749     map_info->viewport_width = new_width;
750     map_info->viewport_height = new_height;
751 }
752 
753 /*
754  * Fill in parameters "regular" and "inverse" with newly created GCs.
755  * Using the given background pixel and the foreground pixel optained
756  * by querying the widget with the resource name.
757  */
758 static void
set_gc(w,font,resource_name,bgpixel,regular,inverse)759 set_gc(w, font, resource_name, bgpixel, regular, inverse)
760     Widget w;
761     Font font;
762     char *resource_name;
763     Pixel bgpixel;
764     GC   *regular, *inverse;
765 {
766     XGCValues values;
767     XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont;
768     Pixel curpixel;
769     Arg arg[1];
770 
771     XtSetArg(arg[0], resource_name, &curpixel);
772     XtGetValues(w, arg, ONE);
773 
774     values.foreground = curpixel;
775     values.background = bgpixel;
776     values.function   = GXcopy;
777     values.font	      = font;
778     *regular = XtGetGC(w, mask, &values);
779     values.foreground = bgpixel;
780     values.background = curpixel;
781     values.function   = GXcopy;
782     values.font	      = font;
783     *inverse = XtGetGC(w, mask, &values);
784 }
785 
786 /*
787  * Create the GC's for each color.
788  *
789  * I'm not sure if it is a good idea to have a GC for each color (and
790  * inverse). It might be faster to just modify the foreground and
791  * background colors on the current GC as needed.
792  */
793 static void
get_text_gc(wp,font)794 get_text_gc(wp, font)
795     struct xwindow *wp;
796     Font font;
797 {
798     struct map_info_t *map_info = wp->map_information;
799     Pixel bgpixel;
800     Arg arg[1];
801 
802     /* Get background pixel. */
803     XtSetArg(arg[0], XtNbackground, &bgpixel);
804     XtGetValues(wp->w, arg, ONE);
805 
806 #ifdef TEXTCOLOR
807 #define set_color_gc(nh_color, resource_name)			\
808 	    set_gc(wp->w, font, resource_name, bgpixel,		\
809 		&map_info->mtype.text_map->color_gcs[nh_color],	\
810 		    &map_info->mtype.text_map->inv_color_gcs[nh_color]);
811 
812     set_color_gc(CLR_BLACK,	XtNblack);
813     set_color_gc(CLR_RED,	XtNred);
814     set_color_gc(CLR_GREEN,	XtNgreen);
815     set_color_gc(CLR_BROWN,	XtNbrown);
816     set_color_gc(CLR_BLUE,	XtNblue);
817     set_color_gc(CLR_MAGENTA,	XtNmagenta);
818     set_color_gc(CLR_CYAN,	XtNcyan);
819     set_color_gc(CLR_GRAY,	XtNgray);
820     set_color_gc(NO_COLOR,	XtNforeground);
821     set_color_gc(CLR_ORANGE,	XtNorange);
822     set_color_gc(CLR_BRIGHT_GREEN, XtNbright_green);
823     set_color_gc(CLR_YELLOW,	XtNyellow);
824     set_color_gc(CLR_BRIGHT_BLUE, XtNbright_blue);
825     set_color_gc(CLR_BRIGHT_MAGENTA, XtNbright_magenta);
826     set_color_gc(CLR_BRIGHT_CYAN, XtNbright_cyan);
827     set_color_gc(CLR_WHITE,	XtNwhite);
828 #else
829     set_gc(wp->w, font, XtNforeground, bgpixel,
830 		&map_info->mtype.text_map->copy_gc,
831 		&map_info->mtype.text_map->inv_copy_gc);
832 #endif
833 }
834 
835 
836 /*
837  * Display the cursor on the map window.
838  */
839 static void
display_cursor(wp)840 display_cursor(wp)
841     struct xwindow *wp;
842 {
843     /* Redisplay the cursor location inverted. */
844     map_update(wp, wp->cursy, wp->cursy, wp->cursx, wp->cursx, TRUE);
845 }
846 
847 
848 /*
849  * Check if there are any changed characters.  If so, then plaster them on
850  * the screen.
851  */
852 void
display_map_window(wp)853 display_map_window(wp)
854     struct xwindow *wp;
855 {
856     register int row;
857     struct map_info_t *map_info = wp->map_information;
858 
859     /*
860      * If the previous cursor position is not the same as the current
861      * cursor position, then update the old cursor position.
862      */
863     if (wp->prevx != wp->cursx || wp->prevy != wp->cursy) {
864 	register unsigned int x = wp->prevx, y = wp->prevy;
865 	if (x < map_info->t_start[y]) map_info->t_start[y] = x;
866 	if (x > map_info->t_stop[y])  map_info->t_stop[y]  = x;
867     }
868 
869     for (row = 0; row < ROWNO; row++) {
870 	if (map_info->t_start[row] <= map_info->t_stop[row]) {
871 	    map_update(wp, row, row,
872 			(int) map_info->t_start[row],
873 			(int) map_info->t_stop[row], FALSE);
874 	    map_info->t_start[row] = COLNO-1;
875 	    map_info->t_stop[row] = 0;
876 	}
877     }
878     display_cursor(wp);
879     wp->prevx = wp->cursx;	/* adjust old cursor position */
880     wp->prevy = wp->cursy;
881 }
882 
883 /*
884  * Set all map tiles to S_stone
885  */
886 static void
map_all_stone(map_info)887 map_all_stone(map_info)
888 struct map_info_t *map_info;
889 {
890     int i;
891     unsigned short *sp, stone;
892     stone = cmap_to_glyph(S_stone);
893 
894     for (sp = (unsigned short *) map_info->mtype.tile_map->glyphs, i = 0;
895 	i < ROWNO*COLNO; sp++, i++)
896 
897     *sp = stone;
898 }
899 
900 /*
901  * Fill the saved screen characters with the "clear" tile or character.
902  *
903  * Flush out everything by resetting the "new" bounds and calling
904  * display_map_window().
905  */
906 void
clear_map_window(wp)907 clear_map_window(wp)
908     struct xwindow *wp;
909 {
910     struct map_info_t *map_info = wp->map_information;
911 
912     if (map_info->is_tile) {
913 	map_all_stone(map_info);
914     } else {
915 	/* Fill text with spaces, and update */
916 	(void) memset((genericptr_t) map_info->mtype.text_map->text, ' ',
917 			sizeof(map_info->mtype.text_map->text));
918 #ifdef TEXTCOLOR
919 	(void) memset((genericptr_t) map_info->mtype.text_map->colors, NO_COLOR,
920 			sizeof(map_info->mtype.text_map->colors));
921 #endif
922     }
923 
924     /* force a full update */
925     (void) memset((genericptr_t) map_info->t_start, (char) 0,
926 			sizeof(map_info->t_start));
927     (void) memset((genericptr_t) map_info->t_stop, (char) COLNO-1,
928 			sizeof(map_info->t_stop));
929     display_map_window(wp);
930 }
931 
932 /*
933  * Retreive the font associated with the map window and save attributes
934  * that are used when updating it.
935  */
936 static void
get_char_info(wp)937 get_char_info(wp)
938     struct xwindow *wp;
939 {
940     XFontStruct *fs;
941     struct map_info_t *map_info = wp->map_information;
942 
943     fs = WindowFontStruct(wp->w);
944     map_info->square_width    = fs->max_bounds.width;
945     map_info->square_height   = fs->max_bounds.ascent + fs->max_bounds.descent;
946     map_info->square_ascent   = fs->max_bounds.ascent;
947     map_info->square_lbearing = -fs->min_bounds.lbearing;
948 
949 #ifdef VERBOSE
950     printf("Font information:\n");
951     printf("fid = %d, direction = %d\n", fs->fid, fs->direction);
952     printf("first = %d, last = %d\n",
953 			fs->min_char_or_byte2, fs->max_char_or_byte2);
954     printf("all chars exist? %s\n", fs->all_chars_exist?"yes":"no");
955     printf("min_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n",
956 		fs->min_bounds.lbearing, fs->min_bounds.rbearing,
957 		fs->min_bounds.width, fs->min_bounds.ascent,
958 		fs->min_bounds.descent, fs->min_bounds.attributes);
959     printf("max_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n",
960 		fs->max_bounds.lbearing, fs->max_bounds.rbearing,
961 		fs->max_bounds.width, fs->max_bounds.ascent,
962 		fs->max_bounds.descent, fs->max_bounds.attributes);
963     printf("per_char = 0x%x\n", fs->per_char);
964     printf("Text: (max) width = %d, height = %d\n",
965 	    map_info->square_width, map_info->square_height);
966 #endif
967 
968     if (fs->min_bounds.width != fs->max_bounds.width)
969 	X11_raw_print("Warning:  map font is not monospaced!");
970 }
971 
972 /*
973  * keyhit buffer
974  */
975 #define INBUF_SIZE 64
976 int inbuf[INBUF_SIZE];
977 int incount = 0;
978 int inptr = 0;	/* points to valid data */
979 
980 
981 /*
982  * Keyboard and button event handler for map window.
983  */
984 void
map_input(w,event,params,num_params)985 map_input(w, event, params, num_params)
986     Widget   w;
987     XEvent   *event;
988     String   *params;
989     Cardinal *num_params;
990 {
991     XKeyEvent *key;
992     XButtonEvent *button;
993     boolean meta = FALSE;
994     int i, nbytes;
995     Cardinal in_nparams = (num_params ? *num_params : 0);
996     char c;
997     char keystring[MAX_KEY_STRING];
998 
999     switch (event->type) {
1000 	case ButtonPress:
1001 	    button = (XButtonEvent *) event;
1002 #ifdef VERBOSE_INPUT
1003 	    printf("button press\n");
1004 #endif
1005 	    if (in_nparams > 0 &&
1006 		(nbytes = strlen(params[0])) < MAX_KEY_STRING) {
1007 		Strcpy(keystring, params[0]);
1008 		key = (XKeyEvent *) event; /* just in case */
1009 		goto key_events;
1010 	    }
1011 	    if (w != window_list[WIN_MAP].w) {
1012 #ifdef VERBOSE_INPUT
1013 		printf("map_input called from wrong window\n");
1014 #endif
1015 		X11_nhbell();
1016 		return;
1017 	    }
1018 	    set_button_values(w, button->x, button->y, button->button);
1019 	    break;
1020 	case KeyPress:
1021 #ifdef VERBOSE_INPUT
1022 	    printf("key: ");
1023 #endif
1024 	    if(appResources.slow && input_func) {
1025 		(*input_func)(w, event, params, num_params);
1026 		break;
1027 	    }
1028 
1029 	    /*
1030 	     * Don't use key_event_to_char() because we want to be able
1031 	     * to allow keys mapped to multiple characters.
1032 	     */
1033 	    key = (XKeyEvent *) event;
1034 	    if (in_nparams > 0 &&
1035 		(nbytes = strlen(params[0])) < MAX_KEY_STRING) {
1036 		Strcpy(keystring, params[0]);
1037 	    } else {
1038 		/*
1039 		 * Assume that mod1 is really the meta key.
1040 		 */
1041 		meta = !!(key->state & Mod1Mask);
1042 		nbytes =
1043 		    XLookupString(key, keystring, MAX_KEY_STRING,
1044 				  (KeySym *)0, (XComposeStatus *)0);
1045 	    }
1046 	key_events:
1047 	    /* Modifier keys return a zero length string when pressed. */
1048 	    if (nbytes) {
1049 #ifdef VERBOSE_INPUT
1050 		printf("\"");
1051 #endif
1052 		for (i = 0; i < nbytes; i++) {
1053 		    c = keystring[i];
1054 
1055 		    if (incount < INBUF_SIZE) {
1056 			inbuf[(inptr+incount)%INBUF_SIZE] =
1057 			    ((int) c) + (meta ? 0x80 : 0);
1058 			incount++;
1059 		    } else {
1060 			X11_nhbell();
1061 		    }
1062 #ifdef VERBOSE_INPUT
1063 		    if (meta)			/* meta will print as M<c> */
1064 			(void) putchar('M');
1065 		    if (c < ' ') {		/* ctrl will print as ^<c> */
1066 			(void) putchar('^');
1067 			c += '@';
1068 		    }
1069 		    (void) putchar(c);
1070 #endif
1071 		}
1072 #ifdef VERBOSE_INPUT
1073 		printf("\" [%d bytes]\n", nbytes);
1074 #endif
1075 	    }
1076 	    break;
1077 
1078 	default:
1079 	    impossible("unexpected X event, type = %d\n", (int) event->type);
1080 	    break;
1081     }
1082 }
1083 
1084 static void
set_button_values(w,x,y,button)1085 set_button_values(w, x, y, button)
1086     Widget w;
1087     int x;
1088     int y;
1089     unsigned int button;
1090 {
1091     struct xwindow *wp;
1092     struct map_info_t *map_info;
1093 
1094     wp = find_widget(w);
1095     map_info = wp->map_information;
1096 
1097     click_x = x / map_info->square_width;
1098     click_y = y / map_info->square_height;
1099 
1100     /* The values can be out of range if the map window has been resized */
1101     /* to be larger than the max size.					 */
1102     if (click_x >= COLNO) click_x = COLNO-1;
1103     if (click_y >= ROWNO) click_x = ROWNO-1;
1104 
1105     /* Map all buttons but the first to the second click */
1106     click_button = (button == Button1) ? CLICK_1 : CLICK_2;
1107 }
1108 
1109 /*
1110  * Map window expose callback.
1111  */
1112 /*ARGSUSED*/
1113 static void
map_exposed(w,client_data,widget_data)1114 map_exposed(w, client_data, widget_data)
1115     Widget w;
1116     XtPointer client_data;	/* unused */
1117     XtPointer widget_data;	/* expose event from Window widget */
1118 {
1119     int x, y;
1120     struct xwindow *wp;
1121     struct map_info_t *map_info;
1122     unsigned width, height;
1123     int start_row, stop_row, start_col, stop_col;
1124     XExposeEvent *event = (XExposeEvent *) widget_data;
1125     int t_height, t_width;	/* tile/text height & width */
1126 
1127     if (!XtIsRealized(w) || event->count > 0) return;
1128 
1129     wp = find_widget(w);
1130     map_info = wp->map_information;
1131     if (wp->keep_window && !map_info) return;
1132     /*
1133      * The map is sent an expose event when the viewport resizes.  Make sure
1134      * that the cursor is still in the viewport after the resize.
1135      */
1136     map_check_size_change(wp);
1137 
1138     if (event) {		/* called from button-event */
1139 	x      = event->x;
1140 	y      = event->y;
1141 	width  = event->width;
1142 	height = event->height;
1143     } else {
1144 	x     = 0;
1145 	y     = 0;
1146 	width = wp->pixel_width;
1147 	height= wp->pixel_height;
1148     }
1149     /*
1150      * Convert pixels into INCLUSIVE text rows and columns.
1151      */
1152     t_height = map_info->square_height;
1153     t_width = map_info->square_width;
1154     start_row = y / t_height;
1155     stop_row = ((y + height) / t_height) +
1156 		((((y + height) % t_height) == 0) ? 0 : 1) - 1;
1157 
1158     start_col = x / t_width;
1159     stop_col = ((x + width) / t_width) +
1160 		((((x + width) % t_width) == 0) ? 0 : 1) - 1;
1161 
1162 #ifdef VERBOSE
1163     printf("map_exposed: x = %d, y = %d, width = %d, height = %d\n",
1164 						    x, y, width, height);
1165     printf("chars %d x %d, rows %d to %d, columns %d to %d\n",
1166 			map_info->square_height, map_info->square_width,
1167 			start_row, stop_row, start_col, stop_col);
1168 #endif
1169 
1170     /* Out of range values are possible if the map window is resized to be */
1171     /* bigger than the largest expected value.				   */
1172     if (stop_row >= ROWNO) stop_row = ROWNO-1;
1173     if (stop_col >= COLNO) stop_col = COLNO-1;
1174 
1175     map_update(wp, start_row, stop_row, start_col, stop_col, FALSE);
1176     display_cursor(wp);		/* make sure cursor shows up */
1177 }
1178 
1179 /*
1180  * Do the actual work of the putting characters onto our X window.  This
1181  * is called from the expose event routine, the display window (flush)
1182  * routine, and the display cursor routine.  The later is a kludge that
1183  * involves the inverted parameter of this function.  A better solution
1184  * would be to double the color count, with any color above CLR_MAX
1185  * being inverted.
1186  *
1187  * This works for rectangular regions (this includes one line rectangles).
1188  * The start and stop columns are *inclusive*.
1189  */
1190 static void
map_update(wp,start_row,stop_row,start_col,stop_col,inverted)1191 map_update(wp, start_row, stop_row, start_col, stop_col, inverted)
1192     struct xwindow *wp;
1193     int start_row, stop_row, start_col, stop_col;
1194     boolean inverted;
1195 {
1196     int win_start_row, win_start_col;
1197     struct map_info_t *map_info = wp->map_information;
1198     int row;
1199     register int count;
1200 
1201     if (start_row < 0 || stop_row >= ROWNO) {
1202 	impossible("map_update:  bad row range %d-%d\n", start_row, stop_row);
1203 	return;
1204     }
1205     if (start_col < 0 || stop_col >=COLNO) {
1206 	impossible("map_update:  bad col range %d-%d\n", start_col, stop_col);
1207 	return;
1208     }
1209 
1210 #ifdef VERBOSE_UPDATE
1211     printf("update: [0x%x] %d %d %d %d\n",
1212 		(int) wp->w, start_row, stop_row, start_col, stop_col);
1213 #endif
1214     win_start_row = start_row;
1215     win_start_col = start_col;
1216 
1217     if (map_info->is_tile) {
1218 	struct tile_map_info_t *tile_map = map_info->mtype.tile_map;
1219 	int cur_col;
1220 	Display* dpy=XtDisplay(wp->w);
1221 	Screen* screen=DefaultScreenOfDisplay(dpy);
1222 
1223 	for (row = start_row; row <= stop_row; row++) {
1224 	    for (cur_col = start_col; cur_col <= stop_col; cur_col++) {
1225 		int glyph = tile_map->glyphs[row][cur_col];
1226 		int tile = glyph2tile[glyph];
1227 		int dest_x = cur_col * map_info->square_width;
1228 		int dest_y = row * map_info->square_height;
1229 
1230 		XCopyArea(dpy, tile_pixmap, XtWindow(wp->w),
1231 			tile_map->black_gc,	/* no grapics_expose */
1232 			0,
1233 			tile * map_info->square_height,
1234 			tile_width, tile_height,
1235 			dest_x,dest_y);
1236 
1237 		if (glyph_is_pet(glyph)
1238 #ifdef TEXTCOLOR
1239 			&& iflags.hilite_pet
1240 #endif
1241 			) {
1242 		    /* draw pet annotation (a heart) */
1243 		    XSetForeground(dpy, tile_map->black_gc, pet_annotation.foreground);
1244 		    XSetClipOrigin(dpy, tile_map->black_gc, dest_x, dest_y);
1245 		    XSetClipMask(dpy, tile_map->black_gc, pet_annotation.bitmap);
1246 		    XCopyPlane(
1247 			dpy,
1248 			pet_annotation.bitmap,
1249 			XtWindow(wp->w),
1250 			tile_map->black_gc,
1251 			0,0,
1252 			pet_annotation.width,pet_annotation.height,
1253 			dest_x,dest_y,
1254 			1
1255 		    );
1256 		    XSetClipOrigin(dpy, tile_map->black_gc, 0, 0);
1257 		    XSetClipMask(dpy, tile_map->black_gc, None);
1258 		    XSetForeground(dpy, tile_map->black_gc, BlackPixelOfScreen(screen));
1259 		}
1260 	    }
1261 	}
1262 
1263 	if (inverted) {
1264 	    XDrawRectangle(XtDisplay(wp->w), XtWindow(wp->w),
1265 #ifdef USE_WHITE
1266 		/* kludge for white square... */
1267 		tile_map->glyphs[start_row][start_col] ==
1268 		    cmap_to_glyph(S_ice) ?
1269 			tile_map->black_gc : tile_map->white_gc,
1270 #else
1271 		tile_map->white_gc,
1272 #endif
1273 		start_col * map_info->square_width,
1274 		start_row * map_info->square_height,
1275 		map_info->square_width-1,
1276 		map_info->square_height-1);
1277 	}
1278     } else {
1279 	struct text_map_info_t *text_map = map_info->mtype.text_map;
1280 
1281 #ifdef TEXTCOLOR
1282 	if (iflags.use_color) {
1283 	    register char *c_ptr;
1284 	    char *t_ptr;
1285 	    int cur_col, color, win_ystart;
1286 
1287 	    for (row = start_row; row <= stop_row; row++) {
1288 		win_ystart = map_info->square_ascent +
1289 					(row * map_info->square_height);
1290 
1291 		t_ptr = (char *) &(text_map->text[row][start_col]);
1292 		c_ptr = (char *) &(text_map->colors[row][start_col]);
1293 		cur_col = start_col;
1294 		while (cur_col <= stop_col) {
1295 		    color = *c_ptr++;
1296 		    count = 1;
1297 		    while ((cur_col + count) <= stop_col && *c_ptr == color) {
1298 			count++;
1299 			c_ptr++;
1300 		    }
1301 
1302 		    XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w),
1303 			inverted ? text_map->inv_color_gcs[color] :
1304 				   text_map->color_gcs[color],
1305 			map_info->square_lbearing + (map_info->square_width * cur_col),
1306 			win_ystart,
1307 			t_ptr, count);
1308 
1309 		    /* move text pointer and column count */
1310 		    t_ptr += count;
1311 		    cur_col += count;
1312 		} /* col loop */
1313 	    } /* row loop */
1314 	} else
1315 #endif /* TEXTCOLOR */
1316 	{
1317 	    int win_row, win_xstart;
1318 
1319 	    /* We always start at the same x window position and have	*/
1320 	    /* the same character count.				*/
1321 	    win_xstart = map_info->square_lbearing +
1322 				    (win_start_col * map_info->square_width);
1323 	    count = stop_col - start_col + 1;
1324 
1325 	    for (row = start_row, win_row = win_start_row;
1326 					row <= stop_row; row++, win_row++) {
1327 
1328 		XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w),
1329 		    inverted ? text_map->inv_copy_gc : text_map->copy_gc,
1330 		    win_xstart,
1331 		    map_info->square_ascent + (win_row * map_info->square_height),
1332 		    (char *) &(text_map->text[row][start_col]), count);
1333 	    }
1334 	}
1335     }
1336 }
1337 
1338 /* Adjust the number of rows and columns on the given map window */
1339 void
set_map_size(wp,cols,rows)1340 set_map_size(wp, cols, rows)
1341     struct xwindow *wp;
1342     Dimension cols, rows;
1343 {
1344     Arg args[4];
1345     Cardinal num_args;
1346 
1347     wp->pixel_width  = wp->map_information->square_width  * cols;
1348     wp->pixel_height = wp->map_information->square_height * rows;
1349 
1350     num_args = 0;
1351     XtSetArg(args[num_args], XtNwidth, wp->pixel_width);   num_args++;
1352     XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++;
1353     XtSetValues(wp->w, args, num_args);
1354 }
1355 
1356 
1357 static void
init_text(wp)1358 init_text(wp)
1359     struct xwindow *wp;
1360 {
1361 
1362     struct map_info_t *map_info = wp->map_information;
1363     struct text_map_info_t *text_map;
1364 
1365     map_info->is_tile = FALSE;
1366     text_map = map_info->mtype.text_map =
1367 	(struct text_map_info_t *) alloc(sizeof(struct text_map_info_t));
1368 
1369     (void) memset((genericptr_t) text_map->text, ' ', sizeof(text_map->text));
1370 #ifdef TEXTCOLOR
1371     (void) memset((genericptr_t) text_map->colors, NO_COLOR,
1372 			sizeof(text_map->colors));
1373 #endif
1374 
1375     get_char_info(wp);
1376     get_text_gc(wp, WindowFont(wp->w));
1377 }
1378 
1379 static char map_translations[] =
1380 "#override\n\
1381  <Key>Left: scroll(4)\n\
1382  <Key>Right: scroll(6)\n\
1383  <Key>Up: scroll(8)\n\
1384  <Key>Down: scroll(2)\n\
1385  <Key>:		input()	\
1386 ";
1387 
1388 /*
1389  * The map window creation routine.
1390  */
1391 void
create_map_window(wp,create_popup,parent)1392 create_map_window(wp, create_popup, parent)
1393     struct xwindow *wp;
1394     boolean create_popup;	/* parent is a popup shell that we create */
1395     Widget parent;
1396 {
1397     struct map_info_t *map_info;	/* map info pointer */
1398     Widget map, viewport;
1399     Arg args[16];
1400     Cardinal num_args;
1401     Dimension rows, columns;
1402 #if 0
1403     int screen_width, screen_height;
1404 #endif
1405 
1406     wp->type = NHW_MAP;
1407 
1408     if (create_popup) {
1409 	/*
1410 	 * Create a popup that accepts key and button events.
1411 	 */
1412 	num_args = 0;
1413 	XtSetArg(args[num_args], XtNinput, False);            num_args++;
1414 
1415 	wp->popup = parent = XtCreatePopupShell("nethack",
1416 					topLevelShellWidgetClass,
1417 				       toplevel, args, num_args);
1418 	/*
1419 	 * If we're here, then this is an auxiliary map window.  If we're
1420 	 * cancelled via a delete window message, we should just pop down.
1421 	 */
1422     }
1423 
1424     num_args = 0;
1425     XtSetArg(args[num_args], XtNallowHoriz, True);	num_args++;
1426     XtSetArg(args[num_args], XtNallowVert,  True);	num_args++;
1427     /* XtSetArg(args[num_args], XtNforceBars,  True);	num_args++; */
1428     XtSetArg(args[num_args], XtNuseBottom,  True);	num_args++;
1429     XtSetArg(args[num_args], XtNtranslations,
1430 		XtParseTranslationTable(map_translations));	num_args++;
1431     viewport = XtCreateManagedWidget(
1432 			"map_viewport",		/* name */
1433 			viewportWidgetClass,	/* widget class from Window.h */
1434 			parent,			/* parent widget */
1435 			args,			/* set some values */
1436 			num_args);		/* number of values to set */
1437 
1438     /*
1439      * Create a map window.  We need to set the width and height to some
1440      * value when we create it.  We will change it to the value we want
1441      * later
1442      */
1443     num_args = 0;
1444     XtSetArg(args[num_args], XtNwidth,  100); num_args++;
1445     XtSetArg(args[num_args], XtNheight, 100); num_args++;
1446     XtSetArg(args[num_args], XtNtranslations,
1447 		XtParseTranslationTable(map_translations));	num_args++;
1448 
1449     wp->w = map = XtCreateManagedWidget(
1450 		"map",			/* name */
1451 		windowWidgetClass,	/* widget class from Window.h */
1452 		viewport,		/* parent widget */
1453 		args,			/* set some values */
1454 		num_args);		/* number of values to set */
1455 
1456     XtAddCallback(map, XtNexposeCallback, map_exposed, (XtPointer) 0);
1457 
1458     map_info = wp->map_information =
1459 			(struct map_info_t *) alloc(sizeof(struct map_info_t));
1460 
1461     map_info->viewport_width = map_info->viewport_height = 0;
1462 
1463     /* reset the "new entry" indicators */
1464     (void) memset((genericptr_t) map_info->t_start, (char) COLNO,
1465 			sizeof(map_info->t_start));
1466     (void) memset((genericptr_t) map_info->t_stop, (char) 0,
1467 			sizeof(map_info->t_stop));
1468 
1469     /* we probably want to restrict this to the 1st map window only */
1470     if (appResources.tile_file[0] && init_tiles(wp)) {
1471 	map_info->is_tile = TRUE;
1472     } else {
1473 	init_text(wp);
1474 	map_info->is_tile = FALSE;
1475     }
1476 
1477 
1478     /*
1479      * Initially, set the map widget to be the size specified by the
1480      * widget rows and columns resources.  We need to do this to
1481      * correctly set the viewport window size.  After the viewport is
1482      * realized, then the map can resize to its normal size.
1483      */
1484     num_args = 0;
1485     XtSetArg(args[num_args], XtNrows,    &rows);	num_args++;
1486     XtSetArg(args[num_args], XtNcolumns, &columns);	num_args++;
1487     XtGetValues(wp->w, args, num_args);
1488 
1489     /* Don't bother with windows larger than ROWNOxCOLNO. */
1490     if (columns > COLNO) columns = COLNO;
1491     if (rows    > ROWNO) rows = ROWNO;
1492 
1493 #if 0 /* This is insufficient.  We now resize final window in winX.c */
1494     /*
1495      * Check for overrunning the size of the screen.  This does an ad hoc
1496      * job.
1497      *
1498      * Width:	We expect that there is nothing but borders on either side
1499      *		of the map window.  Use some arbitrary width to decide
1500      *		when to shrink.
1501      *
1502      * Height:	if the map takes up more than 1/2 of the screen height, start
1503      *		reducing its size.
1504      */
1505     screen_height = HeightOfScreen(XtScreen(wp->w));
1506     screen_width  = WidthOfScreen(XtScreen(wp->w));
1507 
1508 #define WOFF 50
1509     if ((int)(columns*map_info->square_width) > screen_width-WOFF) {
1510 	columns = (screen_width-WOFF) / map_info->square_width;
1511 	if (columns == 0) columns = 1;
1512     }
1513 
1514     if ((int)(rows*map_info->square_height) > screen_height/2) {
1515 	rows = screen_height / (2*map_info->square_height);
1516 	if (rows == 0) rows = 1;
1517     }
1518 #endif
1519 
1520     set_map_size(wp, columns, rows);
1521 
1522 
1523     /*
1524      * If we have created our own popup, then realize it so that the
1525      * viewport is also realized.  Then resize the map window.
1526      */
1527     if (create_popup) {
1528 	XtRealizeWidget(wp->popup);
1529 	XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
1530 			&wm_delete_window, 1);
1531 	set_map_size(wp, COLNO, ROWNO);
1532     }
1533 
1534     if (map_info->is_tile) {
1535 	map_all_stone(map_info);
1536     }
1537 }
1538 
1539 /*
1540  * Destroy this map window.
1541  */
1542 void
destroy_map_window(wp)1543 destroy_map_window(wp)
1544     struct xwindow *wp;
1545 {
1546     struct map_info_t *map_info = wp->map_information;
1547 
1548     if (wp->popup)
1549 	nh_XtPopdown(wp->popup);
1550 
1551     if (map_info) {
1552 	struct text_map_info_t *text_map = map_info->mtype.text_map;
1553 
1554 	/* Free allocated GCs. */
1555 	if (!map_info->is_tile) {
1556 #ifdef TEXTCOLOR
1557 	    int i;
1558 
1559 	    for (i = 0; i < CLR_MAX; i++) {
1560 		XtReleaseGC(wp->w, text_map->color_gcs[i]);
1561 		XtReleaseGC(wp->w, text_map->inv_color_gcs[i]);
1562 	    }
1563 #else
1564 	    XtReleaseGC(wp->w, text_map->copy_gc);
1565 	    XtReleaseGC(wp->w, text_map->inv_copy_gc);
1566 #endif
1567 	}
1568 	/* free alloc'ed text information */
1569 	free((genericptr_t)text_map),   map_info->mtype.text_map = 0;
1570 
1571 	/* Free malloc'ed space. */
1572 	free((genericptr_t)map_info),  wp->map_information = 0;
1573     }
1574 
1575 	/* Destroy map widget. */
1576     if (wp->popup && !wp->keep_window)
1577 	XtDestroyWidget(wp->popup),  wp->popup = (Widget)0;
1578 
1579     if (wp->keep_window)
1580 	XtRemoveCallback(wp->w, XtNexposeCallback, map_exposed, (XtPointer)0);
1581     else
1582 	wp->type = NHW_NONE;	/* allow re-use */
1583 }
1584 
1585 
1586 
1587 boolean exit_x_event;	/* exit condition for the event loop */
1588 /*******
1589 pkey(k)
1590     int k;
1591 {
1592     printf("key = '%s%c'\n", (k<32) ? "^":"", (k<32) ? '@'+k : k);
1593 }
1594 ******/
1595 
1596 /*
1597  * Main X event loop.  Here we accept and dispatch X events.  We only exit
1598  * under certain circumstances.
1599  */
1600 int
x_event(exit_condition)1601 x_event(exit_condition)
1602     int exit_condition;
1603 {
1604     XEvent  event;
1605     int     retval = 0;
1606     boolean keep_going = TRUE;
1607 
1608     /* Hold globals so function is re-entrant */
1609     boolean hold_exit_x_event = exit_x_event;
1610 
1611     click_button = NO_CLICK;	/* reset click exit condition */
1612     exit_x_event = FALSE;	/* reset callback exit condition */
1613 
1614     /*
1615      * Loop until we get a sent event, callback exit, or are accepting key
1616      * press and button press events and we receive one.
1617      */
1618     if((exit_condition == EXIT_ON_KEY_PRESS ||
1619 	exit_condition == EXIT_ON_KEY_OR_BUTTON_PRESS) && incount)
1620 	goto try_test;
1621 
1622     do {
1623 	XtAppNextEvent(app_context, &event);
1624 	XtDispatchEvent(&event);
1625 
1626 	/* See if we can exit. */
1627     try_test:
1628 	switch (exit_condition) {
1629 	    case EXIT_ON_SENT_EVENT: {
1630 		XAnyEvent *any = (XAnyEvent *) &event;
1631 		if (any->send_event) {
1632 		    retval = 0;
1633 		    keep_going = FALSE;
1634 		}
1635 		break;
1636 	    }
1637 	    case EXIT_ON_EXIT:
1638 		if (exit_x_event) {
1639 		    incount = 0;
1640 		    retval = 0;
1641 		    keep_going = FALSE;
1642 		}
1643 		break;
1644 	    case EXIT_ON_KEY_PRESS:
1645 		if (incount != 0) {
1646 		    /* get first pressed key */
1647 		    --incount;
1648 		    retval = inbuf[inptr];
1649 		    inptr = (inptr+1) % INBUF_SIZE;
1650 		    /* pkey(retval); */
1651 		    keep_going = FALSE;
1652 		}
1653 		break;
1654 	    case EXIT_ON_KEY_OR_BUTTON_PRESS:
1655 		if (incount != 0 || click_button != NO_CLICK) {
1656 		    if (click_button != NO_CLICK) {	/* button press */
1657 			/* click values are already set */
1658 			retval = 0;
1659 		    } else {				/* key press */
1660 			/* get first pressed key */
1661 			--incount;
1662 			retval = inbuf[inptr];
1663 			inptr = (inptr+1) % INBUF_SIZE;
1664 			/* pkey(retval); */
1665 		    }
1666 		    keep_going = FALSE;
1667 		}
1668 		break;
1669 	    default:
1670 		panic("x_event: unknown exit condition %d\n", exit_condition);
1671 		break;
1672 	}
1673     } while (keep_going);
1674 
1675     /* Restore globals */
1676     exit_x_event = hold_exit_x_event;
1677 
1678     return retval;
1679 }
1680 
1681 /*winmap.c*/
1682