OSDN Git Service

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