/*	SCCS Id: @(#)winmap.c	3.1	92/04/30		  */
/* Copyright (c) Dean Luick, 1992				  */
/* NetHack may be freely redistributed.  See license for details. */

/*
 * This file contains:
 *	+ global functions print_glyph() and cliparound()
 * 	+ the map window routines
 *	+ the char and pointer input routines
 *
 * Notes:
 *	+ We don't really have a good way to get the compiled ROWNO and 
 *	  COLNO as defaults.  They are hardwired to the current "correct"
 *	  values in the Window widget.  I am _not_ in favor of including
 *	  some nethack include file for Window.c.
 */
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/Scrollbar.h>
#include <X11/Xaw/Viewport.h>
#include "Window.h"	/* map widget declarations */

#include "hack.h"
#include "winX.h"

/* Define these if you really want a lot of junk on your screen. */
/* #define VERBOSE		/* print various info & events as they happen */
/* #define VERBOSE_UPDATE	/* print screen update bounds */
/* #define VERBOSE_INPUT	/* print input events */

static void set_button_values();
static void map_check_size_change();
static void map_update();
static void map_exposed();
static void map_input();
static void set_gc();
static void get_gc();
static void get_char_info();
static void display_cursor();

/* Global functions ======================================================== */

void
X11_print_glyph(window, x, y, glyph)
    winid window;
    xchar x, y;
    int glyph;
{
    uchar	      ch;
    register int      offset;
    struct map_info_t *map_info;
    register unsigned char *ch_ptr;
#ifdef TEXTCOLOR
    int     color;
    register unsigned char *co_ptr;

#define zap_color(n)  color = zapcolors[n]
#define cmap_color(n) color = defsyms[n].color
#define trap_color(n) color = (n == WEB) ? defsyms[S_web ].color : \
					   defsyms[S_trap].color
#define obj_color(n)  color = objects[n].oc_color
#define mon_color(n)  color = mons[n].mcolor
#define pet_color(n)  color = mons[n].mcolor

# else /* no text color */

#define zap_color(n)
#define cmap_color(n)
#define trap_color(n)
#define obj_color(n)
#define mon_color(n)
#define pet_color(n)
#endif

    check_winid(window);
    if (window_list[window].type != NHW_MAP) {
	impossible("print_glyph: can (currently) only print to map windows");
	return;
    }
    map_info = window_list[window].map_information;

    /*
     *  Map the glyph back to a character.
     *
     *  Warning:  For speed, this makes an assumption on the order of
     *            offsets.  The order is set in display.h.
     */
    if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) {		/* swallow */
	/* see swallow_to_glyph() in display.c */
	ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)];
	mon_color(offset >> 3);
    } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) {	/* zap beam */
	/* see zapdir_to_glyph() in display.c */
	ch = showsyms[S_vbeam + (offset & 0x3)];
	zap_color((offset >> 2));
    } else if ((offset = (glyph - GLYPH_CMAP_OFF)) >= 0) {	/* cmap */
	ch = showsyms[offset];
	cmap_color(offset);
    } else if ((offset = (glyph - GLYPH_TRAP_OFF)) >= 0) {	/* trap */
	ch = (offset == WEB) ? showsyms[S_web] : showsyms[S_trap];
	trap_color(offset);
    } else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) {	/* object */
	ch = oc_syms[objects[offset].oc_class];
	obj_color(offset);
    } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) {	/* a corpse */
	ch = oc_syms[objects[CORPSE].oc_class];
	mon_color(offset);
    } else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) {	/* a pet */
	ch = monsyms[mons[offset].mlet];
	pet_color(offset);
    } else {							/* a monster */
	ch = monsyms[mons[glyph].mlet];
	mon_color(glyph);
    }

    /* Only update if we need to. */
    ch_ptr = &map_info->text[y][x];

#ifdef TEXTCOLOR
    co_ptr = &map_info->colors[y][x];
    if (*ch_ptr != ch || *co_ptr != color)
#else
    if (*ch_ptr != ch)
