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