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