#endif
    {
	*ch_ptr = ch;
#ifdef TEXTCOLOR
	*co_ptr = color;
#endif
	/* update row bbox */
	if ((uchar) x < map_info->t_start[y]) map_info->t_start[y] = x;
	if ((uchar) x > map_info->t_stop[y])  map_info->t_stop[y]  = x;
    }

#undef zap_color
#undef cmap_color
#undef trap_color
#undef obj_color
#undef mon_color
#undef pet_color
}

#ifdef CLIPPING
/*
 * The is the tty clip call.  Since X can resize at any time, we can't depend
 * on this being defined.
 */
/*ARGSUSED*/
void X11_cliparound(x, y) int x, y; { }
#endif /* CLIPPING */

/* End global functions ==================================================== */


/*
 * Make sure the map's cursor is always visible.
 */
void
check_cursor_visibility(wp)
    struct xwindow *wp;
{
    Arg arg[2];
    Widget viewport, horiz_sb, vert_sb;
    float top, shown, cursor_middle;
    Boolean do_call, adjusted = False;
#ifdef VERBOSE
    char *s;
#endif

    viewport = XtParent(wp->w);
    horiz_sb = XtNameToWidget(viewport, "horizontal");
    vert_sb  = XtNameToWidget(viewport, "vertical");

#define V_BORDER 0.1		/* if this far from vert edge, shift */
#define H_BORDER 0.0625		/* if this from from horiz edge, shift */

#define H_DELTA 0.25		/* distance of horiz shift */
				/* vert shift is half of curr distance */
/* The V_DELTA is 1/2 the value of shown. */

    if (horiz_sb) {
	XtSetArg(arg[0], XtNshown,	&shown);
	XtSetArg(arg[1], XtNtopOfThumb, &top);
	XtGetValues(horiz_sb, arg, TWO);

	cursor_middle = (((float) wp->cursx) + 0.5) / (float) COLNO;
	do_call = True;

#ifdef VERBOSE
	if (cursor_middle < top) {
	    s = " outside left";
	} else if (cursor_middle < top + H_BORDER) {
	    s = " close to left";
	} else if (cursor_middle > (top + shown)) {
	    s = " outside right";
	} else if (cursor_middle > (top + shown - H_BORDER)) {
	    s = " close to right";
	} else {
	    s = "";
	}
	printf("Horiz: shown = %3.2f, top = %3.2f%s", shown, top, s);
#endif

	if (cursor_middle < top) {
	    top = cursor_middle - H_DELTA;
	    if (top < 0.0) top = 0;
	} else if (cursor_middle < top + H_BORDER) {
	    top -= H_DELTA;
	    if (top < 0.0) top = 0.0;
	} else if (cursor_middle > (top + shown)) {
	    top = cursor_middle + H_DELTA;
	    if (top + shown > 1.0) top = 1.0 - shown;
	} else if (cursor_middle > (top + shown - H_BORDER)) {
	    top += H_DELTA;
	    if (top + shown > 1.0) top = 1.0 - shown;
	} else {
	    do_call = False;
	}

	if (do_call) {
	    XtCallCallbacks(horiz_sb, XtNjumpProc, &top);
	    adjusted = True;
	}
    }

    if (vert_sb) {
	XtSetArg(arg[0], XtNshown,      &shown);
	XtSetArg(arg[1], XtNtopOfThumb, &top);
	XtGetValues(vert_sb, arg, TWO);

	cursor_middle = (((float) wp->cursy) + 0.5) / (float) ROWNO;
	do_call = True;

#ifdef VERBOSE
	if (cursor_middle < top) {
	    s = " above top";
	} else if (cursor_middle < top + V_BORDER) {
	    s = " close to top";
	} else if (cursor_middle > (top + shown)) {
	    s = " below bottom";
	} else if (cursor_middle > (top + shown - V_BORDER)) {
	    s = " close to bottom";
	} else {
	    s = "";
	}
	printf("%sVert: shown = %3.2f, top = %3.2f%s",
				    horiz_sb ? ";  " : "", shown, top, s);
#endif

	if (cursor_middle < top) {
	    top = cursor_middle - (shown / 2.0);
	    if (top < 0.0) top = 0;
	} else if (cursor_middle < top + V_BORDER) {
	    top -= shown / 2.0;
	    if (top < 0.0) top = 0;
	} else if (cursor_middle > (top + shown)) {
	    top = cursor_middle - (shown / 2.0);
	    if (top < 0.0) top = 0;
	    if (top + shown > 1.0) top = 1.0 - shown;
	} else if (cursor_middle > (top + shown - V_BORDER)) {
	    top += shown / 2.0;
	    if (top + shown > 1.0) top = 1.0 - shown;
	} else {
	    do_call = False;
	}

	if (do_call) {
	    XtCallCallbacks(vert_sb, XtNjumpProc, &top);
	    adjusted = True;
	}
    }

