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. */
7 * + global functions print_glyph() and cliparound()
8 * + the map window routines
9 * + the char and pointer input routines
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.
19 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
22 #include <X11/Intrinsic.h>
23 #include <X11/StringDefs.h>
24 #include <X11/Shell.h>
25 #include <X11/Xaw/Cardinals.h>
26 #include <X11/Xaw/Scrollbar.h>
27 #include <X11/Xaw/Viewport.h>
28 #include <X11/Xatom.h>
30 #ifdef PRESERVE_NO_SYSV
34 #undef PRESERVE_NO_SYSV
37 #include "xwindow.h" /* map widget declarations */
48 extern short glyph2tile[];
49 extern int total_tiles_used;
51 /* Define these if you really want a lot of junk on your screen. */
52 /* #define VERBOSE */ /* print various info & events as they happen */
53 /* #define VERBOSE_UPDATE */ /* print screen update bounds */
54 /* #define VERBOSE_INPUT */ /* print input events */
56 #define USE_WHITE /* almost always use white as a tile cursor border */
58 static boolean FDECL(init_tiles, (struct xwindow *));
59 static void FDECL(set_button_values, (Widget, int, int, unsigned));
60 static void FDECL(map_check_size_change, (struct xwindow *));
61 static void FDECL(map_update, (struct xwindow *, int, int, int, int,
63 static void FDECL(init_text, (struct xwindow *));
64 static void FDECL(map_exposed, (Widget, XtPointer, XtPointer));
65 static void FDECL(set_gc, (Widget, Font, const char *, Pixel, GC *, GC *));
66 static void FDECL(get_text_gc, (struct xwindow *, Font));
67 static void FDECL(get_char_info, (struct xwindow *));
68 static void FDECL(display_cursor, (struct xwindow *));
70 /* Global functions ======================================================= */
73 X11_print_glyph(window, x, y, glyph, bkglyph)
79 struct map_info_t *map_info;
80 boolean update_bbox = FALSE;
83 if (window_list[window].type != NHW_MAP) {
84 impossible("print_glyph: can (currently) only print to map windows");
87 map_info = window_list[window].map_information;
89 /* update both the tile and text backing stores */
91 unsigned short *t_ptr = &map_info->tile_map.glyphs[y][x].glyph;
93 if (*t_ptr != glyph) {
95 if (map_info->is_tile)
101 register unsigned char *ch_ptr;
105 register unsigned char *co_ptr;
108 /* map glyph to character and color */
109 (void) mapglyph(glyph, &och, &color, &special, x, y);
112 if (special != map_info->tile_map.glyphs[y][x].special) {
113 map_info->tile_map.glyphs[y][x].special = special;
117 /* Only update if we need to. */
118 ch_ptr = &map_info->text_map.text[y][x];
121 co_ptr = &map_info->text_map.colors[y][x];
122 if (*ch_ptr != ch || *co_ptr != color)
129 if ((special & MG_PET) && iflags.hilite_pet)
131 if ((special & MG_OBJPILE) && iflags.hilite_pile)
134 if (!map_info->is_tile)
139 if (update_bbox) { /* update row bbox */
140 if ((uchar) x < map_info->t_start[y])
141 map_info->t_start[y] = x;
142 if ((uchar) x > map_info->t_stop[y])
143 map_info->t_stop[y] = x;
149 * The is the tty clip call. Since X can resize at any time, we can't depend
150 * on this being defined.
158 #endif /* CLIPPING */
160 /* End global functions =================================================== */
162 #include "tile2x11.h"
165 * We're expecting to never read more than one tile file per session.
166 * If this is false, then we can make an array of this information,
167 * or just keep it on a per-window basis.
169 Pixmap tile_pixmap = None;
170 static int tile_width;
171 static int tile_height;
172 static int tile_count;
173 static XImage *tile_image = 0;
176 * This structure is used for small bitmaps that are used for annotating
177 * tiles. For example, a "heart" annotates pets.
179 struct tile_annotation {
182 unsigned int width, height;
183 int hotx, hoty; /* not currently used */
186 static struct tile_annotation pet_annotation;
187 static struct tile_annotation pile_annotation;
190 init_annotation(annotation, filename, colorpixel)
191 struct tile_annotation *annotation;
195 Display *dpy = XtDisplay(toplevel);
197 if (0 != XReadBitmapFile(dpy, XtWindow(toplevel), filename,
198 &annotation->width, &annotation->height,
199 &annotation->bitmap, &annotation->hotx,
200 &annotation->hoty)) {
203 Sprintf(buf, "Failed to load %s", filename);
207 annotation->foreground = colorpixel;
211 * Put the tile image on the server.
213 * We can't send the image to the server until the top level
214 * is realized. When the tile file is first processed, the top
215 * level is not realized. This routine is called after we
216 * realize the top level, but before we start resizing the
222 Display *dpy = XtDisplay(toplevel);
223 unsigned int width, height;
226 return; /* no tiles */
228 height = tile_image->height;
229 width = tile_image->width;
231 tile_pixmap = XCreatePixmap(dpy, XtWindow(toplevel), width, height,
232 DefaultDepth(dpy, DefaultScreen(dpy)));
234 XPutImage(dpy, tile_pixmap, DefaultGC(dpy, DefaultScreen(dpy)),
235 tile_image, 0, 0, 0, 0, /* src, dest top left */
238 XDestroyImage(tile_image); /* data bytes free'd also */
241 init_annotation(&pet_annotation, appResources.pet_mark_bitmap,
242 appResources.pet_mark_color);
243 init_annotation(&pile_annotation, appResources.pilemark_bitmap,
244 appResources.pilemark_color);
248 * Open and read the tile file. Return TRUE if there were no problems.
249 * Return FALSE otherwise.
256 XpmAttributes attributes;
259 FILE *fp = (FILE *) 0;
261 unsigned char *cp, *colormap = (unsigned char *) 0;
262 unsigned char *tb, *tile_bytes = (unsigned char *) 0;
264 XColor *colors = (XColor *) 0;
271 Display *dpy = XtDisplay(toplevel);
272 Screen *screen = DefaultScreenOfDisplay(dpy);
273 struct map_info_t *map_info = (struct map_info_t *) 0;
274 struct tile_map_info_t *tile_info = (struct tile_map_info_t *) 0;
275 unsigned int image_height = 0, image_width = 0;
276 boolean result = TRUE;
280 /* already have tile information */
281 if (tile_pixmap != None)
284 map_info = wp->map_information;
285 tile_info = &map_info->tile_map;
286 (void) memset((genericptr_t) tile_info, 0,
287 sizeof(struct tile_map_info_t));
289 /* no tile file name, no tile information */
290 if (!appResources.tile_file[0]) {
296 attributes.valuemask = XpmCloseness;
297 attributes.closeness = 25000;
299 errorcode = XpmReadFileToImage(dpy, appResources.tile_file, &tile_image,
302 if (errorcode == XpmColorFailed) {
303 Sprintf(buf, "Insufficient colors available to load %s.",
304 appResources.tile_file);
306 X11_raw_print("Try closing other colorful applications and restart.");
307 X11_raw_print("Attempting to load with inferior colors.");
308 attributes.closeness = 50000;
309 errorcode = XpmReadFileToImage(dpy, appResources.tile_file,
310 &tile_image, 0, &attributes);
313 if (errorcode != XpmSuccess) {
314 if (errorcode == XpmColorFailed) {
315 Sprintf(buf, "Insufficient colors available to load %s.",
316 appResources.tile_file);
319 Sprintf(buf, "Failed to load %s: %s", appResources.tile_file,
320 XpmGetErrorString(errorcode));
324 X11_raw_print("Switching to text-based mode.");
328 /* assume a fixed number of tiles per row */
329 if (tile_image->width % TILES_PER_ROW != 0
330 || tile_image->width <= TILES_PER_ROW) {
332 "%s is not a multiple of %d (number of tiles/row) pixels wide",
333 appResources.tile_file, TILES_PER_ROW);
335 XDestroyImage(tile_image);
341 /* infer tile dimensions from image size and TILES_PER_ROW */
342 image_width = tile_image->width;
343 image_height = tile_image->height;
345 tile_count = total_tiles_used;
346 if ((tile_count % TILES_PER_ROW) != 0) {
347 tile_count += TILES_PER_ROW - (tile_count % TILES_PER_ROW);
349 tile_width = image_width / TILES_PER_ROW;
350 tile_height = image_height / (tile_count / TILES_PER_ROW);
352 /* any less than 16 colours makes tiles useless */
353 ddepth = DefaultDepthOfScreen(screen);
355 X11_raw_print("need a screen depth of at least 4");
360 fp = fopen_datafile(appResources.tile_file, RDBMODE, FALSE);
362 X11_raw_print("can't open tile file");
367 if ((int) fread((char *) &header, sizeof(header), 1, fp) != 1) {
368 X11_raw_print("read of header failed");
373 if (header.version != 2) {
374 Sprintf(buf, "Wrong tile file version, expected 2, got %lu",
382 X11 tile file:\n version %ld\n ncolors %ld\n \
383 tile width %ld\n tile height %ld\n per row %ld\n \
385 header.version, header.ncolors, header.tile_width,
386 header.tile_height, header.per_row, header.ntiles);
389 size = 3 * header.ncolors;
390 colormap = (unsigned char *) alloc((unsigned) size);
391 if ((int) fread((char *) colormap, 1, size, fp) != size) {
392 X11_raw_print("read of colormap failed");
397 colors = (XColor *) alloc(sizeof(XColor) * (unsigned) header.ncolors);
398 for (i = 0; i < header.ncolors; i++) {
399 cp = colormap + (3 * i);
400 colors[i].red = cp[0] * 256;
401 colors[i].green = cp[1] * 256;
402 colors[i].blue = cp[2] * 256;
406 if (!XAllocColor(dpy, DefaultColormapOfScreen(screen), &colors[i])
407 && !nhApproxColor(screen, DefaultColormapOfScreen(screen),
408 (char *) 0, &colors[i])) {
409 Sprintf(buf, "%dth out of %ld color allocation failed", i,
417 size = header.tile_height * header.tile_width;
419 * This alloc() and the one below require 32-bit ints, since tile_bytes
420 * is currently ~200k and alloc() takes an int
422 tile_count = header.ntiles;
423 if ((tile_count % header.per_row) != 0) {
424 tile_count += header.per_row - (tile_count % header.per_row);
426 tile_bytes = (unsigned char *) alloc((unsigned) tile_count * size);
427 if ((int) fread((char *) tile_bytes, size, tile_count, fp)
429 X11_raw_print("read of tile bytes failed");
434 if (header.ntiles < (unsigned) total_tiles_used) {
435 Sprintf(buf, "tile file incomplete, expecting %d tiles, found %lu",
436 total_tiles_used, header.ntiles);
442 if (appResources.double_tile_size) {
443 tile_width = 2 * header.tile_width;
444 tile_height = 2 * header.tile_height;
446 tile_width = header.tile_width;
447 tile_height = header.tile_height;
450 image_height = tile_height * tile_count / header.per_row;
451 image_width = tile_width * header.per_row;
453 /* calculate bitmap_pad */
461 tile_image = XCreateImage(dpy, DefaultVisualOfScreen(screen),
463 ZPixmap, /* format */
465 (char *) 0, /* data */
466 image_width, /* width */
467 image_height, /* height */
468 bitmap_pad, /* bit pad */
469 0); /* bytes_per_line */
472 impossible("init_tiles: insufficient memory to create image");
474 /* now we know the physical memory requirements, we can allocate space */
476 (char *) alloc((unsigned) tile_image->bytes_per_line * image_height);
478 if (appResources.double_tile_size) {
479 unsigned long *expanded_row =
480 (unsigned long *) alloc(sizeof(unsigned long) * image_width);
483 for (y = 0; y < (int) image_height; y++) {
484 for (x = 0; x < (int) image_width / 2; x++)
485 expanded_row[2 * x] = expanded_row[(2 * x) + 1] =
488 for (x = 0; x < (int) image_width; x++)
489 XPutPixel(tile_image, x, y, expanded_row[x]);
491 y++; /* duplicate row */
492 for (x = 0; x < (int) image_width; x++)
493 XPutPixel(tile_image, x, y, expanded_row[x]);
495 free((genericptr_t) expanded_row);
498 for (tb = tile_bytes, y = 0; y < (int) image_height; y++)
499 for (x = 0; x < (int) image_width; x++, tb++)
500 XPutPixel(tile_image, x, y, colors[*tb].pixel);
502 #endif /* ?USE_XPM */
504 /* fake an inverted tile by drawing a border around the edges */
506 /* use white or black as the border */
507 mask = GCFunction | GCForeground | GCGraphicsExposures;
508 values.graphics_exposures = False;
509 values.foreground = WhitePixelOfScreen(screen);
510 values.function = GXcopy;
511 tile_info->white_gc = XtGetGC(wp->w, mask, &values);
512 values.graphics_exposures = False;
513 values.foreground = BlackPixelOfScreen(screen);
514 values.function = GXcopy;
515 tile_info->black_gc = XtGetGC(wp->w, mask, &values);
518 * Use xor so we don't have to check for special colors. Xor white
519 * against the upper left pixel of the corridor so that we have a
520 * white rectangle when in a corridor.
522 mask = GCFunction | GCForeground | GCGraphicsExposures;
523 values.graphics_exposures = False;
525 WhitePixelOfScreen(screen)
526 ^ XGetPixel(tile_image, 0,
527 tile_height * glyph2tile[cmap_to_glyph(S_corr)]);
528 values.function = GXxor;
529 tile_info->white_gc = XtGetGC(wp->w, mask, &values);
531 mask = GCFunction | GCGraphicsExposures;
532 values.function = GXCopy;
533 values.graphics_exposures = False;
534 tile_info->black_gc = XtGetGC(wp->w, mask, &values);
535 #endif /* USE_WHITE */
542 free((genericptr_t) colormap);
544 free((genericptr_t) tile_bytes);
546 free((genericptr_t) colors);
549 if (result) { /* succeeded */
550 tile_info->square_height = tile_height;
551 tile_info->square_width = tile_width;
552 tile_info->square_ascent = 0;
553 tile_info->square_lbearing = 0;
554 tile_info->image_width = image_width;
555 tile_info->image_height = image_height;
562 * Make sure the map's cursor is always visible.
565 check_cursor_visibility(wp)
569 Widget viewport, horiz_sb, vert_sb;
570 float top, shown, cursor_middle;
571 Boolean do_call, adjusted = False;
576 viewport = XtParent(wp->w);
577 horiz_sb = XtNameToWidget(viewport, "horizontal");
578 vert_sb = XtNameToWidget(viewport, "vertical");
580 /* All values are relative to currently visible area */
582 #define V_BORDER 0.25 /* if this far from vert edge, shift */
583 #define H_BORDER 0.25 /* if this from from horiz edge, shift */
585 #define H_DELTA 0.25 /* distance of horiz shift */
586 #define V_DELTA 0.25 /* distance of vert shift */
589 XtSetArg(arg[0], XtNshown, &shown);
590 XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
591 XtGetValues(horiz_sb, arg, TWO);
593 /* [ALI] Don't assume map widget is the same size as actual map */
594 if (wp->map_information->is_tile)
595 cursor_middle = wp->map_information->tile_map.square_width;
597 cursor_middle = wp->map_information->text_map.square_width;
598 cursor_middle = (wp->cursx + 0.5) * cursor_middle / wp->pixel_width;
602 if (cursor_middle < top) {
604 } else if (cursor_middle < top + shown * H_BORDER) {
605 s = " close to left";
606 } else if (cursor_middle > (top + shown)) {
607 s = " outside right";
608 } else if (cursor_middle > (top + shown - shown * H_BORDER)) {
609 s = " close to right";
613 printf("Horiz: shown = %3.2f, top = %3.2f%s", shown, top, s);
616 if (cursor_middle < top) {
617 top = cursor_middle - shown * H_DELTA;
620 } else if (cursor_middle < top + shown * H_BORDER) {
621 top -= shown * H_DELTA;
624 } else if (cursor_middle > (top + shown)) {
625 top = cursor_middle - shown * H_DELTA;
628 if (top + shown > 1.0)
630 } else if (cursor_middle > (top + shown - shown * H_BORDER)) {
631 top += shown * H_DELTA;
632 if (top + shown > 1.0)
639 XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
645 XtSetArg(arg[0], XtNshown, &shown);
646 XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top);
647 XtGetValues(vert_sb, arg, TWO);
649 if (wp->map_information->is_tile)
650 cursor_middle = wp->map_information->tile_map.square_height;
652 cursor_middle = wp->map_information->text_map.square_height;
653 cursor_middle = (wp->cursy + 0.5) * cursor_middle / wp->pixel_height;
657 if (cursor_middle < top) {
659 } else if (cursor_middle < top + shown * V_BORDER) {
661 } else if (cursor_middle > (top + shown)) {
663 } else if (cursor_middle > (top + shown - shown * V_BORDER)) {
664 s = " close to bottom";
668 printf("%sVert: shown = %3.2f, top = %3.2f%s", horiz_sb ? "; " : "",
672 if (cursor_middle < top) {
673 top = cursor_middle - shown * V_DELTA;
676 } else if (cursor_middle < top + shown * V_BORDER) {
677 top -= shown * V_DELTA;
680 } else if (cursor_middle > (top + shown)) {
681 top = cursor_middle - shown * V_DELTA;
684 if (top + shown > 1.0)
686 } else if (cursor_middle > (top + shown - shown * V_BORDER)) {
687 top += shown * V_DELTA;
688 if (top + shown > 1.0)
695 XtCallCallbacks(vert_sb, XtNjumpProc, &top);
700 /* make sure cursor is displayed during dowhatis.. */
705 if (horiz_sb || vert_sb)
711 * Check to see if the viewport has grown smaller. If so, then we want to
713 * sure that the cursor is still on the screen. We do this to keep the cursor
714 * on the screen when the user resizes the nethack window.
717 map_check_size_change(wp)
720 struct map_info_t *map_info = wp->map_information;
722 Dimension new_width, new_height;
725 viewport = XtParent(wp->w);
727 XtSetArg(arg[0], XtNwidth, &new_width);
728 XtSetArg(arg[1], XtNheight, &new_height);
729 XtGetValues(viewport, arg, TWO);
731 /* Only do cursor check if new size is smaller. */
732 if (new_width < map_info->viewport_width
733 || new_height < map_info->viewport_height) {
734 /* [ALI] If the viewport was larger than the map (and so the map
735 * widget was contrained to be larger than the actual map) then we
736 * may be able to shrink the map widget as the viewport shrinks.
738 if (map_info->is_tile) {
739 wp->pixel_width = map_info->tile_map.square_width * COLNO;
740 wp->pixel_height = map_info->tile_map.square_height * ROWNO;
742 wp->pixel_width = map_info->text_map.square_width * COLNO;
743 wp->pixel_height = map_info->text_map.square_height * ROWNO;
746 if (wp->pixel_width < new_width)
747 wp->pixel_width = new_width;
748 if (wp->pixel_height < new_height)
749 wp->pixel_height = new_height;
750 XtSetArg(arg[0], XtNwidth, wp->pixel_width);
751 XtSetArg(arg[1], XtNheight, wp->pixel_height);
752 XtSetValues(wp->w, arg, TWO);
754 check_cursor_visibility(wp);
757 map_info->viewport_width = new_width;
758 map_info->viewport_height = new_height;
760 /* [ALI] These may have changed if the user has re-sized the viewport */
761 XtSetArg(arg[0], XtNwidth, &wp->pixel_width);
762 XtSetArg(arg[1], XtNheight, &wp->pixel_height);
763 XtGetValues(wp->w, arg, TWO);
767 * Fill in parameters "regular" and "inverse" with newly created GCs.
768 * Using the given background pixel and the foreground pixel optained
769 * by querying the widget with the resource name.
772 set_gc(w, font, resource_name, bgpixel, regular, inverse)
775 const char *resource_name;
777 GC *regular, *inverse;
780 XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont;
784 XtSetArg(arg[0], (char *) resource_name, &curpixel);
785 XtGetValues(w, arg, ONE);
787 values.foreground = curpixel;
788 values.background = bgpixel;
789 values.function = GXcopy;
791 *regular = XtGetGC(w, mask, &values);
792 values.foreground = bgpixel;
793 values.background = curpixel;
794 values.function = GXcopy;
796 *inverse = XtGetGC(w, mask, &values);
800 * Create the GC's for each color.
802 * I'm not sure if it is a good idea to have a GC for each color (and
803 * inverse). It might be faster to just modify the foreground and
804 * background colors on the current GC as needed.
807 get_text_gc(wp, font)
811 struct map_info_t *map_info = wp->map_information;
815 /* Get background pixel. */
816 XtSetArg(arg[0], XtNbackground, &bgpixel);
817 XtGetValues(wp->w, arg, ONE);
820 #define set_color_gc(nh_color, resource_name) \
821 set_gc(wp->w, font, resource_name, bgpixel, \
822 &map_info->text_map.color_gcs[nh_color], \
823 &map_info->text_map.inv_color_gcs[nh_color]);
825 set_color_gc(CLR_BLACK, XtNblack);
826 set_color_gc(CLR_RED, XtNred);
827 set_color_gc(CLR_GREEN, XtNgreen);
828 set_color_gc(CLR_BROWN, XtNbrown);
829 set_color_gc(CLR_BLUE, XtNblue);
830 set_color_gc(CLR_MAGENTA, XtNmagenta);
831 set_color_gc(CLR_CYAN, XtNcyan);
832 set_color_gc(CLR_GRAY, XtNgray);
833 set_color_gc(NO_COLOR, XtNforeground);
834 set_color_gc(CLR_ORANGE, XtNorange);
835 set_color_gc(CLR_BRIGHT_GREEN, XtNbright_green);
836 set_color_gc(CLR_YELLOW, XtNyellow);
837 set_color_gc(CLR_BRIGHT_BLUE, XtNbright_blue);
838 set_color_gc(CLR_BRIGHT_MAGENTA, XtNbright_magenta);
839 set_color_gc(CLR_BRIGHT_CYAN, XtNbright_cyan);
840 set_color_gc(CLR_WHITE, XtNwhite);
842 set_gc(wp->w, font, XtNforeground, bgpixel, &map_info->text_map.copy_gc,
843 &map_info->text_map.inv_copy_gc);
848 * Display the cursor on the map window.
854 /* Redisplay the cursor location inverted. */
855 map_update(wp, wp->cursy, wp->cursy, wp->cursx, wp->cursx, TRUE);
859 * Check if there are any changed characters. If so, then plaster them on
863 display_map_window(wp)
867 struct map_info_t *map_info = wp->map_information;
869 if ((Is_rogue_level(&u.uz) ? map_info->is_tile
870 : (map_info->is_tile != iflags.wc_tiled_map))
871 && map_info->tile_map.image_width) {
872 /* changed map display mode, re-display the full map */
873 (void) memset((genericptr_t) map_info->t_start, (char) 0,
874 sizeof(map_info->t_start));
875 (void) memset((genericptr_t) map_info->t_stop, (char) (COLNO - 1),
876 sizeof(map_info->t_stop));
877 map_info->is_tile = iflags.wc_tiled_map && !Is_rogue_level(&u.uz);
878 XClearWindow(XtDisplay(wp->w), XtWindow(wp->w));
879 set_map_size(wp, COLNO, ROWNO);
880 check_cursor_visibility(wp);
881 } else if (wp->prevx != wp->cursx || wp->prevy != wp->cursy) {
882 register unsigned int x = wp->prevx, y = wp->prevy;
885 * Previous cursor position is not the same as the current
886 * cursor position, update the old cursor position.
888 if (x < map_info->t_start[y])
889 map_info->t_start[y] = x;
890 if (x > map_info->t_stop[y])
891 map_info->t_stop[y] = x;
894 for (row = 0; row < ROWNO; row++) {
895 if (map_info->t_start[row] <= map_info->t_stop[row]) {
896 map_update(wp, row, row, (int) map_info->t_start[row],
897 (int) map_info->t_stop[row], FALSE);
898 map_info->t_start[row] = COLNO - 1;
899 map_info->t_stop[row] = 0;
903 wp->prevx = wp->cursx; /* adjust old cursor position */
904 wp->prevy = wp->cursy;
908 * Set all map tiles to S_stone
911 map_all_stone(map_info)
912 struct map_info_t *map_info;
915 unsigned short stone = cmap_to_glyph(S_stone);
917 for (x = 0; x < COLNO; x++)
918 for (y = 0; y < ROWNO; y++) {
919 map_info->tile_map.glyphs[y][x].glyph = stone;
920 map_info->tile_map.glyphs[y][x].special = 0;
925 * Fill the saved screen characters with the "clear" tile or character.
927 * Flush out everything by resetting the "new" bounds and calling
928 * display_map_window().
934 struct map_info_t *map_info = wp->map_information;
936 /* update both tile and text backing store, then update */
938 map_all_stone(map_info);
939 (void) memset((genericptr_t) map_info->text_map.text, ' ',
940 sizeof(map_info->text_map.text));
942 (void) memset((genericptr_t) map_info->text_map.colors, NO_COLOR,
943 sizeof(map_info->text_map.colors));
946 /* force a full update */
947 (void) memset((genericptr_t) map_info->t_start, (char) 0,
948 sizeof(map_info->t_start));
949 (void) memset((genericptr_t) map_info->t_stop, (char) COLNO - 1,
950 sizeof(map_info->t_stop));
951 display_map_window(wp);
955 * Retreive the font associated with the map window and save attributes
956 * that are used when updating it.
963 struct map_info_t *map_info = wp->map_information;
964 struct text_map_info_t *text_map = &map_info->text_map;
966 fs = WindowFontStruct(wp->w);
967 text_map->square_width = fs->max_bounds.width;
968 text_map->square_height = fs->max_bounds.ascent + fs->max_bounds.descent;
969 text_map->square_ascent = fs->max_bounds.ascent;
970 text_map->square_lbearing = -fs->min_bounds.lbearing;
973 printf("Font information:\n");
974 printf("fid = %ld, direction = %d\n", fs->fid, fs->direction);
975 printf("first = %d, last = %d\n", fs->min_char_or_byte2,
976 fs->max_char_or_byte2);
977 printf("all chars exist? %s\n", fs->all_chars_exist ? "yes" : "no");
978 printf("min_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n",
979 fs->min_bounds.lbearing, fs->min_bounds.rbearing,
980 fs->min_bounds.width, fs->min_bounds.ascent,
981 fs->min_bounds.descent, fs->min_bounds.attributes);
982 printf("max_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n",
983 fs->max_bounds.lbearing, fs->max_bounds.rbearing,
984 fs->max_bounds.width, fs->max_bounds.ascent,
985 fs->max_bounds.descent, fs->max_bounds.attributes);
986 printf("per_char = 0x%lx\n", (unsigned long) fs->per_char);
987 printf("Text: (max) width = %d, height = %d\n", text_map->square_width,
988 text_map->square_height);
991 if (fs->min_bounds.width != fs->max_bounds.width)
992 X11_raw_print("Warning: map font is not monospaced!");
998 #define INBUF_SIZE 64
999 int inbuf[INBUF_SIZE];
1001 int inptr = 0; /* points to valid data */
1004 * Keyboard and button event handler for map window.
1007 map_input(w, event, params, num_params)
1011 Cardinal *num_params;
1014 XButtonEvent *button;
1015 boolean meta = FALSE;
1017 Cardinal in_nparams = (num_params ? *num_params : 0);
1019 char keystring[MAX_KEY_STRING];
1021 switch (event->type) {
1023 button = (XButtonEvent *) event;
1024 #ifdef VERBOSE_INPUT
1025 printf("button press\n");
1027 if (in_nparams > 0 && (nbytes = strlen(params[0])) < MAX_KEY_STRING) {
1028 Strcpy(keystring, params[0]);
1029 key = (XKeyEvent *) event; /* just in case */
1032 if (w != window_list[WIN_MAP].w) {
1033 #ifdef VERBOSE_INPUT
1034 printf("map_input called from wrong window\n");
1039 set_button_values(w, button->x, button->y, button->button);
1042 #ifdef VERBOSE_INPUT
1045 if (appResources.slow && input_func) {
1046 (*input_func)(w, event, params, num_params);
1051 * Don't use key_event_to_char() because we want to be able
1052 * to allow keys mapped to multiple characters.
1054 key = (XKeyEvent *) event;
1055 if (in_nparams > 0 && (nbytes = strlen(params[0])) < MAX_KEY_STRING) {
1056 Strcpy(keystring, params[0]);
1059 * Assume that mod1 is really the meta key.
1061 meta = !!(key->state & Mod1Mask);
1062 nbytes = XLookupString(key, keystring, MAX_KEY_STRING,
1063 (KeySym *) 0, (XComposeStatus *) 0);
1066 /* Modifier keys return a zero length string when pressed. */
1068 #ifdef VERBOSE_INPUT
1071 for (i = 0; i < nbytes; i++) {
1074 if (incount < INBUF_SIZE) {
1075 inbuf[(inptr + incount) % INBUF_SIZE] =
1076 ((int) c) + (meta ? 0x80 : 0);
1081 #ifdef VERBOSE_INPUT
1082 if (meta) /* meta will print as M<c> */
1083 (void) putchar('M');
1084 if (c < ' ') { /* ctrl will print as ^<c> */
1085 (void) putchar('^');
1091 #ifdef VERBOSE_INPUT
1092 printf("\" [%d bytes]\n", nbytes);
1098 impossible("unexpected X event, type = %d\n", (int) event->type);
1104 set_button_values(w, x, y, button)
1108 unsigned int button;
1111 struct map_info_t *map_info;
1113 wp = find_widget(w);
1114 map_info = wp->map_information;
1116 if (map_info->is_tile) {
1117 click_x = x / map_info->tile_map.square_width;
1118 click_y = y / map_info->tile_map.square_height;
1120 click_x = x / map_info->text_map.square_width;
1121 click_y = y / map_info->text_map.square_height;
1124 /* The values can be out of range if the map window has been resized
1125 to be larger than the max size. */
1126 if (click_x >= COLNO)
1127 click_x = COLNO - 1;
1128 if (click_y >= ROWNO)
1129 click_y = ROWNO - 1;
1131 /* Map all buttons but the first to the second click */
1132 click_button = (button == Button1) ? CLICK_1 : CLICK_2;
1136 * Map window expose callback.
1140 map_exposed(w, client_data, widget_data)
1142 XtPointer client_data; /* unused */
1143 XtPointer widget_data; /* expose event from Window widget */
1147 struct map_info_t *map_info;
1148 unsigned width, height;
1149 int start_row, stop_row, start_col, stop_col;
1150 XExposeEvent *event = (XExposeEvent *) widget_data;
1151 int t_height, t_width; /* tile/text height & width */
1155 if (!XtIsRealized(w) || event->count > 0)
1158 wp = find_widget(w);
1159 map_info = wp->map_information;
1160 if (wp->keep_window && !map_info)
1163 * The map is sent an expose event when the viewport resizes. Make sure
1164 * that the cursor is still in the viewport after the resize.
1166 map_check_size_change(wp);
1168 if (event) { /* called from button-event */
1171 width = event->width;
1172 height = event->height;
1176 width = wp->pixel_width;
1177 height = wp->pixel_height;
1180 * Convert pixels into INCLUSIVE text rows and columns.
1182 if (map_info->is_tile) {
1183 t_height = map_info->tile_map.square_height;
1184 t_width = map_info->tile_map.square_width;
1186 t_height = map_info->text_map.square_height;
1187 t_width = map_info->text_map.square_width;
1189 start_row = y / t_height;
1190 stop_row = ((y + height) / t_height)
1191 + ((((y + height) % t_height) == 0) ? 0 : 1) - 1;
1193 start_col = x / t_width;
1194 stop_col = ((x + width) / t_width)
1195 + ((((x + width) % t_width) == 0) ? 0 : 1) - 1;
1198 printf("map_exposed: x = %d, y = %d, width = %d, height = %d\n", x, y,
1200 printf("chars %d x %d, rows %d to %d, columns %d to %d\n", t_height,
1201 t_width, start_row, stop_row, start_col, stop_col);
1204 /* Out of range values are possible if the map window is resized to be
1205 bigger than the largest expected value. */
1206 if (stop_row >= ROWNO)
1207 stop_row = ROWNO - 1;
1208 if (stop_col >= COLNO)
1209 stop_col = COLNO - 1;
1211 map_update(wp, start_row, stop_row, start_col, stop_col, FALSE);
1212 display_cursor(wp); /* make sure cursor shows up */
1216 * Do the actual work of the putting characters onto our X window. This
1217 * is called from the expose event routine, the display window (flush)
1218 * routine, and the display cursor routine. The later involves inverting
1219 * the foreground and background colors, which are also inverted when the
1220 * position's color is above CLR_MAX.
1222 * This works for rectangular regions (this includes one line rectangles).
1223 * The start and stop columns are *inclusive*.
1226 map_update(wp, start_row, stop_row, start_col, stop_col, inverted)
1228 int start_row, stop_row, start_col, stop_col;
1231 int win_start_row, win_start_col;
1232 struct map_info_t *map_info = wp->map_information;
1236 if (start_row < 0 || stop_row >= ROWNO) {
1237 impossible("map_update: bad row range %d-%d\n", start_row, stop_row);
1240 if (start_col < 0 || stop_col >= COLNO) {
1241 impossible("map_update: bad col range %d-%d\n", start_col, stop_col);
1245 #ifdef VERBOSE_UPDATE
1246 printf("update: [0x%x] %d %d %d %d\n", (int) wp->w, start_row, stop_row,
1247 start_col, stop_col);
1249 win_start_row = start_row;
1250 win_start_col = start_col;
1252 if (map_info->is_tile) {
1253 struct tile_map_info_t *tile_map = &map_info->tile_map;
1255 Display *dpy = XtDisplay(wp->w);
1256 Screen *screen = DefaultScreenOfDisplay(dpy);
1258 for (row = start_row; row <= stop_row; row++) {
1259 for (cur_col = start_col; cur_col <= stop_col; cur_col++) {
1260 int glyph = tile_map->glyphs[row][cur_col].glyph;
1261 int tile = glyph2tile[glyph];
1263 int dest_x = cur_col * tile_map->square_width;
1264 int dest_y = row * tile_map->square_height;
1266 src_x = (tile % TILES_PER_ROW) * tile_width;
1267 src_y = (tile / TILES_PER_ROW) * tile_height;
1268 XCopyArea(dpy, tile_pixmap, XtWindow(wp->w),
1269 tile_map->black_gc, /* no grapics_expose */
1270 src_x, src_y, tile_width, tile_height, dest_x,
1273 if (glyph_is_pet(glyph) && iflags.hilite_pet) {
1274 /* draw pet annotation (a heart) */
1275 XSetForeground(dpy, tile_map->black_gc,
1276 pet_annotation.foreground);
1277 XSetClipOrigin(dpy, tile_map->black_gc, dest_x, dest_y);
1278 XSetClipMask(dpy, tile_map->black_gc,
1279 pet_annotation.bitmap);
1280 XCopyPlane(dpy, pet_annotation.bitmap, XtWindow(wp->w),
1281 tile_map->black_gc, 0, 0, pet_annotation.width,
1282 pet_annotation.height, dest_x, dest_y, 1);
1283 XSetClipOrigin(dpy, tile_map->black_gc, 0, 0);
1284 XSetClipMask(dpy, tile_map->black_gc, None);
1285 XSetForeground(dpy, tile_map->black_gc,
1286 BlackPixelOfScreen(screen));
1288 if ((tile_map->glyphs[row][cur_col].special & MG_OBJPILE)) {
1289 /* draw object pile annotation (a plus sign) */
1290 XSetForeground(dpy, tile_map->black_gc,
1291 pile_annotation.foreground);
1292 XSetClipOrigin(dpy, tile_map->black_gc, dest_x, dest_y);
1293 XSetClipMask(dpy, tile_map->black_gc,
1294 pile_annotation.bitmap);
1295 XCopyPlane(dpy, pile_annotation.bitmap, XtWindow(wp->w),
1296 tile_map->black_gc, 0, 0,
1297 pile_annotation.width, pile_annotation.height,
1299 XSetClipOrigin(dpy, tile_map->black_gc, 0, 0);
1300 XSetClipMask(dpy, tile_map->black_gc, None);
1301 XSetForeground(dpy, tile_map->black_gc,
1302 BlackPixelOfScreen(screen));
1308 XDrawRectangle(XtDisplay(wp->w), XtWindow(wp->w),
1310 /* kludge for white square... */
1311 (tile_map->glyphs[start_row][start_col].glyph
1312 == cmap_to_glyph(S_ice))
1313 ? tile_map->black_gc
1314 : tile_map->white_gc,
1318 start_col * tile_map->square_width,
1319 start_row * tile_map->square_height,
1320 tile_map->square_width - 1,
1321 tile_map->square_height - 1);
1324 struct text_map_info_t *text_map = &map_info->text_map;
1327 if (iflags.use_color) {
1328 register char *c_ptr;
1330 int cur_col, color, win_ystart;
1333 for (row = start_row; row <= stop_row; row++) {
1335 text_map->square_ascent + (row * text_map->square_height);
1337 t_ptr = (char *) &(text_map->text[row][start_col]);
1338 c_ptr = (char *) &(text_map->colors[row][start_col]);
1339 cur_col = start_col;
1340 while (cur_col <= stop_col) {
1344 while ((cur_col + count) <= stop_col && *c_ptr == color) {
1348 if (color >= CLR_MAX) {
1353 XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w),
1354 cur_inv ? text_map->inv_color_gcs[color]
1355 : text_map->color_gcs[color],
1356 text_map->square_lbearing
1357 + (text_map->square_width * cur_col),
1358 win_ystart, t_ptr, count);
1360 /* move text pointer and column count */
1366 #endif /* TEXTCOLOR */
1368 int win_row, win_xstart;
1370 /* We always start at the same x window position and have
1371 the same character count. */
1372 win_xstart = text_map->square_lbearing
1373 + (win_start_col * text_map->square_width);
1374 count = stop_col - start_col + 1;
1376 for (row = start_row, win_row = win_start_row; row <= stop_row;
1378 XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w),
1379 inverted ? text_map->inv_copy_gc
1380 : text_map->copy_gc,
1382 text_map->square_ascent
1383 + (win_row * text_map->square_height),
1384 (char *) &(text_map->text[row][start_col]),
1391 /* Adjust the number of rows and columns on the given map window */
1393 set_map_size(wp, cols, rows)
1395 Dimension cols, rows;
1400 if (wp->map_information->is_tile) {
1401 wp->pixel_width = wp->map_information->tile_map.square_width * cols;
1402 wp->pixel_height = wp->map_information->tile_map.square_height * rows;
1404 wp->pixel_width = wp->map_information->text_map.square_width * cols;
1405 wp->pixel_height = wp->map_information->text_map.square_height * rows;
1409 XtSetArg(args[num_args], XtNwidth, wp->pixel_width);
1411 XtSetArg(args[num_args], XtNheight, wp->pixel_height);
1413 XtSetValues(wp->w, args, num_args);
1420 struct map_info_t *map_info = wp->map_information;
1421 struct text_map_info_t *text_map = &map_info->text_map;
1423 (void) memset((genericptr_t) text_map->text, ' ', sizeof(text_map->text));
1425 (void) memset((genericptr_t) text_map->colors, NO_COLOR,
1426 sizeof(text_map->colors));
1430 get_text_gc(wp, WindowFont(wp->w));
1433 static char map_translations[] = "#override\n\
1434 <Key>Left: scroll(4)\n\
1435 <Key>Right: scroll(6)\n\
1436 <Key>Up: scroll(8)\n\
1437 <Key>Down: scroll(2)\n\
1442 * The map window creation routine.
1445 create_map_window(wp, create_popup, parent)
1447 boolean create_popup; /* parent is a popup shell that we create */
1450 struct map_info_t *map_info; /* map info pointer */
1451 Widget map, viewport;
1454 Dimension rows, columns;
1456 int screen_width, screen_height;
1463 * Create a popup that accepts key and button events.
1466 XtSetArg(args[num_args], XtNinput, False);
1469 wp->popup = parent = XtCreatePopupShell("nethack",
1470 topLevelShellWidgetClass,
1471 toplevel, args, num_args);
1473 * If we're here, then this is an auxiliary map window. If we're
1474 * cancelled via a delete window message, we should just pop down.
1479 XtSetArg(args[num_args], XtNallowHoriz, True);
1481 XtSetArg(args[num_args], XtNallowVert, True);
1484 XtSetArg(args[num_args], XtNforceBars, True);
1487 XtSetArg(args[num_args], XtNuseBottom, True);
1489 XtSetArg(args[num_args], XtNtranslations,
1490 XtParseTranslationTable(map_translations));
1492 viewport = XtCreateManagedWidget("map_viewport", /* name */
1493 viewportWidgetClass, /* from Window.h */
1494 parent, /* parent widget */
1495 args, /* set some values */
1496 num_args); /* number of values to set */
1499 * Create a map window. We need to set the width and height to some
1500 * value when we create it. We will change it to the value we want
1504 XtSetArg(args[num_args], XtNwidth, 100);
1506 XtSetArg(args[num_args], XtNheight, 100);
1508 XtSetArg(args[num_args], XtNtranslations,
1509 XtParseTranslationTable(map_translations));
1512 wp->w = map = XtCreateManagedWidget(
1514 windowWidgetClass, /* widget class from Window.h */
1515 viewport, /* parent widget */
1516 args, /* set some values */
1517 num_args); /* number of values to set */
1519 XtAddCallback(map, XtNexposeCallback, map_exposed, (XtPointer) 0);
1521 map_info = wp->map_information =
1522 (struct map_info_t *) alloc(sizeof(struct map_info_t));
1524 map_info->viewport_width = map_info->viewport_height = 0;
1526 /* reset the "new entry" indicators */
1527 (void) memset((genericptr_t) map_info->t_start, (char) COLNO,
1528 sizeof(map_info->t_start));
1529 (void) memset((genericptr_t) map_info->t_stop, (char) 0,
1530 sizeof(map_info->t_stop));
1532 /* we probably want to restrict this to the 1st map window only */
1533 map_info->is_tile = (init_tiles(wp) && iflags.wc_tiled_map);
1537 * Initially, set the map widget to be the size specified by the
1538 * widget rows and columns resources. We need to do this to
1539 * correctly set the viewport window size. After the viewport is
1540 * realized, then the map can resize to its normal size.
1543 XtSetArg(args[num_args], nhStr(XtNrows), &rows);
1545 XtSetArg(args[num_args], nhStr(XtNcolumns), &columns);
1547 XtGetValues(wp->w, args, num_args);
1549 /* Don't bother with windows larger than ROWNOxCOLNO. */
1550 if (columns > COLNO)
1555 set_map_size(wp, columns, rows);
1558 * If we have created our own popup, then realize it so that the
1559 * viewport is also realized. Then resize the map window.
1562 XtRealizeWidget(wp->popup);
1563 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
1564 &wm_delete_window, 1);
1565 set_map_size(wp, COLNO, ROWNO);
1568 map_all_stone(map_info);
1572 * Destroy this map window.
1575 destroy_map_window(wp)
1578 struct map_info_t *map_info = wp->map_information;
1581 nh_XtPopdown(wp->popup);
1584 struct text_map_info_t *text_map = &map_info->text_map;
1586 /* Free allocated GCs. */
1590 for (i = 0; i < CLR_MAX; i++) {
1591 XtReleaseGC(wp->w, text_map->color_gcs[i]);
1592 XtReleaseGC(wp->w, text_map->inv_color_gcs[i]);
1595 XtReleaseGC(wp->w, text_map->copy_gc);
1596 XtReleaseGC(wp->w, text_map->inv_copy_gc);
1599 /* Free malloc'ed space. */
1600 free((genericptr_t) map_info);
1601 wp->map_information = 0;
1604 /* Destroy map widget. */
1605 if (wp->popup && !wp->keep_window)
1606 XtDestroyWidget(wp->popup), wp->popup = (Widget) 0;
1608 if (wp->keep_window)
1609 XtRemoveCallback(wp->w, XtNexposeCallback, map_exposed,
1612 wp->type = NHW_NONE; /* allow re-use */
1615 boolean exit_x_event; /* exit condition for the event loop */
1622 printf("key = '%s%c'\n", (k < 32) ? "^" : "", (k < 32) ? '@' + k : k);
1627 * Main X event loop. Here we accept and dispatch X events. We only exit
1628 * under certain circumstances.
1631 x_event(exit_condition)
1636 boolean keep_going = TRUE;
1638 /* Hold globals so function is re-entrant */
1639 boolean hold_exit_x_event = exit_x_event;
1641 click_button = NO_CLICK; /* reset click exit condition */
1642 exit_x_event = FALSE; /* reset callback exit condition */
1645 * Loop until we get a sent event, callback exit, or are accepting key
1646 * press and button press events and we receive one.
1648 if ((exit_condition == EXIT_ON_KEY_PRESS
1649 || exit_condition == EXIT_ON_KEY_OR_BUTTON_PRESS) && incount)
1653 XtAppNextEvent(app_context, &event);
1654 XtDispatchEvent(&event);
1656 /* See if we can exit. */
1658 switch (exit_condition) {
1659 case EXIT_ON_SENT_EVENT: {
1660 XAnyEvent *any = (XAnyEvent *) &event;
1662 if (any->send_event) {
1675 case EXIT_ON_KEY_PRESS:
1677 /* get first pressed key */
1679 retval = inbuf[inptr];
1680 inptr = (inptr + 1) % INBUF_SIZE;
1683 } else if (program_state.done_hup) {
1685 inptr = (inptr + 1) % INBUF_SIZE;
1689 case EXIT_ON_KEY_OR_BUTTON_PRESS:
1690 if (incount != 0 || click_button != NO_CLICK) {
1691 if (click_button != NO_CLICK) { /* button press */
1692 /* click values are already set */
1694 } else { /* key press */
1695 /* get first pressed key */
1697 retval = inbuf[inptr];
1698 inptr = (inptr + 1) % INBUF_SIZE;
1702 } else if (program_state.done_hup) {
1704 inptr = (inptr + 1) % INBUF_SIZE;
1709 panic("x_event: unknown exit condition %d", exit_condition);
1712 } while (keep_going);
1714 /* Restore globals */
1715 exit_x_event = hold_exit_x_event;