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
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*/
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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*/