    /* make sure cursor is displayed during dowhatis.. */
    if (adjusted) display_cursor(wp);

#ifdef VERBOSE
    if (horiz_sb || vert_sb) printf("\n");
#endif
}


/*
 * Check to see if the viewport has grown smaller.  If so, then we want to make
 * sure that the cursor is still on the screen.  We do this to keep the cursor
 * on the screen when the user resizes the nethack window.
 */
static void
map_check_size_change(wp)
    struct xwindow *wp;
{
    struct map_info_t *map_info = wp->map_information;
    Arg arg[2];
    Dimension new_width, new_height;
    Widget viewport;

    viewport = XtParent(wp->w);

    XtSetArg(arg[0], XtNwidth,  &new_width);
    XtSetArg(arg[1], XtNheight, &new_height);
    XtGetValues(viewport, arg, TWO);

    /* Only do cursor check if new size is smaller. */
    if (new_width < map_info->viewport_width
		    || new_height < map_info->viewport_height) {
	check_cursor_visibility(wp);
    }

    map_info->viewport_width = new_width;
    map_info->viewport_height = new_height;
}

/*
 * Fill in parameters "regular" and "inverse" with newly created GCs.
 * Using the given background pixel and the foreground pixel optained
 * by querying the widget with the resource name.
 */
static void
set_gc(w, font, resource_name, bgpixel, regular, inverse)
    Widget w;
    Font font;
    char *resource_name;
    Pixel bgpixel;
    GC   *regular, *inverse;
{
    XGCValues values;
    XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont;
    Pixel curpixel;
    Arg arg[1];

    XtSetArg(arg[0], resource_name, &curpixel);
    XtGetValues(w, arg, ONE);

    values.foreground = curpixel;
    values.background = bgpixel;
    values.function   = GXcopy;
    values.font	      = font;
    *regular = XtGetGC(w, mask, &values);
    values.foreground = bgpixel;
    values.background = curpixel;
    values.function   = GXcopy;
    values.font	      = font;
    *inverse = XtGetGC(w, mask, &values);
}

/*
 * Create the GC's for each color.
 *
 * I'm not sure if it is a good idea to have a GC for each color (and
 * inverse). It might be faster to just modify the foreground and
 * background colors on the current GC as needed.
 */
static void
get_gc(wp, font)
    struct xwindow *wp;
    Font font;
{
    struct map_info_t *map_info = wp->map_information;
    Pixel bgpixel;
    Arg arg[1];

    /* Get background pixel. */
    XtSetArg(arg[0], XtNbackground, &bgpixel);
    XtGetValues(wp->w, arg, ONE);

#ifdef TEXTCOLOR
#define set_color_gc(nh_color, resource_name)			\
	    set_gc(wp->w, font, resource_name, bgpixel,		\
		    &map_info->color_gcs[nh_color],		\
		    &map_info->inv_color_gcs[nh_color]);

    set_color_gc(BLACK,		 XtNblack);
    set_color_gc(RED,		 XtNred);
    set_color_gc(GREEN,		 XtNgreen);
    set_color_gc(BROWN,		 XtNbrown);
    set_color_gc(BLUE,		 XtNblue);
    set_color_gc(MAGENTA,	 XtNmagenta);
    set_color_gc(CYAN,		 XtNcyan);
    set_color_gc(GRAY,		 XtNgray);
    set_color_gc(NO_COLOR,	 XtNforeground);
    set_color_gc(ORANGE_COLORED, XtNorange);
    set_color_gc(BRIGHT_GREEN,	 XtNbright_green);
    set_color_gc(YELLOW,	 XtNyellow);
    set_color_gc(BRIGHT_BLUE,	 XtNbright_blue);
    set_color_gc(BRIGHT_MAGENTA, XtNbright_magenta);
    set_color_gc(BRIGHT_CYAN,	 XtNbright_cyan);
    set_color_gc(WHITE,		 XtNwhite);
#else
    set_gc(wp->w, font, XtNforeground, bgpixel,
				&map_info->copy_gc, &map_info->inv_copy_gc);
#endif
}


