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
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*/
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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*/