OSDN Git Service

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