/*
 * Display the cursor on the map window.
 */
static void
display_cursor(wp)
    struct xwindow *wp;
{
    /* Redisplay the cursor location inverted. */
    map_update(wp, wp->cursy, wp->cursy, wp->cursx, wp->cursx, TRUE);
}


/*
 * Check if there are any changed characters.  If so, then plaster them on
 * the screen.
 */
void
display_map_window(wp)
    struct xwindow *wp;
{
    register int row;
    struct map_info_t *map_info = wp->map_information;

    /*
     * If the previous cursor position is not the same as the current
     * cursor position, then update the old cursor position.
     */
    if (wp->prevx != wp->cursx || wp->prevy != wp->cursy) {
	register unsigned int x = wp->prevx, y = wp->prevy;
	if (x < map_info->t_start[y]) map_info->t_start[y] = x;
	if (x > map_info->t_stop[y])  map_info->t_stop[y]  = x;
    }

    for (row = 0; row < ROWNO; row++) {
	if (map_info->t_start[row] <= map_info->t_stop[row]) {
	    map_update(wp, row, row,
			(int) map_info->t_start[row],
			(int) map_info->t_stop[row], FALSE);
	    map_info->t_start[row] = COLNO-1;
	    map_info->t_stop[row] = 0;
	}
    }
    display_cursor(wp);
    wp->prevx = wp->cursx;	/* adjust old cursor position */
    wp->prevy = wp->cursy;
}

/*
 * Fill the saved screen characters with the "clear" character, and reset
 * all colors to the neutral color.  Flush out everything by resetting the
 * "new" bounds and calling display_map_window().
 */
void
clear_map_window(wp)
    struct xwindow *wp;
{
    struct map_info_t *map_info = wp->map_information;

    /* Fill with spaces, and update */
    (void) memset((genericptr_t) map_info->text, ' ',
			sizeof(map_info->text));
    (void) memset((genericptr_t) map_info->t_start, (char) 0,
			sizeof(map_info->t_start));
    (void) memset((genericptr_t) map_info->t_stop, (char) COLNO-1,
			sizeof(map_info->t_stop));
#ifdef TEXTCOLOR
    (void) memset((genericptr_t) map_info->colors, NO_COLOR,
			sizeof(map_info->colors));
#endif
    display_map_window(wp);
}

/*
 * Retreive the font associated with the map window and save attributes
 * that are used when updating it.
 */
static void
get_char_info(wp)
    struct xwindow *wp;
{
    XFontStruct *fs;

    fs = WindowFontStruct(wp->w);
    wp->map_information->char_width    = fs->max_bounds.width;
    wp->map_information->char_height   = fs->max_bounds.ascent +
						fs->max_bounds.descent;
    wp->map_information->char_ascent   = fs->max_bounds.ascent;
    wp->map_information->char_lbearing = -fs->min_bounds.lbearing;

#ifdef VERBOSE
    printf("Font information:\n");
    printf("fid = %d, direction = %d\n", fs->fid, fs->direction);
    printf("first = %d, last = %d\n",
			fs->min_char_or_byte2, fs->max_char_or_byte2);
    printf("all chars exist? %s\n", fs->all_chars_exist?"yes":"no");
    printf("min_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n",
		fs->min_bounds.lbearing, fs->min_bounds.rbearing,
		fs->min_bounds.width, fs->min_bounds.ascent,
		fs->min_bounds.descent, fs->min_bounds.attributes);
    printf("max_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n",
		fs->max_bounds.lbearing, fs->max_bounds.rbearing,
		fs->max_bounds.width, fs->max_bounds.ascent,
		fs->max_bounds.descent, fs->max_bounds.attributes);
    printf("per_char = 0x%x\n", fs->per_char);
    printf("Text: (max) width = %d, height = %d\n",
	    wp->map_information->char_width, wp->map_information->char_height);
#endif

