1 /* SCCS Id: @(#)winmap.c 3.4 1996/04/05 */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
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 */
49 extern short glyph2tile[];
50 extern int total_tiles_used;
52 /* Define these if you really want a lot of junk on your screen. */
53 /* #define VERBOSE */ /* print various info & events as they happen */
54 /* #define VERBOSE_UPDATE */ /* print screen update bounds */
55 /* #define VERBOSE_INPUT */ /* print input events */
58 #define USE_WHITE /* almost always use white as a tile cursor border */
61 static boolean FDECL(init_tiles, (struct xwindow *));
62 static void FDECL(set_button_values, (Widget,int,int,unsigned));
63 static void FDECL(map_check_size_change, (struct xwindow *));
64 static void FDECL(map_update, (struct xwindow *,int,int,int,int,BOOLEAN_P));
65 static void FDECL(init_text, (struct xwindow *));
66 static void FDECL(map_exposed, (Widget,XtPointer,XtPointer));
67 static void FDECL(set_gc, (Widget,Font,char *,Pixel,GC *,GC *));
68 static void FDECL(get_text_gc, (struct xwindow *,Font));
69 static void FDECL(get_char_info, (struct xwindow *));
70 static void FDECL(display_cursor, (struct xwindow *));
72 /* Global functions ======================================================== */
75 X11_print_glyph(window, x, y, glyph)
80 struct map_info_t *map_info;
84 if (window_list[window].type != NHW_MAP) {
85 impossible("print_glyph: can (currently) only print to map windows");
88 map_info = window_list[window].map_information;
90 if (map_info->is_tile) {
91 unsigned short *t_ptr;
93 t_ptr = &map_info->mtype.tile_map->glyphs[y][x];
95 if (*t_ptr != glyph) {
103 register unsigned char *ch_ptr;
107 register unsigned char *co_ptr;
109 /* map glyph to character and color */
110 mapglyph(glyph, &och, &color, &special, x, y);
113 /* Only update if we need to. */
114 ch_ptr = &map_info->mtype.text_map->text[y][x];
117 co_ptr = &map_info->mtype.text_map->colors[y][x];
118 if (*ch_ptr != ch || *co_ptr != color)
132 if (update_bbox) { /* update row bbox */
133 if ((uchar) x < map_info->t_start[y]) map_info->t_start[y] = x;
134 if ((uchar) x > map_info->t_stop[y]) map_info->t_stop[y] = x;
140 * The is the tty clip call. Since X can resize at any time, we can't depend
141 * on this being defined.
144 void X11_cliparound(x, y) int x, y; { }
145 #endif /* CLIPPING */
147 /* End global functions ==================================================== */
149 #include "tile2x11.h"
152 * We're expecting to never read more than one tile file per session.
153 * If this is false, then we can make an array of this information,
154 * or just keep it on a per-window basis.
156 Pixmap tile_pixmap = None;
157 static int tile_width;
158 static int tile_height;
159 static int tile_count;
160 static XImage *tile_image = 0;
163 * This structure is used for small bitmaps that are used for annotating
164 * tiles. For example, a "heart" annotates pets.
166 struct tile_annotation {
169 unsigned int width, height;
170 int hotx, hoty; /* not currently used */
173 static struct tile_annotation pet_annotation;
176 init_annotation(annotation, filename, colorpixel)
177 struct tile_annotation *annotation;
181 Display *dpy = XtDisplay(toplevel);
183 if (0!=XReadBitmapFile(dpy, XtWindow(toplevel), filename,
184 &annotation->width, &annotation->height, &annotation->bitmap,
185 &annotation->hotx, &annotation->hoty)) {
187 Sprintf(buf, "Failed to load %s", filename);
191 annotation->foreground = colorpixel;
195 * Put the tile image on the server.
197 * We can't send the image to the server until the top level
198 * is realized. When the tile file is first processed, the top
199 * level is not realized. This routine is called after we
200 * realize the top level, but before we start resizing the
206 Display *dpy = XtDisplay(toplevel);
207 unsigned int width, height;
209 if (tile_image == 0) return; /* no tiles */
211 height = tile_image->height;
212 width = tile_image->width;
214 tile_pixmap = XCreatePixmap(dpy, XtWindow(toplevel),
217 DefaultDepth(dpy, DefaultScreen(dpy)));
219 XPutImage(dpy, tile_pixmap,
220 DefaultGC(dpy, DefaultScreen(dpy)),
222 0,0, 0,0, /* src, dest top left */
226 XDestroyImage(tile_image); /* data bytes free'd also */
229 init_annotation(&pet_annotation,
230 appResources.pet_mark_bitmap, appResources.pet_mark_color);
235 * Open and read the tile file. Return TRUE if there were no problems.
236 * Return FALSE otherwise.
243 XpmAttributes attributes;
246 FILE *fp = (FILE *)0;
248 unsigned char *cp, *colormap = (unsigned char *)0;
249 unsigned char *tb, *tile_bytes = (unsigned char *)0;
251 XColor *colors = (XColor *)0;
257 Display *dpy = XtDisplay(toplevel);
258 Screen *screen = DefaultScreenOfDisplay(dpy);
259 struct map_info_t *map_info = (struct map_info_t *)0;
260 struct tile_map_info_t *tile_info = (struct tile_map_info_t *)0;
261 unsigned int image_height = 0, image_width = 0;
262 boolean result = TRUE;
266 /* already have tile information */
267 if (tile_pixmap != None) goto tiledone;
269 map_info = wp->map_information;
270 tile_info = map_info->mtype.tile_map =
271 (struct tile_map_info_t *) alloc(sizeof(struct tile_map_info_t));
272 (void) memset((genericptr_t) tile_info, 0,
273 sizeof(struct tile_map_info_t));
276 attributes.valuemask = XpmCloseness;
277 attributes.closeness = 25000;
279 errorcode = XpmReadFileToImage(dpy, appResources.tile_file,
280 &tile_image, 0, &attributes);
282 if (errorcode == XpmColorFailed) {
283 Sprintf(buf, "Insufficient colors available to load %s.",
284 appResources.tile_file);
286 X11_raw_print("Try closing other colorful applications and restart.");
287 X11_raw_print("Attempting to load with inferior colors.");
288 attributes.closeness = 50000;
289 errorcode = XpmReadFileToImage(dpy, appResources.tile_file,
290 &tile_image, 0, &attributes);
293 if (errorcode != XpmSuccess) {
294 if (errorcode == XpmColorFailed) {
295 Sprintf(buf, "Insufficient colors available to load %s.",
296 appResources.tile_file);
299 Sprintf(buf, "Failed to load %s: %s", appResources.tile_file,
300 XpmGetErrorString(errorcode));
304 X11_raw_print("Switching to text-based mode.");
308 /* assume a fixed number of tiles per row */
309 if (tile_image->width % TILES_PER_ROW != 0 ||
310 tile_image->width <= TILES_PER_ROW) {
312 "%s is not a multiple of %d (number of tiles/row) pixels wide",
313 appResources.tile_file, TILES_PER_ROW);
315 XDestroyImage(tile_image);
321 /* infer tile dimensions from image size and TILES_PER_ROW */
322 image_width = tile_image->width;
323 image_height = tile_image->height;
325 tile_count = total_tiles_used;
326 if ((tile_count % TILES_PER_ROW) != 0) {
327 tile_count += TILES_PER_ROW - (tile_count % TILES_PER_ROW);
329 tile_width = image_width / TILES_PER_ROW;
330 tile_height = image_height / (tile_count / TILES_PER_ROW);
332 /* any less than 16 colours makes tiles useless */
333 ddepth = DefaultDepthOfScreen(screen);
335 X11_raw_print("need a screen depth of at least 4");
340 fp = fopen_datafile(appResources.tile_file, RDBMODE, FALSE);
342 X11_raw_print("can't open tile file");
347 if (fread((char *) &header, sizeof(header), 1, fp) != 1) {
348 X11_raw_print("read of header failed");
353 if (header.version != 2) {
354 Sprintf(buf, "Wrong tile file version, expected 2, got %lu",
362 fprintf(stderr, "X11 tile file:\n version %ld\n ncolors %ld\n tile width %ld\n tile height %ld\n per row %ld\n ntiles %ld\n",
371 size = 3*header.ncolors;
372 colormap = (unsigned char *) alloc((unsigned)size);
373 if (fread((char *) colormap, 1, size, fp) != size) {
374 X11_raw_print("read of colormap failed");
379 /* defined in decl.h - these are _not_ good defines to have */
384 colors = (XColor *) alloc(sizeof(XColor) * (unsigned)header.ncolors);
385 for (i = 0; i < header.ncolors; i++) {
386 cp = colormap + (3 * i);
387 colors[i].red = cp[0] * 256;
388 colors[i].green = cp[1] * 256;
389 colors[i].blue = cp[2] * 256;
393 if (!XAllocColor(dpy, DefaultColormapOfScreen(screen), &colors[i]) &&
394 !nhApproxColor(screen, DefaultColormapOfScreen(screen),
395 (char *)0, &colors[i])) {
396 Sprintf(buf, "%dth out of %ld color allocation failed",
404 size = header.tile_height * header.tile_width;
406 * This alloc() and the one below require 32-bit ints, since tile_bytes
407 * is currently ~200k and alloc() takes an int
409 tile_count = header.ntiles;
410 if ((tile_count % header.per_row) != 0) {
411 tile_count += header.per_row - (tile_count % header.per_row);
413 tile_bytes = (unsigned char *) alloc((unsigned)tile_count*size);
414 if (fread((char *) tile_bytes, size, tile_count, fp) != tile_count) {
415 X11_raw_print("read of tile bytes failed");
420 if (header.ntiles < total_tiles_used) {
421 Sprintf(buf, "tile file incomplete, expecting %d tiles, found %lu",
422 total_tiles_used, header.ntiles);
429 if (appResources.double_tile_size) {
430 tile_width = 2*header.tile_width;
431 tile_height = 2*header.tile_height;
433 tile_width = header.tile_width;
434 tile_height = header.tile_height;
437 image_height = tile_height * tile_count / header.per_row;
438 image_width = tile_width * header.per_row;
440 /* calculate bitmap_pad */
448 tile_image = XCreateImage(dpy, DefaultVisualOfScreen(screen),
450 ZPixmap, /* format */
453 image_width, /* width */
454 image_height, /* height */
455 bitmap_pad, /* bit pad */
456 0); /* bytes_per_line */
459 impossible("init_tiles: insufficient memory to create image");
461 /* now we know the physical memory requirements, we can allocate space */
463 (char *) alloc((unsigned)tile_image->bytes_per_line * image_height);
465 if (appResources.double_tile_size) {
466 unsigned long *expanded_row =
467 (unsigned long *)alloc(sizeof(unsigned long)*(unsigned)image_width);
470 for (y = 0; y < image_height; y++) {
471 for (x = 0; x < image_width/2; x++)
473 expanded_row[(2*x)+1] = colors[*tb++].pixel;
475 for (x = 0; x < image_width; x++)
476 XPutPixel(tile_image, x, y, expanded_row[x]);
478 y++; /* duplicate row */
479 for (x = 0; x < image_width; x++)
480 XPutPixel(tile_image, x, y, expanded_row[x]);
482 free((genericptr_t)expanded_row);
486 for (tb = tile_bytes, y = 0; y < image_height; y++)
487 for (x = 0; x < image_width; x++, tb++)
488 XPutPixel(tile_image, x, y, colors[*tb].pixel);
492 /* fake an inverted tile by drawing a border around the edges */
494 /* use white or black as the border */
495 mask = GCFunction | GCForeground | GCGraphicsExposures;
496 values.graphics_exposures = False;
497 values.foreground = WhitePixelOfScreen(screen);
498 values.function = GXcopy;
499 tile_info->white_gc = XtGetGC(wp->w, mask, &values);
500 values.graphics_exposures = False;
501 values.foreground = BlackPixelOfScreen(screen);
502 values.function = GXcopy;
503 tile_info->black_gc = XtGetGC(wp->w, mask, &values);
506 * Use xor so we don't have to check for special colors. Xor white
507 * against the upper left pixel of the corridor so that we have a
508 * white rectangle when in a corridor.
510 mask = GCFunction | GCForeground | GCGraphicsExposures;
511 values.graphics_exposures = False;
512 values.foreground = WhitePixelOfScreen(screen) ^
513 XGetPixel(tile_image, 0, tile_height*glyph2tile[cmap_to_glyph(S_corr)]);
514 values.function = GXxor;
515 tile_info->white_gc = XtGetGC(wp->w, mask, &values);
517 mask = GCFunction | GCGraphicsExposures;
518 values.function = GXCopy;
519 values.graphics_exposures = False;
520 tile_info->black_gc = XtGetGC(wp->w, mask, &values);
521 #endif /* USE_WHITE */
525 if (fp) (void) fclose(fp);
526 if (colormap) free((genericptr_t)colormap);
527 if (tile_bytes) free((genericptr_t)tile_bytes);
528 if (colors) free((genericptr_t)colors);
531 if (result) { /* succeeded */
532 map_info->square_height = tile_height;
533 map_info->square_width = tile_width;
534 map_info->square_ascent = 0;
535 map_info->square_lbearing = 0;
536 tile_info->image_width = image_width;
537 tile_info->image_height = image_height;
539 if (tile_info) free((genericptr_t)tile_info);
548 * Make sure the map's cursor is always visible.
551 check_cursor_visibility(wp)
555 Widget viewport, horiz_sb, vert_sb;
556 float top, shown, cursor_middle;
557 Boolean do_call, adjusted = False;
562 viewport = XtParent(wp->w);
563 horiz_sb = XtNameToWidget(viewport, "horizontal");
564 vert_sb = XtNameToWidget(viewport, "vertical");
566 /* All values are relative to currently visible area */
568 #define V_BORDER 0.3 /* if this far from vert edge, shift */
569 #define H_BORDER 0.3 /* if this from from horiz edge, shift */
571 #define H_DELTA 0.4 /* distance of horiz shift */
572 #define V_DELTA 0.4 /* distance of vert shift */
575 XtSetArg(arg[0], XtNshown, &shown);
576 XtSetArg(arg[1], XtNtopOfThumb, &top);
577 XtGetValues(horiz_sb, arg, TWO);
579 /* [ALI] Don't assume map widget is the same size as actual map */
580 cursor_middle = (wp->cursx + 0.5) * wp->map_information->square_width /
585 if (cursor_middle < top) {
587 } else if (cursor_middle < top + shown*H_BORDER) {
588 s = " close to left";
589 } else if (cursor_middle > (top + shown)) {
590 s = " outside right";
591 } else if (cursor_middle > (top + shown - shown*H_BORDER)) {
592 s = " close to right";
596 printf("Horiz: shown = %3.2f, top = %3.2f%s", shown, top, s);
599 if (cursor_middle < top) {
600 top = cursor_middle - shown*H_DELTA;
601 if (top < 0.0) top = 0.0;
602 } else if (cursor_middle < top + shown*H_BORDER) {
603 top -= shown*H_DELTA;
604 if (top < 0.0) top = 0.0;
605 } else if (cursor_middle > (top + shown)) {
606 top = cursor_middle - shown*H_DELTA;
607 if (top < 0.0) top = 0.0;
608 if (top + shown > 1.0) top = 1.0 - shown;
609 } else if (cursor_middle > (top + shown - shown*H_BORDER)) {
610 top += shown*H_DELTA;
611 if (top + shown > 1.0) top = 1.0 - shown;
617 XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
623 XtSetArg(arg[0], XtNshown, &shown);
624 XtSetArg(arg[1], XtNtopOfThumb, &top);
625 XtGetValues(vert_sb, arg, TWO);
627 cursor_middle = (wp->cursy + 0.5) * wp->map_information->square_height /
632 if (cursor_middle < top) {
634 } else if (cursor_middle < top + shown*V_BORDER) {
636 } else if (cursor_middle > (top + shown)) {
638 } else if (cursor_middle > (top + shown - shown*V_BORDER)) {
639 s = " close to bottom";
643 printf("%sVert: shown = %3.2f, top = %3.2f%s",
644 horiz_sb ? "; " : "", shown, top, s);
647 if (cursor_middle < top) {
648 top = cursor_middle - shown*V_DELTA;
649 if (top < 0.0) top = 0.0;
650 } else if (cursor_middle < top + shown*V_BORDER) {
651 top -= shown*V_DELTA;
652 if (top < 0.0) top = 0.0;
653 } else if (cursor_middle > (top + shown)) {
654 top = cursor_middle - shown*V_DELTA;
655 if (top < 0.0) top = 0.0;
656 if (top + shown > 1.0) top = 1.0 - shown;
657 } else if (cursor_middle > (top + shown - shown*V_BORDER)) {
658 top += shown*V_DELTA;
659 if (top + shown > 1.0) top = 1.0 - shown;
665 XtCallCallbacks(vert_sb, XtNjumpProc, &top);
670 /* make sure cursor is displayed during dowhatis.. */
671 if (adjusted) display_cursor(wp);
674 if (horiz_sb || vert_sb) printf("\n");
680 * Check to see if the viewport has grown smaller. If so, then we want to make
681 * sure that the cursor is still on the screen. We do this to keep the cursor
682 * on the screen when the user resizes the nethack window.
685 map_check_size_change(wp)
688 struct map_info_t *map_info = wp->map_information;
690 Dimension new_width, new_height;
693 viewport = XtParent(wp->w);
695 XtSetArg(arg[0], XtNwidth, &new_width);
696 XtSetArg(arg[1], XtNheight, &new_height);
697 XtGetValues(viewport, arg, TWO);
699 /* Only do cursor check if new size is smaller. */
700 if (new_width < map_info->viewport_width
701 || new_height < map_info->viewport_height) {
702 /* [ALI] If the viewport was larger than the map (and so the map
703 * widget was contrained to be larger than the actual map) then we
704 * may be able to shrink the map widget as the viewport shrinks.
706 wp->pixel_width = map_info->square_width * COLNO;
707 if (wp->pixel_width < new_width)
708 wp->pixel_width = new_width;
709 wp->pixel_height = map_info->square_height * ROWNO;
710 if (wp->pixel_height < new_height)
711 wp->pixel_height = new_height;
712 XtSetArg(arg[0], XtNwidth, wp->pixel_width);
713 XtSetArg(arg[1], XtNheight, wp->pixel_height);
714 XtSetValues(wp->w, arg, TWO);
716 check_cursor_visibility(wp);
719 map_info->viewport_width = new_width;
720 map_info->viewport_height = new_height;
722 /* [ALI] These may have changed if the user has re-sized the viewport */
723 XtSetArg(arg[0], XtNwidth, &wp->pixel_width);
724 XtSetArg(arg[1], XtNheight, &wp->pixel_height);
725 XtGetValues(wp->w, arg, TWO);
729 * Fill in parameters "regular" and "inverse" with newly created GCs.
730 * Using the given background pixel and the foreground pixel optained
731 * by querying the widget with the resource name.
734 set_gc(w, font, resource_name, bgpixel, regular, inverse)
739 GC *regular, *inverse;
742 XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont;
746 XtSetArg(arg[0], resource_name, &curpixel);
747 XtGetValues(w, arg, ONE);
749 values.foreground = curpixel;
750 values.background = bgpixel;
751 values.function = GXcopy;
753 *regular = XtGetGC(w, mask, &values);
754 values.foreground = bgpixel;
755 values.background = curpixel;
756 values.function = GXcopy;
758 *inverse = XtGetGC(w, mask, &values);
762 * Create the GC's for each color.
764 * I'm not sure if it is a good idea to have a GC for each color (and
765 * inverse). It might be faster to just modify the foreground and
766 * background colors on the current GC as needed.
769 get_text_gc(wp, font)
773 struct map_info_t *map_info = wp->map_information;
777 /* Get background pixel. */
778 XtSetArg(arg[0], XtNbackground, &bgpixel);
779 XtGetValues(wp->w, arg, ONE);
782 #define set_color_gc(nh_color, resource_name) \
783 set_gc(wp->w, font, resource_name, bgpixel, \
784 &map_info->mtype.text_map->color_gcs[nh_color], \
785 &map_info->mtype.text_map->inv_color_gcs[nh_color]);
787 set_color_gc(CLR_BLACK, XtNblack);
788 set_color_gc(CLR_RED, XtNred);
789 set_color_gc(CLR_GREEN, XtNgreen);
790 set_color_gc(CLR_BROWN, XtNbrown);
791 set_color_gc(CLR_BLUE, XtNblue);
792 set_color_gc(CLR_MAGENTA, XtNmagenta);
793 set_color_gc(CLR_CYAN, XtNcyan);
794 set_color_gc(CLR_GRAY, XtNgray);
795 set_color_gc(NO_COLOR, XtNforeground);
796 set_color_gc(CLR_ORANGE, XtNorange);
797 set_color_gc(CLR_BRIGHT_GREEN, XtNbright_green);
798 set_color_gc(CLR_YELLOW, XtNyellow);
799 set_color_gc(CLR_BRIGHT_BLUE, XtNbright_blue);
800 set_color_gc(CLR_BRIGHT_MAGENTA, XtNbright_magenta);
801 set_color_gc(CLR_BRIGHT_CYAN, XtNbright_cyan);
802 set_color_gc(CLR_WHITE, XtNwhite);
804 set_gc(wp->w, font, XtNforeground, bgpixel,
805 &map_info->mtype.text_map->copy_gc,
806 &map_info->mtype.text_map->inv_copy_gc);
812 * Display the cursor on the map window.
818 /* Redisplay the cursor location inverted. */
819 map_update(wp, wp->cursy, wp->cursy, wp->cursx, wp->cursx, TRUE);
824 * Check if there are any changed characters. If so, then plaster them on
828 display_map_window(wp)
832 struct map_info_t *map_info = wp->map_information;
835 * If the previous cursor position is not the same as the current
836 * cursor position, then update the old cursor position.
838 if (wp->prevx != wp->cursx || wp->prevy != wp->cursy) {
839 register unsigned int x = wp->prevx, y = wp->prevy;
840 if (x < map_info->t_start[y]) map_info->t_start[y] = x;
841 if (x > map_info->t_stop[y]) map_info->t_stop[y] = x;
844 for (row = 0; row < ROWNO; row++) {
845 if (map_info->t_start[row] <= map_info->t_stop[row]) {
846 map_update(wp, row, row,
847 (int) map_info->t_start[row],
848 (int) map_info->t_stop[row], FALSE);
849 map_info->t_start[row] = COLNO-1;
850 map_info->t_stop[row] = 0;
854 wp->prevx = wp->cursx; /* adjust old cursor position */
855 wp->prevy = wp->cursy;
859 * Set all map tiles to S_stone
862 map_all_stone(map_info)
863 struct map_info_t *map_info;
866 unsigned short *sp, stone;
867 stone = cmap_to_glyph(S_stone);
869 for (sp = (unsigned short *) map_info->mtype.tile_map->glyphs, i = 0;
870 i < ROWNO*COLNO; sp++, i++)
876 * Fill the saved screen characters with the "clear" tile or character.
878 * Flush out everything by resetting the "new" bounds and calling
879 * display_map_window().
885 struct map_info_t *map_info = wp->map_information;
887 if (map_info->is_tile) {
888 map_all_stone(map_info);
890 /* Fill text with spaces, and update */
891 (void) memset((genericptr_t) map_info->mtype.text_map->text, ' ',
892 sizeof(map_info->mtype.text_map->text));
894 (void) memset((genericptr_t) map_info->mtype.text_map->colors, NO_COLOR,
895 sizeof(map_info->mtype.text_map->colors));
899 /* force a full update */
900 (void) memset((genericptr_t) map_info->t_start, (char) 0,
901 sizeof(map_info->t_start));
902 (void) memset((genericptr_t) map_info->t_stop, (char) COLNO-1,
903 sizeof(map_info->t_stop));
904 display_map_window(wp);
908 * Retreive the font associated with the map window and save attributes
909 * that are used when updating it.
916 struct map_info_t *map_info = wp->map_information;
918 fs = WindowFontStruct(wp->w);
919 map_info->square_width = fs->max_bounds.width;
920 map_info->square_height = fs->max_bounds.ascent + fs->max_bounds.descent;
921 map_info->square_ascent = fs->max_bounds.ascent;
922 map_info->square_lbearing = -fs->min_bounds.lbearing;
925 printf("Font information:\n");
926 printf("fid = %ld, direction = %d\n", fs->fid, fs->direction);
927 printf("first = %d, last = %d\n",
928 fs->min_char_or_byte2, fs->max_char_or_byte2);
929 printf("all chars exist? %s\n", fs->all_chars_exist?"yes":"no");
930 printf("min_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n",
931 fs->min_bounds.lbearing, fs->min_bounds.rbearing,
932 fs->min_bounds.width, fs->min_bounds.ascent,
933 fs->min_bounds.descent, fs->min_bounds.attributes);
934 printf("max_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n",
935 fs->max_bounds.lbearing, fs->max_bounds.rbearing,
936 fs->max_bounds.width, fs->max_bounds.ascent,
937 fs->max_bounds.descent, fs->max_bounds.attributes);
938 printf("per_char = 0x%lx\n", (unsigned long) fs->per_char);
939 printf("Text: (max) width = %d, height = %d\n",
940 map_info->square_width, map_info->square_height);
943 if (fs->min_bounds.width != fs->max_bounds.width)
944 X11_raw_print("Warning: map font is not monospaced!");
950 #define INBUF_SIZE 64
951 int inbuf[INBUF_SIZE];
953 int inptr = 0; /* points to valid data */
957 * Keyboard and button event handler for map window.
960 map_input(w, event, params, num_params)
964 Cardinal *num_params;
967 XButtonEvent *button;
968 boolean meta = FALSE;
970 Cardinal in_nparams = (num_params ? *num_params : 0);
972 char keystring[MAX_KEY_STRING];
974 switch (event->type) {
976 button = (XButtonEvent *) event;
978 printf("button press\n");
980 if (in_nparams > 0 &&
981 (nbytes = strlen(params[0])) < MAX_KEY_STRING) {
982 Strcpy(keystring, params[0]);
983 key = (XKeyEvent *) event; /* just in case */
986 if (w != window_list[WIN_MAP].w) {
988 printf("map_input called from wrong window\n");
993 set_button_values(w, button->x, button->y, button->button);
999 if(appResources.slow && input_func) {
1000 (*input_func)(w, event, params, num_params);
1005 * Don't use key_event_to_char() because we want to be able
1006 * to allow keys mapped to multiple characters.
1008 key = (XKeyEvent *) event;
1009 if (in_nparams > 0 &&
1010 (nbytes = strlen(params[0])) < MAX_KEY_STRING) {
1011 Strcpy(keystring, params[0]);
1014 * Assume that mod1 is really the meta key.
1016 meta = !!(key->state & Mod1Mask);
1018 XLookupString(key, keystring, MAX_KEY_STRING,
1019 (KeySym *)0, (XComposeStatus *)0);
1022 /* Modifier keys return a zero length string when pressed. */
1024 #ifdef VERBOSE_INPUT
1027 for (i = 0; i < nbytes; i++) {
1030 if (incount < INBUF_SIZE) {
1031 inbuf[(inptr+incount)%INBUF_SIZE] =
1032 ((int) c) + (meta ? 0x80 : 0);
1037 #ifdef VERBOSE_INPUT
1038 if (meta) /* meta will print as M<c> */
1039 (void) putchar('M');
1040 if (c < ' ') { /* ctrl will print as ^<c> */
1041 (void) putchar('^');
1047 #ifdef VERBOSE_INPUT
1048 printf("\" [%d bytes]\n", nbytes);
1054 impossible("unexpected X event, type = %d\n", (int) event->type);
1060 set_button_values(w, x, y, button)
1064 unsigned int button;
1067 struct map_info_t *map_info;
1069 wp = find_widget(w);
1070 map_info = wp->map_information;
1072 click_x = x / map_info->square_width;
1073 click_y = y / map_info->square_height;
1075 /* The values can be out of range if the map window has been resized */
1076 /* to be larger than the max size. */
1077 if (click_x >= COLNO) click_x = COLNO-1;
1078 if (click_y >= ROWNO) click_x = ROWNO-1;
1080 /* Map all buttons but the first to the second click */
1081 click_button = (button == Button1) ? CLICK_1 : CLICK_2;
1085 * Map window expose callback.
1089 map_exposed(w, client_data, widget_data)
1091 XtPointer client_data; /* unused */
1092 XtPointer widget_data; /* expose event from Window widget */
1096 struct map_info_t *map_info;
1097 unsigned width, height;
1098 int start_row, stop_row, start_col, stop_col;
1099 XExposeEvent *event = (XExposeEvent *) widget_data;
1100 int t_height, t_width; /* tile/text height & width */
1102 if (!XtIsRealized(w) || event->count > 0) return;
1104 wp = find_widget(w);
1105 map_info = wp->map_information;
1106 if (wp->keep_window && !map_info) return;
1108 * The map is sent an expose event when the viewport resizes. Make sure
1109 * that the cursor is still in the viewport after the resize.
1111 map_check_size_change(wp);
1113 if (event) { /* called from button-event */
1116 width = event->width;
1117 height = event->height;
1121 width = wp->pixel_width;
1122 height= wp->pixel_height;
1125 * Convert pixels into INCLUSIVE text rows and columns.
1127 t_height = map_info->square_height;
1128 t_width = map_info->square_width;
1129 start_row = y / t_height;
1130 stop_row = ((y + height) / t_height) +
1131 ((((y + height) % t_height) == 0) ? 0 : 1) - 1;
1133 start_col = x / t_width;
1134 stop_col = ((x + width) / t_width) +
1135 ((((x + width) % t_width) == 0) ? 0 : 1) - 1;
1138 printf("map_exposed: x = %d, y = %d, width = %d, height = %d\n",
1139 x, y, width, height);
1140 printf("chars %d x %d, rows %d to %d, columns %d to %d\n",
1141 map_info->square_height, map_info->square_width,
1142 start_row, stop_row, start_col, stop_col);
1145 /* Out of range values are possible if the map window is resized to be */
1146 /* bigger than the largest expected value. */
1147 if (stop_row >= ROWNO) stop_row = ROWNO-1;
1148 if (stop_col >= COLNO) stop_col = COLNO-1;
1150 map_update(wp, start_row, stop_row, start_col, stop_col, FALSE);
1151 display_cursor(wp); /* make sure cursor shows up */
1155 * Do the actual work of the putting characters onto our X window. This
1156 * is called from the expose event routine, the display window (flush)
1157 * routine, and the display cursor routine. The later is a kludge that
1158 * involves the inverted parameter of this function. A better solution
1159 * would be to double the color count, with any color above CLR_MAX
1162 * This works for rectangular regions (this includes one line rectangles).
1163 * The start and stop columns are *inclusive*.
1166 map_update(wp, start_row, stop_row, start_col, stop_col, inverted)
1168 int start_row, stop_row, start_col, stop_col;
1171 int win_start_row, win_start_col;
1172 struct map_info_t *map_info = wp->map_information;
1176 if (start_row < 0 || stop_row >= ROWNO) {
1177 impossible("map_update: bad row range %d-%d\n", start_row, stop_row);
1180 if (start_col < 0 || stop_col >=COLNO) {
1181 impossible("map_update: bad col range %d-%d\n", start_col, stop_col);
1185 #ifdef VERBOSE_UPDATE
1186 printf("update: [0x%x] %d %d %d %d\n",
1187 (int) wp->w, start_row, stop_row, start_col, stop_col);
1189 win_start_row = start_row;
1190 win_start_col = start_col;
1192 if (map_info->is_tile) {
1193 struct tile_map_info_t *tile_map = map_info->mtype.tile_map;
1195 Display* dpy = XtDisplay(wp->w);
1196 Screen* screen = DefaultScreenOfDisplay(dpy);
1198 for (row = start_row; row <= stop_row; row++) {
1199 for (cur_col = start_col; cur_col <= stop_col; cur_col++) {
1200 int glyph = tile_map->glyphs[row][cur_col];
1201 int tile = glyph2tile[glyph];
1203 int dest_x = cur_col * map_info->square_width;
1204 int dest_y = row * map_info->square_height;
1206 src_x = (tile % TILES_PER_ROW) * tile_width;
1207 src_y = (tile / TILES_PER_ROW) * tile_height;
1208 XCopyArea(dpy, tile_pixmap, XtWindow(wp->w),
1209 tile_map->black_gc, /* no grapics_expose */
1211 tile_width, tile_height,
1214 if (glyph_is_pet(glyph) && iflags.hilite_pet) {
1215 /* draw pet annotation (a heart) */
1216 XSetForeground(dpy, tile_map->black_gc, pet_annotation.foreground);
1217 XSetClipOrigin(dpy, tile_map->black_gc, dest_x, dest_y);
1218 XSetClipMask(dpy, tile_map->black_gc, pet_annotation.bitmap);
1221 pet_annotation.bitmap,
1225 pet_annotation.width,pet_annotation.height,
1229 XSetClipOrigin(dpy, tile_map->black_gc, 0, 0);
1230 XSetClipMask(dpy, tile_map->black_gc, None);
1231 XSetForeground(dpy, tile_map->black_gc, BlackPixelOfScreen(screen));
1237 XDrawRectangle(XtDisplay(wp->w), XtWindow(wp->w),
1239 /* kludge for white square... */
1240 tile_map->glyphs[start_row][start_col] ==
1241 cmap_to_glyph(S_ice) ?
1242 tile_map->black_gc : tile_map->white_gc,
1246 start_col * map_info->square_width,
1247 start_row * map_info->square_height,
1248 map_info->square_width-1,
1249 map_info->square_height-1);
1252 struct text_map_info_t *text_map = map_info->mtype.text_map;
1255 if (iflags.use_color) {
1256 register char *c_ptr;
1258 int cur_col, color, win_ystart;
1260 for (row = start_row; row <= stop_row; row++) {
1261 win_ystart = map_info->square_ascent +
1262 (row * map_info->square_height);
1264 t_ptr = (char *) &(text_map->text[row][start_col]);
1265 c_ptr = (char *) &(text_map->colors[row][start_col]);
1266 cur_col = start_col;
1267 while (cur_col <= stop_col) {
1270 while ((cur_col + count) <= stop_col && *c_ptr == color) {
1275 XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w),
1276 inverted ? text_map->inv_color_gcs[color] :
1277 text_map->color_gcs[color],
1278 map_info->square_lbearing + (map_info->square_width * cur_col),
1282 /* move text pointer and column count */
1288 #endif /* TEXTCOLOR */
1290 int win_row, win_xstart;
1292 /* We always start at the same x window position and have */
1293 /* the same character count. */
1294 win_xstart = map_info->square_lbearing +
1295 (win_start_col * map_info->square_width);
1296 count = stop_col - start_col + 1;
1298 for (row = start_row, win_row = win_start_row;
1299 row <= stop_row; row++, win_row++) {
1301 XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w),
1302 inverted ? text_map->inv_copy_gc : text_map->copy_gc,
1304 map_info->square_ascent + (win_row * map_info->square_height),
1305 (char *) &(text_map->text[row][start_col]), count);
1311 /* Adjust the number of rows and columns on the given map window */
1313 set_map_size(wp, cols, rows)
1315 Dimension cols, rows;
1320 wp->pixel_width = wp->map_information->square_width * cols;
1321 wp->pixel_height = wp->map_information->square_height * rows;
1324 XtSetArg(args[num_args], XtNwidth, wp->pixel_width); num_args++;
1325 XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++;
1326 XtSetValues(wp->w, args, num_args);
1335 struct map_info_t *map_info = wp->map_information;
1336 struct text_map_info_t *text_map;
1338 map_info->is_tile = FALSE;
1339 text_map = map_info->mtype.text_map =
1340 (struct text_map_info_t *) alloc(sizeof(struct text_map_info_t));
1342 (void) memset((genericptr_t) text_map->text, ' ', sizeof(text_map->text));
1344 (void) memset((genericptr_t) text_map->colors, NO_COLOR,
1345 sizeof(text_map->colors));
1349 get_text_gc(wp, WindowFont(wp->w));
1352 static char map_translations[] =
1354 <Key>Left: scroll(4)\n\
1355 <Key>Right: scroll(6)\n\
1356 <Key>Up: scroll(8)\n\
1357 <Key>Down: scroll(2)\n\
1362 * The map window creation routine.
1365 create_map_window(wp, create_popup, parent)
1367 boolean create_popup; /* parent is a popup shell that we create */
1370 struct map_info_t *map_info; /* map info pointer */
1371 Widget map, viewport;
1374 Dimension rows, columns;
1376 int screen_width, screen_height;
1383 * Create a popup that accepts key and button events.
1386 XtSetArg(args[num_args], XtNinput, False); num_args++;
1388 wp->popup = parent = XtCreatePopupShell("nethack",
1389 topLevelShellWidgetClass,
1390 toplevel, args, num_args);
1392 * If we're here, then this is an auxiliary map window. If we're
1393 * cancelled via a delete window message, we should just pop down.
1398 XtSetArg(args[num_args], XtNallowHoriz, True); num_args++;
1399 XtSetArg(args[num_args], XtNallowVert, True); num_args++;
1400 /* XtSetArg(args[num_args], XtNforceBars, True); num_args++; */
1401 XtSetArg(args[num_args], XtNuseBottom, True); num_args++;
1402 XtSetArg(args[num_args], XtNtranslations,
1403 XtParseTranslationTable(map_translations)); num_args++;
1404 viewport = XtCreateManagedWidget(
1405 "map_viewport", /* name */
1406 viewportWidgetClass, /* widget class from Window.h */
1407 parent, /* parent widget */
1408 args, /* set some values */
1409 num_args); /* number of values to set */
1412 * Create a map window. We need to set the width and height to some
1413 * value when we create it. We will change it to the value we want
1417 XtSetArg(args[num_args], XtNwidth, 100); num_args++;
1418 XtSetArg(args[num_args], XtNheight, 100); num_args++;
1419 XtSetArg(args[num_args], XtNtranslations,
1420 XtParseTranslationTable(map_translations)); num_args++;
1422 wp->w = map = XtCreateManagedWidget(
1424 windowWidgetClass, /* widget class from Window.h */
1425 viewport, /* parent widget */
1426 args, /* set some values */
1427 num_args); /* number of values to set */
1429 XtAddCallback(map, XtNexposeCallback, map_exposed, (XtPointer) 0);
1431 map_info = wp->map_information =
1432 (struct map_info_t *) alloc(sizeof(struct map_info_t));
1434 map_info->viewport_width = map_info->viewport_height = 0;
1436 /* reset the "new entry" indicators */
1437 (void) memset((genericptr_t) map_info->t_start, (char) COLNO,
1438 sizeof(map_info->t_start));
1439 (void) memset((genericptr_t) map_info->t_stop, (char) 0,
1440 sizeof(map_info->t_stop));
1442 /* we probably want to restrict this to the 1st map window only */
1443 if (appResources.tile_file[0] && init_tiles(wp)) {
1444 map_info->is_tile = TRUE;
1447 map_info->is_tile = FALSE;
1452 * Initially, set the map widget to be the size specified by the
1453 * widget rows and columns resources. We need to do this to
1454 * correctly set the viewport window size. After the viewport is
1455 * realized, then the map can resize to its normal size.
1458 XtSetArg(args[num_args], XtNrows, &rows); num_args++;
1459 XtSetArg(args[num_args], XtNcolumns, &columns); num_args++;
1460 XtGetValues(wp->w, args, num_args);
1462 /* Don't bother with windows larger than ROWNOxCOLNO. */
1463 if (columns > COLNO) columns = COLNO;
1464 if (rows > ROWNO) rows = ROWNO;
1466 #if 0 /* This is insufficient. We now resize final window in winX.c */
1468 * Check for overrunning the size of the screen. This does an ad hoc
1471 * Width: We expect that there is nothing but borders on either side
1472 * of the map window. Use some arbitrary width to decide
1475 * Height: if the map takes up more than 1/2 of the screen height, start
1476 * reducing its size.
1478 screen_height = HeightOfScreen(XtScreen(wp->w));
1479 screen_width = WidthOfScreen(XtScreen(wp->w));
1482 if ((int)(columns*map_info->square_width) > screen_width-WOFF) {
1483 columns = (screen_width-WOFF) / map_info->square_width;
1484 if (columns == 0) columns = 1;
1487 if ((int)(rows*map_info->square_height) > screen_height/2) {
1488 rows = screen_height / (2*map_info->square_height);
1489 if (rows == 0) rows = 1;
1493 set_map_size(wp, columns, rows);
1497 * If we have created our own popup, then realize it so that the
1498 * viewport is also realized. Then resize the map window.
1501 XtRealizeWidget(wp->popup);
1502 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
1503 &wm_delete_window, 1);
1504 set_map_size(wp, COLNO, ROWNO);
1507 if (map_info->is_tile) {
1508 map_all_stone(map_info);
1513 * Destroy this map window.
1516 destroy_map_window(wp)
1519 struct map_info_t *map_info = wp->map_information;
1522 nh_XtPopdown(wp->popup);
1525 struct text_map_info_t *text_map = map_info->mtype.text_map;
1527 /* Free allocated GCs. */
1528 if (!map_info->is_tile) {
1532 for (i = 0; i < CLR_MAX; i++) {
1533 XtReleaseGC(wp->w, text_map->color_gcs[i]);
1534 XtReleaseGC(wp->w, text_map->inv_color_gcs[i]);
1537 XtReleaseGC(wp->w, text_map->copy_gc);
1538 XtReleaseGC(wp->w, text_map->inv_copy_gc);
1541 /* free alloc'ed text information */
1542 free((genericptr_t)text_map), map_info->mtype.text_map = 0;
1544 /* Free malloc'ed space. */
1545 free((genericptr_t)map_info), wp->map_information = 0;
1548 /* Destroy map widget. */
1549 if (wp->popup && !wp->keep_window)
1550 XtDestroyWidget(wp->popup), wp->popup = (Widget)0;
1552 if (wp->keep_window)
1553 XtRemoveCallback(wp->w, XtNexposeCallback, map_exposed, (XtPointer)0);
1555 wp->type = NHW_NONE; /* allow re-use */
1560 boolean exit_x_event; /* exit condition for the event loop */
1565 printf("key = '%s%c'\n", (k<32) ? "^":"", (k<32) ? '@'+k : k);
1570 * Main X event loop. Here we accept and dispatch X events. We only exit
1571 * under certain circumstances.
1574 x_event(exit_condition)
1579 boolean keep_going = TRUE;
1581 /* Hold globals so function is re-entrant */
1582 boolean hold_exit_x_event = exit_x_event;
1584 click_button = NO_CLICK; /* reset click exit condition */
1585 exit_x_event = FALSE; /* reset callback exit condition */
1588 * Loop until we get a sent event, callback exit, or are accepting key
1589 * press and button press events and we receive one.
1591 if((exit_condition == EXIT_ON_KEY_PRESS ||
1592 exit_condition == EXIT_ON_KEY_OR_BUTTON_PRESS) && incount)
1596 XtAppNextEvent(app_context, &event);
1597 XtDispatchEvent(&event);
1599 /* See if we can exit. */
1601 switch (exit_condition) {
1602 case EXIT_ON_SENT_EVENT: {
1603 XAnyEvent *any = (XAnyEvent *) &event;
1604 if (any->send_event) {
1617 case EXIT_ON_KEY_PRESS:
1619 /* get first pressed key */
1621 retval = inbuf[inptr];
1622 inptr = (inptr+1) % INBUF_SIZE;
1627 case EXIT_ON_KEY_OR_BUTTON_PRESS:
1628 if (incount != 0 || click_button != NO_CLICK) {
1629 if (click_button != NO_CLICK) { /* button press */
1630 /* click values are already set */
1632 } else { /* key press */
1633 /* get first pressed key */
1635 retval = inbuf[inptr];
1636 inptr = (inptr+1) % INBUF_SIZE;
1643 panic("x_event: unknown exit condition %d", exit_condition);
1646 } while (keep_going);
1648 /* Restore globals */
1649 exit_x_event = hold_exit_x_event;