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