    if (fs->min_bounds.width != fs->max_bounds.width)
	X11_raw_print("Warning:  map font is not monospaced!");
}

/*
 * keyhit buffer
 */
#define INBUF_SIZE 64
int inbuf[INBUF_SIZE];
int incount = 0;
int inptr = 0;	/* points to valid data */


void
extern_map_input(event)
    XEvent *event;
{
    if(event->type == KeyPress)
	map_input(window_list[WIN_MAP].w, (XtPointer) 0, (XtPointer) event);
}

/*
 * Keyboard and button event handler for map window.
 */
/* ARGSUSED */
static void
map_input(w, client_data, call_data)
    Widget w;
    XtPointer client_data, call_data;
{
    XEvent *event = (XEvent *) call_data;
    XKeyEvent *key;
    XButtonEvent *button;
    int i, nbytes;
    char c;
    char keystring[MAX_KEY_STRING];

    switch (event->type) {
	case ButtonPress:
	    button = (XButtonEvent *) event;
#ifdef VERBOSE_INPUT
	    printf("button press\n");
#endif
	    set_button_values(w, button->x, button->y, button->button);
	    break;
	case KeyPress:
#ifdef VERBOSE_INPUT
	    printf("key: ");
#endif
	    if(appResources.slow && input_func) {
		(*input_func)(w, event, NULL, NULL);
		break;
	    }

	    /*
	     * Don't use key_event_to_char() because we want to be able
	     * to allow keys mapped to multiple characters.
	     */
	    key = (XKeyEvent *) event;
	    nbytes = XLookupString(key, keystring, MAX_KEY_STRING, NULL, NULL);
	    /* Modifier keys return a zero length string when pressed. */
	    if (nbytes) {
#ifdef VERBOSE_INPUT
		printf("\"");
#endif
		for (i = 0; i < nbytes; i++) {
		    c = keystring[i];

		    if (incount < INBUF_SIZE) {
			inbuf[(inptr+incount)%INBUF_SIZE] =
			    ((int) c) + ((key->state & Mod1Mask) ? 0x80 : 0);
			incount++;
		    } else {
			X11_nhbell();
		    }
#ifdef VERBOSE_INPUT
		    /*
		     * Assume that mod1 is really the meta key.
		     */
		    if (key->state & Mod1Mask)	/* meta will print as M<c> */
			(void) putchar('M');
		    if (c < ' ') {		/* ctrl will print as ^<c> */
			(void) putchar('^');
			c += '@';
		    }
		    (void) putchar(c);
#endif
		}
#ifdef VERBOSE_INPUT
		printf("\" [%d bytes]\n", nbytes);
#endif
	    }
	    break;

	default:
	    impossible("unexpected X event, type = %d\n", (int) event->type);
	    break;
    }
}

static void
set_button_values(w, x, y, button)
    Widget w;
    int x;
    int y;
    unsigned int button;
{
    struct xwindow *wp;
    struct map_info_t *map_info;

    wp = find_widget(w);
    map_info = wp->map_information;

    click_x = x / map_info->char_width;
    click_y = y / map_info->char_height;

    /* The values can be out of range if the map window has been resized */
    /* to be larger than the max size.					 */
    if (click_x >= COLNO) click_x = COLNO-1;
    if (click_y >= ROWNO) click_x = ROWNO-1;

    /* Map all buttons but the first to the second click */
    click_button = (button == Button1) ? CLICK_1 : CLICK_2;
}

/*
 * Map window expose callback.
 */
