OSDN Git Service

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