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