static void
map_exposed(w, event)
    Widget w;
    XExposeEvent *event;
{
    int x, y;
    struct xwindow *wp;
    struct map_info_t *map_info;
    unsigned width, height;
    int start_row, stop_row, start_col, stop_col;

    if (!XtIsRealized(w)) return;

    wp = find_widget(w);
    map_info = wp->map_information;
    /*
     * The map is sent an expose event when the viewport resizes.  Make sure
     * that the cursor is still in the viewport after the resize.
     */
    map_check_size_change(wp);

    if (event) {		/* called from button-event */
	x      = event->x;
	y      = event->y;
	width  = event->width;
	height = event->height;
    } else {
	x     = 0;
	y     = 0;
	width = wp->pixel_width;
	height= wp->pixel_height;
    }
    /*
     * Convert pixels into INCLUSIVE text rows and columns.
     */
    start_row = y / map_info->char_height;
    stop_row = start_row + (height / map_info->char_height) +
			(((height % map_info->char_height) == 0) ? 0 : 1) - 1;

    start_col = x / map_info->char_width;
    stop_col = start_col + (width / map_info->char_width) +
			(((width % map_info->char_width) == 0) ? 0 : 1) - 1;

#ifdef VERBOSE
    printf("map_exposed: x = %d, y = %d, width = %d, height = %d\n",
						    x, y, width, height);
#endif

    /* Out of range values are possible if the map window is resized to be */
    /* bigger than the largest expected value.				   */
    if (stop_row >= ROWNO) stop_row = ROWNO-1;
    if (stop_col >= COLNO) stop_col = COLNO-1;

    map_update(wp, start_row, stop_row, start_col, stop_col, FALSE);
    display_cursor(wp);		/* make sure cursor shows up */
}

/*
 * Do the actual work of the putting characters onto our X window.  This
 * is called from the expose event routine, the display window (flush)
 * routine, and the display cursor routine.  The later is a kludge that
 * involves the inverted parameter of this function.  A better solution
 * would be to double the color count, with any color above MAXCOLORS
 * being inverted.
 *
 * This works for rectangular regions (this includes one line rectangles).
 * The start and stop columns are *inclusive*.
 */
static void
map_update(wp, start_row, stop_row, start_col, stop_col, inverted)
    struct xwindow *wp;
    int start_row, stop_row, start_col, stop_col;
    boolean inverted;
{
    int win_start_row, win_start_col;
    struct map_info_t *map_info = wp->map_information;
    int row;
    register int count;

    if (start_row < 0 || stop_row >= ROWNO) {
	impossible("map_update:  bad row range %d-%d\n", start_row, stop_row);
	return;
    }
    if (start_col < 0 || stop_col >=COLNO) {
	impossible("map_update:  bad col range %d-%d\n", start_col, stop_col);
	return;
    }

#ifdef VERBOSE_UPDATE
    printf("update: [0x%x] %d %d %d %d\n", 
		(int) wp->w, start_row, stop_row, start_col, stop_col);
#endif
    win_start_row = start_row;
    win_start_col = start_col;

#ifdef TEXTCOLOR
    if (flags.use_color) {
	register char *c_ptr;
	char *t_ptr;
	int cur_col, color, win_ystart;

	for (row = start_row; row <= stop_row; row++) {
	    win_ystart = map_info->char_ascent +
				    (row * map_info->char_height);

	    t_ptr = (char *) &(map_info->text[row][start_col]);
	    c_ptr = (char *) &(map_info->colors[row][start_col]);
	    cur_col = start_col;
	    while (cur_col <= stop_col) {
		color = *c_ptr++;
		count = 1;
		while ((cur_col + count) <= stop_col && *c_ptr == color) {
		    count++;
		    c_ptr++;
		}

		XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w),
		    inverted ? map_info->inv_color_gcs[color] :
			       map_info->color_gcs[color],
		    map_info->char_lbearing + (map_info->char_width * cur_col),
		    win_ystart,
		    t_ptr, count);

		/* move text pointer and column count */
		t_ptr += count;
		cur_col += count;
	    } /* col loop */
	} /* row loop */
    } else
