OSDN Git Service

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