#endif /* TEXTCOLOR */
    {
	int win_row, win_xstart;

	/* We always start at the same x window position and have	*/
	/* the same character count.					*/
	win_xstart = map_info->char_lbearing +
				    (win_start_col * map_info->char_width);
	count = stop_col - start_col + 1;

	for (row = start_row, win_row = win_start_row;
					row <= stop_row; row++, win_row++) {

	    XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w),
		inverted ? map_info->inv_copy_gc : map_info->copy_gc,
		win_xstart,
		map_info->char_ascent + (win_row * map_info->char_height),
		(char *) &(map_info->text[row][start_col]), count);
	}
    }
}

/* Adjust the number of rows and columns on the given map window */
void
set_map_size(wp, cols, rows)
    struct xwindow *wp;
    Dimension cols, rows;
{
    Arg args[4];
    Cardinal num_args;

    wp->pixel_width  = wp->map_information->char_width  * cols;
    wp->pixel_height = wp->map_information->char_height * rows;

    num_args = 0;
    XtSetArg(args[num_args], XtNwidth, wp->pixel_width);   num_args++;
    XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++;
    XtSetValues(wp->w, args, num_args);
}

/*
 * The map window creation routine.
 */
void
create_map_window(wp, create_popup, parent)
    struct xwindow *wp;
    boolean create_popup;	/* parent is a popup shell that we create */
    Widget parent;
{
    struct map_info_t *map_info;	/* map info pointer */
    Widget map, viewport;
    Arg args[10];
    Cardinal num_args;
    Dimension rows, columns;

    wp->type = NHW_MAP;

    map_info = wp->map_information =
			(struct map_info_t *) alloc(sizeof(struct map_info_t));

    map_info->viewport_width = map_info->viewport_height = 0;
    (void) memset((genericptr_t) map_info->text, ' ', sizeof(map_info->text));
    (void) memset((genericptr_t) map_info->t_start, (char) COLNO,
			sizeof(map_info->t_start));
    (void) memset((genericptr_t) map_info->t_stop, (char) 0,
			sizeof(map_info->t_stop));
#ifdef TEXTCOLOR
    (void) memset((genericptr_t) map_info->colors, NO_COLOR,
			sizeof(map_info->colors));
#endif

    if (create_popup) {
	/*
	 * Create a popup that accepts key and button events.
	 */
	num_args = 0;
	XtSetArg(args[num_args], XtNinput, False);            num_args++;

	wp->popup = parent = XtCreatePopupShell("nethack",
					topLevelShellWidgetClass,
				       toplevel, args, num_args);
    }

    num_args = 0;
    XtSetArg(args[num_args], XtNallowHoriz, True);	num_args++;
    XtSetArg(args[num_args], XtNallowVert,  True);	num_args++;
    /* XtSetArg(args[num_args], XtNforceBars,  True);	num_args++; */
    XtSetArg(args[num_args], XtNuseBottom,  True);	num_args++;
    viewport = XtCreateManagedWidget(
			"map_viewport",		/* name */
			viewportWidgetClass,	/* widget class from Window.h */
			parent,			/* parent widget */
			args,			/* set some values */
			num_args);		/* number of values to set */

    /*
     * Create a map window.  We need to set the width and height to some
     * value when we create it.  We will change it to the value we want
     * later
     */
    num_args = 0;
    XtSetArg(args[num_args], XtNwidth,  100); num_args++;
    XtSetArg(args[num_args], XtNheight, 100); num_args++;

    wp->w = map = XtCreateManagedWidget(
		"map",			/* name */
		windowWidgetClass,	/* widget class from Window.h */
		viewport,		/* parent widget */
		args,			/* set some values */
		num_args);		/* number of values to set */

    XtAddCallback(map, XtNcallback,	  map_input,      (XtPointer) 0);
    XtAddCallback(map, XtNexposeCallback, map_exposed, (XtPointer) 0);

    get_char_info(wp);
    get_gc(wp, WindowFont(map));

    /*
     * Initially, set the map widget to be the size specified by the
     * widget rows and columns resources.  We need to do this to
     * correctly set the viewport window size.  After the viewport is
     * realized, then the map can resize to its normal size.
     */
    num_args = 0;
    XtSetArg(args[num_args], XtNrows,    &rows);	num_args++;
    XtSetArg(args[num_args], XtNcolumns, &columns);	num_args++;
    XtGetValues(wp->w, args, num_args);

    /* Don't bother with windows larger than ROWNOxCOLNO. */
    if (columns > COLNO) columns = COLNO;
    if (rows    > ROWNO) rows = ROWNO;

    set_map_size(wp, columns, rows);

    /*
     * If we have created our own popup, then realize it so that the
     * viewport is also realized.  Then resize the map window.
     */
    if (create_popup) {
	XtRealizeWidget(wp->popup);
	set_map_size(wp, COLNO, ROWNO);
    }
}

/*
 * Destroy this map window.
 */
void
destroy_map_window(wp)
    struct xwindow *wp;
{
    struct map_info_t *map_info = wp->map_information;
#ifdef TEXTCOLOR
    int i;
#endif

    if (wp->popup) {
	nh_XtPopdown(wp->popup);

	/* Free allocated GCs. */
#ifdef TEXTCOLOR
	for (i = 0; i < MAXCOLORS; i++) {
	    XtReleaseGC(wp->w, map_info->color_gcs[i]);
	    XtReleaseGC(wp->w, map_info->inv_color_gcs[i]);
	}
#else
	XtReleaseGC(wp->w, map_info->copy_gc);
	XtReleaseGC(wp->w, map_info->inv_copy_gc);
#endif

	/* Free malloc'ed space. */
	free((char *) map_info);

	/* Destroy map widget. */
	XtDestroyWidget(wp->popup);
    }

    wp->type = NHW_NONE;	/* allow re-use */
}



boolean exit_x_event;	/* exit condition for the event loop */
/*******
pkey(k)
    int k;
{
    printf("key = '%s%c'\n", (k<32) ? "^":"", (k<32) ? '@'+k : k);
}
******/

/*
 * Main X event loop.  Here we accept and dispatch X events.  We only exit
 * under certain circumstances.
 */
int
x_event(exit_condition)
    int exit_condition;
{
    XEvent  event;
    int     retval;
    boolean keep_going = TRUE;

#ifdef GCC_WARN
    retval = 0;
#endif

    click_button = NO_CLICK;	/* reset click exit condition */
    exit_x_event = FALSE;	/* reset callback exit condition */

    /*
     * Loop until we get a sent event, callback exit, or are accepting key
     * press and button press events and we receive one.
     */
    if((exit_condition == EXIT_ON_KEY_PRESS ||
	exit_condition == EXIT_ON_KEY_OR_BUTTON_PRESS) && incount)
	goto try_test;

    do {
	XtAppNextEvent(app_context, &event);
	XtDispatchEvent(&event);

	/* See if we can exit. */
    try_test:
	switch (exit_condition) {
	    case EXIT_ON_SENT_EVENT: {
		XAnyEvent *any = (XAnyEvent *) &event;
		if (any->send_event) {
		    retval = 0;
		    keep_going = FALSE;
		}
		break;
	    }
	    case EXIT_ON_EXIT:
		if (exit_x_event) {
		    incount = 0;
		    retval = 0;
		    keep_going = FALSE;
		}
		break;
	    case EXIT_ON_KEY_PRESS:
		if (incount != 0) {
		    /* get first pressed key */
		    --incount;
		    retval = inbuf[inptr];
		    inptr = (inptr+1) % INBUF_SIZE;
		    /* pkey(retval); */
		    keep_going = FALSE;
		}
		break;
	    case EXIT_ON_KEY_OR_BUTTON_PRESS:
		if (incount != 0 || click_button != NO_CLICK) {
		    if (click_button != NO_CLICK) {	/* button press */
			/* click values are already set */
			retval = 0;
		    } else {				/* key press */
			/* get first pressed key */
			--incount;
			retval = inbuf[inptr];
			inptr = (inptr+1) % INBUF_SIZE;
			/* pkey(retval); */
		    }
		    keep_going = FALSE;
		}
		break;
	    default:
		panic("x_event: unknown exit condition %d\n", exit_condition);
		break;
	}
    } while (keep_going);

    return retval;
}

/*winmap.c*/
