/**
 * \file main-x11.c
 * \brief Provide support for the X Windowing System
 *
 * Copyright (c) 1997 Ben Harrison, and others
 *
 * This work is free software; you can redistribute it and/or modify it
 * under the terms of either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation, version 2, or
 *
 * b) the "Angband licence":
 *    This software may be copied and distributed for educational, research,
 *    and not for profit purposes provided that this copyright and statement
 *    are included in all such copies.  Other copyrights may also apply.
 */
#include "angband.h"
#include "buildid.h"
#include "game-world.h"
#include "init.h"
#include "ui-display.h"
#include "ui-game.h"

/**
 * This file helps Angband work with UNIX/X11 computers.
 *
 * To use this file, compile with "USE_X11" defined, and link against all
 * the various "X11" libraries which may be needed.
 *
 * Part of this file provides a user interface package composed of several
 * pseudo-objects, including "metadpy" (a display), "infowin" (a window),
 * "infoclr" (a color), and "infofnt" (a font).  Actually, the package was
 * originally much more interesting, but it was bastardized to keep this
 * file simple.
 *
 * The rest of this file is an implementation of "main-xxx.c" for X11.
 *
 * Most of this file is by Ben Harrison (benh@phial.com).
 */

/**
 * The following shell script can be used to launch Angband, assuming that
 * it was extracted into "~/Angband", and compiled using "USE_X11", on a
 * Linux machine, with a 1280x1024 screen, using 6 windows (with the given
 * characteristics), with gamma correction of 1.8 -> (1 / 1.8) * 256 = 142,
 * and without graphics (add "-g" for graphics).  Just copy this comment
 * into a file, remove the leading " * " characters (and the head/tail of
 * this comment), and make the file executable.
 *
 *
 * #!/bin/csh
 *
 * # Describe attempt
 * echo "Launching angband..."
 * sleep 2
 *
 * # Main window
 * setenv ANGBAND_X11_FONT_0 10x20
 * setenv ANGBAND_X11_AT_X_0 5
 * setenv ANGBAND_X11_AT_Y_0 510
 *
 * # Message window
 * setenv ANGBAND_X11_FONT_1 8x13
 * setenv ANGBAND_X11_AT_X_1 5
 * setenv ANGBAND_X11_AT_Y_1 22
 * setenv ANGBAND_X11_ROWS_1 35
 *
 * # Inventory window
 * setenv ANGBAND_X11_FONT_2 8x13
 * setenv ANGBAND_X11_AT_X_2 635
 * setenv ANGBAND_X11_AT_Y_2 182
 * setenv ANGBAND_X11_ROWS_2 23
 *
 * # Equipment window
 * setenv ANGBAND_X11_FONT_3 8x13
 * setenv ANGBAND_X11_AT_X_3 635
 * setenv ANGBAND_X11_AT_Y_3 22
 * setenv ANGBAND_X11_ROWS_3 12
 *
 * # Monster recall window
 * setenv ANGBAND_X11_FONT_4 6x13
 * setenv ANGBAND_X11_AT_X_4 817
 * setenv ANGBAND_X11_AT_Y_4 847
 * setenv ANGBAND_X11_COLS_4 76
 * setenv ANGBAND_X11_ROWS_4 11
 *
 * # Object recall window
 * setenv ANGBAND_X11_FONT_5 6x13
 * setenv ANGBAND_X11_AT_X_5 817
 * setenv ANGBAND_X11_AT_Y_5 520
 * setenv ANGBAND_X11_COLS_5 76
 * setenv ANGBAND_X11_ROWS_5 24
 *
 * # The build directory
 * cd ~/Angband
 *
 * # Gamma correction
 * setenv ANGBAND_X11_GAMMA 142
 *
 * # Launch Angband
 * ./src/angband -mx11 -- -n6 &
 *
 */



#ifdef USE_X11

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/keysymdef.h>
#include <X11/XKBlib.h>

#include "main.h"

/*
 * Default fonts (when using X11).
 */
#define DEFAULT_X11_FONT		"9x15"
#define DEFAULT_X11_FONT_0		"10x20"
#define DEFAULT_X11_FONT_1		"9x15"
#define DEFAULT_X11_FONT_2		"9x15"
#define DEFAULT_X11_FONT_3		"7x13"
#define DEFAULT_X11_FONT_4		"7x13"
#define DEFAULT_X11_FONT_5		"7x13"
#define DEFAULT_X11_FONT_6		"7x13"
#define DEFAULT_X11_FONT_7		"7x13"


#ifndef IsModifierKey

/**
 * Keysym macros, used on Keysyms to test for classes of symbols
 * These were stolen from one of the X11 header files
 */

#define IsKeypadKey(keysym) \
  (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))

#define IsCursorKey(keysym) \
  (((unsigned)(keysym) >= XK_Home)     && ((unsigned)(keysym) <  XK_Select))

#define IsPFKey(keysym) \
  (((unsigned)(keysym) >= XK_KP_F1)    && ((unsigned)(keysym) <= XK_KP_F4))

#define IsFunctionKey(keysym) \
  (((unsigned)(keysym) >= XK_F1)       && ((unsigned)(keysym) <= XK_F35))

#define IsMiscFunctionKey(keysym) \
  (((unsigned)(keysym) >= XK_Select)   && ((unsigned)(keysym) <  XK_KP_Space))

#define IsModifierKey(keysym) \
  (((unsigned)(keysym) >= XK_Shift_L)  && ((unsigned)(keysym) <= XK_Hyper_R))

#endif /* IsModifierKey */


/**
 * Checks if the keysym is a special key or a normal key
 * Assume that XK_MISCELLANY keysyms are special
 */
#define IsSpecialKey(keysym) \
  ((unsigned)(keysym) >= 0xFF00)


/**
 * Avoid some compiler warnings
 */
#define IGNORE_UNUSED_FUNCTIONS


/**
 * Notes on Colors:
 *
 *   1) On a monochrome (or "fake-monochrome") display, all colors
 *   will be "cast" to "fg," except for the bg color, which is,
 *   obviously, cast to "bg".  Thus, one can ignore this setting.
 *
 *   2) Because of the inner functioning of the color allocation
 *   routines, colors may be specified as (a) a typical color name,
 *   (b) a hexidecimal color specification (preceded by a pound sign),
 *   or (c) by strings such as "fg", "bg", "zg".
 *
 *   3) Due to the workings of the init routines, many colors
 *   may also be dealt with by their actual pixel values.  Note that
 *   the pixel with all bits set is "zg = (1<<metadpy->depth)-1", which
 *   is not necessarily either black or white.
 */



/**
 * ------------------------------------------------------------------------
 *  Generic Types
 * ------------------------------------------------------------------------ */



/**
 * An X11 pixell specifier
 */
typedef unsigned long Pixell;

/*
 * The structures defined below
 */
typedef struct metadpy metadpy;
typedef struct infowin infowin;
typedef struct infoclr infoclr;
typedef struct infofnt infofnt;


/**
 * A structure summarizing a given Display.
 *
 *	- The Display itself
 *	- The default Screen for the display
 *	- The virtual root (usually just the root)
 *	- The default colormap (from a macro)
 *  - The Alt key modifier mask
 *  - The Super key modifier mask
 *
 *	- The "name" of the display
 *
 *	- The socket to listen to for events
 *
 *	- The width of the display screen (from a macro)
 *	- The height of the display screen (from a macro)
 *	- The bit depth of the display screen (from a macro)
 *
 *	- The black Pixell (from a macro)
 *	- The white Pixell (from a macro)
 *
 *	- The background Pixell (default: black)
 *	- The foreground Pixell (default: white)
 *	- The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly)
 *
 *	- Bit Flag: Force all colors to black and white (default: !color)
 *	- Bit Flag: Allow the use of color (default: depth > 1)
 *	- Bit Flag: We created 'dpy', and so should nuke it when done.
 */
struct metadpy
{
	Display *dpy;
	Screen *screen;
	Window root;
	Colormap cmap;
	unsigned int alt_mask;
	unsigned int super_mask;

	char *name;

	int fd;

	unsigned int width;
	unsigned int height;
	unsigned int depth;

	Pixell black;
	Pixell white;

	Pixell bg;
	Pixell fg;
	Pixell zg;

	unsigned int mono:1;
	unsigned int color:1;
	unsigned int nuke:1;
};



/**
 * A Structure summarizing Window Information.
 *
 * I assume that a window is at most 30000 pixels on a side.
 * I assume that the root windw is also at most 30000 square.
 *
 *	- The Window
 *	- The current Input Event Mask
 *
 *	- The location of the window
 *	- The saved (startup) location of the window
 *	- The width, height of the window
 *	- The border width of this window
 *
 *	- Byte: 1st Extra byte
 *
 *	- Bit Flag: This window is currently Mapped
 *	- Bit Flag: This window needs to be redrawn
 *	- Bit Flag: This window has been resized
 *
 *	- Bit Flag: We should nuke 'win' when done with it
 *
 *	- Bit Flag: 1st extra flag
 *	- Bit Flag: 2nd extra flag
 *	- Bit Flag: 3rd extra flag
 *	- Bit Flag: 4th extra flag
 */
struct infowin
{
	Window win;
	long mask;

	int16_t ox, oy;

	int16_t x, y;
	int16_t x_save, y_save;
	int16_t w, h;
	uint16_t b;

	uint8_t byte1;

	unsigned int mapped:1;
	unsigned int redraw:1;
	unsigned int resize:1;

	unsigned int nuke:1;

	unsigned int flag1:1;
	unsigned int flag2:1;
	unsigned int flag3:1;
	unsigned int flag4:1;
};






/**
 * A Structure summarizing Operation+Color Information
 *
 *	- The actual GC corresponding to this info
 *
 *	- The Foreground Pixell Value
 *	- The Background Pixell Value
 *
 *	- Num (0-15): The operation code (As in Clear, Xor, etc)
 *	- Bit Flag: The GC is in stipple mode
 *	- Bit Flag: Destroy 'gc' at Nuke time.
 */
struct infoclr
{
	GC gc;

	Pixell fg;
	Pixell bg;

	unsigned int code:4;
	unsigned int stip:1;
	unsigned int nuke:1;
};



/**
 * A Structure to Hold Font Information
 *
 *	- The 'XFontStruct*' (yields the 'Font')
 *
 *	- The font name
 *
 *	- The default character width
 *	- The default character height
 *	- The default character ascent
 *
 *	- Byte: Pixel offset used during fake mono
 *
 *	- Flag: Force monospacing via 'wid'
 *	- Flag: Nuke info when done
 */
struct infofnt
{
	XFontSet	fs;

	const char *name;

	int16_t wid;
	int16_t twid;
	int16_t hgt;
	int16_t asc;

	uint8_t off;

	unsigned int mono:1;
	unsigned int nuke:1;
};



/**
 * Forward declare
 */
typedef struct term_data term_data;

/**
 * A structure for each "term"
 */
struct term_data
{
	term t;

	infofnt *fnt;

	infowin *win;

	int tile_wid;
	int tile_wid2; /* Tile-width with bigscreen */
	int tile_hgt;

	/* Pointers to allocated data, needed to clear up memory */
	XClassHint *classh;
	XSizeHints *sizeh;
};



/**
 * ------------------------------------------------------------------------
 *  Generic Macros
 * ------------------------------------------------------------------------ */


/* Initialize 'M' using a Display named 'N' */
#define Metadpy_init_name(N) \
	Metadpy_init_2((Display*)(NULL),N)

/* Init a top level infowin by pos,size,bord,Colors */
#define Infowin_init_top(X,Y,W,H,B,FG,BG) \
	Infowin_init_data(None,X,Y,W,H,B,FG,BG)

/* Set the current Infowin */
#define Infowin_set(I) \
	(Infowin = (I))


/* Set the current Infoclr */
#define Infoclr_set(C) \
	(Infoclr = (C))


#define Infoclr_init_ppo(F,B,O,M) \
	Infoclr_init_data(F,B,O,M)

#define Infoclr_init_ppn(F,B,O,M) \
	Infoclr_init_ppo(F,B,Infoclr_Opcode(O),M)

/* Set the current infofnt */
#define Infofnt_set(I) \
	(Infofnt = (I))

/**
 * ------------------------------------------------------------------------
 *  Generic Globals
 * ------------------------------------------------------------------------ */



/**
 * The "default" values
 */
static metadpy metadpy_default;


/**
 * The "current" variables
 */
static metadpy *Metadpy = &metadpy_default;
static infowin *Infowin = (infowin*)(NULL);
static infoclr *Infoclr = (infoclr*)(NULL);
static infofnt *Infofnt = (infofnt*)(NULL);


/*
 * Actual color table
 */
static infoclr *clr[MAX_COLORS * BG_MAX];



/**
 * ------------------------------------------------------------------------
 *  Code imported from the old maid-x11.c
 * ------------------------------------------------------------------------ */


static bool gamma_table_ready = false;
static int gamma_val = 0;


/**
 * Convert an RGB value to an X11 Pixel, or die.
 */
static uint32_t create_pixel(Display *dpy, uint8_t red, uint8_t green,
		uint8_t blue)
{
	Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy));

	XColor xcolour;

	if (!gamma_table_ready) {
		const char *str = getenv("ANGBAND_X11_GAMMA");
		if (str != NULL) gamma_val = atoi(str);

		gamma_table_ready = true;

		/* Only need to build the table if gamma exists */
		if (gamma_val) build_gamma_table(gamma_val);
	}

	/* Gamma Correction */
	if (gamma_val > 0) {
		red = gamma_table[red];
		green = gamma_table[green];
		blue = gamma_table[blue];
	}

	/* Build the color */

	xcolour.red   = red * 257;
	xcolour.green = green * 257;
	xcolour.blue  = blue * 257;
	xcolour.flags = DoRed | DoGreen | DoBlue;

	/* Attempt to Allocate the Parsed color */
	if (!(XAllocColor(dpy, cmap, &xcolour))) {
		quit_fmt("Couldn't allocate bitmap color #%04x%04x%04x\n",
			 xcolour.red, xcolour.green, xcolour.blue);
	}

	return (xcolour.pixel);
}


/**
 * Get the name of the default font to use for the term.
 */
static const char *get_default_font(int term_num)
{
	const char *font;
	char buf[80];

	/* Window specific font name */
	strnfmt(buf, sizeof(buf), "ANGBAND_X11_FONT_%d", term_num);

	/* Check environment for that font */
	font = getenv(buf);
	if (font) return font;

	/* Check environment for "base" font */
	font = getenv("ANGBAND_X11_FONT");
	if (font) return font;

	switch (term_num)
	{
		case 0:
			return DEFAULT_X11_FONT_0;
		case 1:
			return DEFAULT_X11_FONT_1;
		case 2:
			return DEFAULT_X11_FONT_2;
		case 3:
			return DEFAULT_X11_FONT_3;
		case 4:
			return DEFAULT_X11_FONT_4;
		case 5:
			return DEFAULT_X11_FONT_5;
		case 6:
			return DEFAULT_X11_FONT_6;
		case 7:
			return DEFAULT_X11_FONT_7;
	}

	return DEFAULT_X11_FONT;
}


/**
 * Look up keyboard modifiers using the Xkb extension
 */
static unsigned int xkb_mask_modifier( XkbDescPtr xkb, const char *name )
{
	unsigned int mask=0;
	
	if (streq(name, "Caps Lock")) return 2;
	
	for (int i = 0; (!mask) && (i < XkbNumVirtualMods); i++ ) {
		char* modStr;

		/* Avoid BadAtom errors:  skip elements with a null atom. */
		if (xkb->names->vmods[i] == None) {
		        continue;
		}
		modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] );
		if (modStr) {
			if (streq(name, modStr))
				XkbVirtualModsToReal( xkb, 1U << i, &mask );

			XFree(modStr);
		}
	}
	return 0;
}

/**
 * ------------------------------------------------------------------------
 *  Generic code
 * ------------------------------------------------------------------------ */



/**
 * Init the current metadpy, with various initialization stuff.
 *
 * Inputs:
 *	dpy:  The Display* to use (if NULL, create it)
 *	name: The name of the Display (if NULL, the current)
 *
 * Notes:
 *	If 'name' is NULL, but 'dpy' is set, extract name from dpy
 *	If 'dpy' is NULL, then Create the named Display
 *	If 'name' is NULL, and so is 'dpy', use current Display
 *
 * Return -1 if no Display given, and none can be opened.
 */
static errr Metadpy_init_2(Display *dpy, const char *name)
{
	metadpy *m = Metadpy;
	XkbDescPtr xkb;
	
	/* Attempt to create a display if none given, otherwise use the given one */
	if (!dpy) {
		/* Attempt to open the display */
		dpy = XOpenDisplay(name);

		/* Failure */
		if (!dpy) return (-1);

		/* We will have to nuke it when done */
		m->nuke = 1;
	} else {
		/* We will not have to nuke it when done */
		m->nuke = 0;
	}


	/*** Save some information ***/

	/* Save the Display itself */
	m->dpy = dpy;

	/* Get the Screen and Virtual Root Window */
	m->screen = DefaultScreenOfDisplay(dpy);
	m->root = RootWindowOfScreen(m->screen);

	/* get the modifier key layout */
	m->alt_mask = Mod1Mask;
	m->super_mask = Mod4Mask;
	xkb = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
	if (xkb != NULL) {
		m->alt_mask = xkb_mask_modifier( xkb, "Alt" );
		m->super_mask = xkb_mask_modifier( xkb, "Super" );
		XkbFreeKeyboard( xkb, 0, True );
	}

	/* Get the default colormap */
	m->cmap = DefaultColormapOfScreen(m->screen);

	/* Extract the true name of the display */
	m->name = DisplayString(dpy);

	/* Extract the fd */
	m->fd = ConnectionNumber(Metadpy->dpy);

	/* Save the Size and Depth of the screen */
	m->width = WidthOfScreen(m->screen);
	m->height = HeightOfScreen(m->screen);
	m->depth = DefaultDepthOfScreen(m->screen);

	/* Save the Standard Colors */
	m->black = BlackPixelOfScreen(m->screen);
	m->white = WhitePixelOfScreen(m->screen);

	/*** Make some clever Guesses ***/

	/* Guess at the desired 'fg' and 'bg' Pixell's */
	m->bg = m->black;
	m->fg = m->white;

	/* Calculate the Maximum allowed Pixel value.  */
	m->zg = ((Pixell)1 << m->depth) - 1;

	/* Save various default Flag Settings */
	m->color = ((m->depth > 1) ? 1 : 0);
	m->mono = ((m->color) ? 0 : 1);

	/* Return "success" */
	return (0);
}


/**
 * Nuke the current metadpy
 */
static errr Metadpy_nuke(void)
{
	metadpy *m = Metadpy;


	/* If required, Free the Display */
	if (m->nuke) {
		/* Close the Display */
		XCloseDisplay(m->dpy);

		/* Forget the Display */
		m->dpy = (Display*)(NULL);

		/* Do not nuke it again */
		m->nuke = 0;
	}

	/* Return Success */
	return (0);
}


/**
 * General Flush/ Sync/ Discard routine
 */
static errr Metadpy_update(int flush, int sync, int discard)
{
	/* Flush if desired */
	if (flush) XFlush(Metadpy->dpy);

	/* Sync if desired, using 'discard' */
	if (sync) XSync(Metadpy->dpy, discard);

	/* Success */
	return (0);
}


/**
 * Make a simple beep
 */
static errr Metadpy_do_beep(void)
{
	/* Make a simple beep */
	XBell(Metadpy->dpy, 100);

	return (0);
}



/**
 * Set the name (in the title bar) of Infowin
 */
static errr Infowin_set_name(const char *name)
{
	Status st;
	XTextProperty tp;
	char buf[128];
	char *bp = buf;
	my_strcpy(buf, name, sizeof(buf));
	st = XStringListToTextProperty(&bp, 1, &tp);
	if (st) XSetWMName(Metadpy->dpy, Infowin->win, &tp);
	XFree(tp.value);
	return (0);
}


#ifndef IGNORE_UNUSED_FUNCTIONS

/**
 * Set the icon name of Infowin
 */
static errr Infowin_set_icon_name(const char *name)
{
	Status st;
	XTextProperty tp;
	char buf[128];
	char *bp = buf;
	my_strcpy(buf, name, sizeof(buf));
	st = XStringListToTextProperty(&bp, 1, &tp);
	if (st) XSetWMIconName(Metadpy->dpy, Infowin->win, &tp);
	return (0);
}

#endif /* IGNORE_UNUSED_FUNCTIONS */


/**
 * Nuke Infowin
 */
static errr Infowin_nuke(void)
{
	infowin *iwin = Infowin;

	/* Nuke if requested */
	if (iwin->nuke) {
		/* Destory the old window */
		XDestroyWindow(Metadpy->dpy, iwin->win);
	}

	/* Success */
	return (0);
}


/**
 * Prepare a new 'infowin'.
 */
static errr Infowin_prepare(Window xid)
{
	infowin *iwin = Infowin;

	Window tmp_win;
	XWindowAttributes xwa;
	int x, y;
	unsigned int w, h, b, d;

	/* Assign stuff */
	iwin->win = xid;

	/* Check For Error XXX Extract some ACTUAL data from 'xid' */
	XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d);

	/* Apply the above info */
	iwin->x = x;
	iwin->y = y;
	iwin->x_save = x;
	iwin->y_save = y;
	iwin->w = w;
	iwin->h = h;
	iwin->b = b;

	/* Check Error XXX Extract some more ACTUAL data */
	XGetWindowAttributes(Metadpy->dpy, xid, &xwa);

	/* Apply the above info */
	iwin->mask = xwa.your_event_mask;
	iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1);

	/* And assume that we are exposed */
	iwin->redraw = 1;

	/* Success */
	return (0);
}


#ifndef IGNORE_UNUSED_FUNCTIONS

/**
 * Initialize a new 'infowin'.
 */
static errr Infowin_init_real(Window xid)
{
	/* Wipe it clean */
	(void)memset(Infowin, 0, sizeof(infowin));

	/* Start out non-nukable */
	Infowin->nuke = 0;

	/* Attempt to Prepare ourself */
	return (Infowin_prepare(xid));
}

#endif /* IGNORE_UNUSED_FUNCTIONS */


/**
 * Init an infowin by giving some data.
 *
 * Inputs:
 *	dad: The Window that should own this Window (if any)
 *	x,y: The position of this Window
 *	w,h: The size of this Window
 *	b,d: The border width and pixel depth
 *
 * Notes:
 *	If 'dad == None' assume 'dad == root'
 */
static errr Infowin_init_data(Window dad, int x, int y, int w, int h,
                              int b, Pixell fg, Pixell bg)
{
	Window xid;

	/* Wipe it clean */
	(void)memset(Infowin, 0, sizeof(infowin));


	/*** Error Check XXX ***/


	/*** Create the Window 'xid' from data ***/

	/* What happened here?  XXX XXX XXX */

	/* If no parent given, depend on root */
	if (dad == None)
		dad = Metadpy->root;

	/* Create the Window XXX Error Check */
	xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg);

	/* Start out selecting No events */
	XSelectInput(Metadpy->dpy, xid, 0L);

	/*** Prepare the new infowin ***/

	/* Mark it as nukable */
	Infowin->nuke = 1;

	/* Attempt to Initialize the infowin */
	return (Infowin_prepare(xid));
}



/**
 * Modify the event mask of an Infowin
 */
static errr Infowin_set_mask(long mask)
{
	/* Save the new setting */
	Infowin->mask = mask;

	/* Execute the Mapping */
	XSelectInput(Metadpy->dpy, Infowin->win, Infowin->mask);

	/* Success */
	return (0);
}


/**
 * Request that Infowin be mapped
 */
static errr Infowin_map(void)
{
	/* Execute the Mapping */
	XMapWindow(Metadpy->dpy, Infowin->win);

	/* Success */
	return (0);
}


#ifndef IGNORE_UNUSED_FUNCTIONS

/**
 * Request that Infowin be unmapped
 */
static errr Infowin_unmap(void)
{
	/* Execute the Un-Mapping */
	XUnmapWindow(Metadpy->dpy, Infowin->win);

	/* Success */
	return (0);
}

#endif /* IGNORE_UNUSED_FUNCTIONS */


/**
 * Request that Infowin be raised
 */
static errr Infowin_raise(void)
{
	/* Raise towards visibility */
	XRaiseWindow(Metadpy->dpy, Infowin->win);

	/* Success */
	return (0);
}


#ifndef IGNORE_UNUSED_FUNCTIONS

/**
 * Request that Infowin be lowered
 */
static errr Infowin_lower(void)
{
	/* Lower towards invisibility */
	XLowerWindow(Metadpy->dpy, Infowin->win);

	/* Success */
	return (0);
}

#endif /* IGNORE_UNUSED_FUNCTIONS */


/**
 * Request that Infowin be moved to a new location
 */
static errr Infowin_impell(int x, int y)
{
	/* Execute the request */
	XMoveWindow(Metadpy->dpy, Infowin->win, x, y);

	/* Success */
	return (0);
}


/**
 * Resize an infowin
 */
static errr Infowin_resize(int w, int h)
{
	/* Execute the request */
	XResizeWindow(Metadpy->dpy, Infowin->win, w, h);

	/* Success */
	return (0);
}


#ifndef IGNORE_UNUSED_FUNCTIONS

/**
 * Move and Resize an infowin
 */
static errr Infowin_locate(int x, int y, int w, int h)
{
	/* Execute the request */
	XMoveResizeWindow(Metadpy->dpy, Infowin->win, x, y, w, h);

	/* Success */
	return (0);
}

#endif /* IGNORE_UNUSED_FUNCTIONS */


/**
 * Visually clear Infowin
 */
static errr Infowin_wipe(void)
{
	/* Execute the request */
	XClearWindow(Metadpy->dpy, Infowin->win);

	/* Success */
	return (0);
}


#ifndef IGNORE_UNUSED_FUNCTIONS

/**
 * Visually Paint Infowin with the current color
 */
static errr Infowin_fill(void)
{
	/* Execute the request */
	XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc,
	               0, 0, Infowin->w, Infowin->h);

	/* Success */
	return (0);
}

#endif /* IGNORE_UNUSED_FUNCTIONS */


/**
 * A NULL terminated pair list of legal "operation names"
 *
 * Pairs of values, first is texttual name, second is the string
 * holding the decimal value that the operation corresponds to.
 */
static const char *opcode_pairs[] =
{
	"cpy", "3",
	"xor", "6",
	"and", "1",
	"ior", "7",
	"nor", "8",
	"inv", "10",
	"clr", "0",
	"set", "15",

	"src", "3",
	"dst", "5",

	"+andReverse", "2",
	"+andInverted", "4",
	"+noop", "5",
	"+equiv", "9",
	"+orReverse", "11",
	"+copyInverted", "12",
	"+orInverted", "13",
	"+nand", "14",
	NULL
};


/**
 * Parse a word into an operation "code"
 *
 * Inputs:
 *	str: A string, hopefully representing an Operation
 *
 * Output:
 *	0-15: if 'str' is a valid Operation
 *	-1:   if 'str' could not be parsed
 */
static int Infoclr_Opcode(const char *str)
{
	register int i;

	/* Scan through all legal operation names */
	for (i = 0; opcode_pairs[i * 2]; ++i) {
		/* Is this the right oprname? */
		if (streq(opcode_pairs[i * 2], str)) {
			/* Convert the second element in the pair into a Code */
			return (atoi(opcode_pairs[i * 2 + 1]));
		}
	}

	/* The code was not found, return -1 */
	return (-1);
}


#ifndef IGNORE_UNUSED_FUNCTIONS

/**
 * Request a Pixell by name.  Note: uses 'Metadpy'.
 *
 * Inputs:
 *      name: The name of the color to try to load (see below)
 *
 * Output:
 *	The Pixell value that metched the given name
 *	'Metadpy->fg' if the name was unparseable
 *
 * Valid forms for 'name':
 *	'fg', 'bg', 'zg', '<name>' and '#<code>'
 */
static Pixell Infoclr_Pixell(const char *name)
{
	XColor scrn;

	/* Attempt to Parse the name */
	if (name && name[0]) {
		/* The 'bg' color is available */
		if (streq(name, "bg")) return (Metadpy->bg);

		/* The 'fg' color is available */
		if (streq(name, "fg")) return (Metadpy->fg);

		/* The 'zg' color is available */
		if (streq(name, "zg")) return (Metadpy->zg);

		/* The 'white' color is available */
		if (streq(name, "white")) return (Metadpy->white);

		/* The 'black' color is available */
		if (streq(name, "black")) return (Metadpy->black);

		/* Attempt to parse 'name' into 'scrn' */
		if (!(XParseColor(Metadpy->dpy, Metadpy->cmap, name, &scrn)))
			plog_fmt("Warning: Couldn't parse color '%s'\n", name);

		/* Attempt to Allocate the Parsed color */
		if (!(XAllocColor(Metadpy->dpy, Metadpy->cmap, &scrn)))
			plog_fmt("Warning: Couldn't allocate color '%s'\n", name);

		/* The Pixel was Allocated correctly */
		else return (scrn.pixel);
	}

	/* Warn about the Default being Used */
	plog_fmt("Warning: Using 'fg' for unknown color '%s'\n", name);

	/* Default to the 'Foreground' color */
	return (Metadpy->fg);
}


/**
 * Initialize a new 'infoclr' with a real GC.
 */
static errr Infoclr_init_1(GC gc)
{
	infoclr *iclr = Infoclr;

	/* Wipe the iclr clean */
	(void)memset(iclr, 0, sizeof(infoclr));

	/* Assign the GC */
	iclr->gc = gc;

	/* Success */
	return (0);
}

#endif /* IGNORE_UNUSED_FUNCTIONS */


/**
 * Nuke an old 'infoclr'.
 */
static errr Infoclr_nuke(void)
{
	infoclr *iclr = Infoclr;

	/* Deal with 'GC' */
	if (iclr->nuke) {
		/* Free the GC */
		XFreeGC(Metadpy->dpy, iclr->gc);
	}

	/* Forget the current */
	Infoclr = (infoclr*)(NULL);

	/* Success */
	return (0);
}


/**
 * Initialize an infoclr with some data
 *
 * Inputs:
 *	fg:   The Pixell for the requested Foreground (see above)
 *	bg:   The Pixell for the requested Background (see above)
 *	op:   The Opcode for the requested Operation (see above)
 *	stip: The stipple mode
 */
static errr Infoclr_init_data(Pixell fg, Pixell bg, int op, int stip)
{
	infoclr *iclr = Infoclr;

	GC gc;
	XGCValues gcv;
	unsigned long gc_mask;

	/*** Simple error checking of opr and clr ***/

	/* Check the 'Pixells' for realism */
	if (bg > Metadpy->zg) return (-1);
	if (fg > Metadpy->zg) return (-1);

	/* Check the data for trueness */
	if ((op < 0) || (op > 15)) return (-1);

	/*** Create the requested 'GC' ***/

	/* Assign the proper GC function */
	gcv.function = op;

	/* Assign the proper GC background */
	gcv.background = bg;

	/* Assign the proper GC foreground */
	gcv.foreground = fg;

	/* Handle XOR (xor is code 6) by hacking bg and fg */
	if (op == 6) gcv.background = 0;
	if (op == 6) gcv.foreground = (bg ^ fg);

	/* Assign the proper GC Fill Style */
	gcv.fill_style = (stip ? FillStippled : FillSolid);

	/* Turn off 'Give exposure events for pixmap copying' */
	gcv.graphics_exposures = False;

	/* Set up the GC mask */
	gc_mask = (GCFunction | GCBackground | GCForeground |
	           GCFillStyle | GCGraphicsExposures);

	/* Create the GC detailed above */
	gc = XCreateGC(Metadpy->dpy, Metadpy->root, gc_mask, &gcv);


	/*** Initialize ***/

	/* Wipe the iclr clean */
	(void)memset(iclr, 0, sizeof(infoclr));

	/* Assign the GC */
	iclr->gc = gc;

	/* Nuke it when done */
	iclr->nuke = 1;

	/* Assign the parms */
	iclr->fg = fg;
	iclr->bg = bg;
	iclr->code = op;
	iclr->stip = stip ? 1 : 0;

	/* Success */
	return (0);
}



/**
 * Change the 'fg' for an infoclr
 *
 * Inputs:
 *	fg:   The Pixell for the requested Foreground (see above)
 */
static errr Infoclr_change_fg(Pixell fg)
{
	infoclr *iclr = Infoclr;


	/*** Simple error checking of opr and clr ***/

	/* Check the 'Pixells' for realism */
	if (fg > Metadpy->zg) return (-1);

	/*** Change ***/

	/* Change */
	XSetForeground(Metadpy->dpy, iclr->gc, fg);

	/* Success */
	return (0);
}



/**
 * Change the 'bg' for an infoclr
 *
 * Inputs:
 *	bg:   The Pixell for the requested Background (see above)
 */
static errr Infoclr_change_bg(Pixell bg)
{
	infoclr *iclr = Infoclr;


	/*** Simple error checking of opr and clr ***/

	/* Check the 'Pixells' for realism */
	if (bg > Metadpy->zg) return (-1);

	/*** Change ***/

	/* Change */
	XSetBackground(Metadpy->dpy, iclr->gc, bg);

	/* Success */
	return (0);
}



/**
 * Nuke an old 'infofnt'.
 */
static errr Infofnt_nuke(void)
{
	infofnt *ifnt = Infofnt;

	/* Deal with 'name' */
	if (ifnt->name) {
		/* Free the name */
		string_free((void *) ifnt->name);
	}

	/* Nuke info if needed */
	if (ifnt->nuke) {
		/* Free the font */
		XFreeFontSet(Metadpy->dpy, ifnt->fs);
	}

	/* Success */
	return (0);
}


/**
 * Prepare a new 'infofnt'
 */
static errr Infofnt_prepare(XFontSet fs)
{
	infofnt *ifnt = Infofnt;
	int font_count, i;
	XFontSetExtents *extents;
	XFontStruct **fonts;
	char **names;

	/* Assign the struct */
	ifnt->fs = fs;
	extents = XExtentsOfFontSet(fs);

	font_count = XFontsOfFontSet(fs, &fonts, &names);
	ifnt->asc = 0;
	for (i = 0; i < font_count; i++, fonts++)
	   if (ifnt->asc < (*fonts)->ascent) ifnt->asc = (*fonts)->ascent;

	/* Extract default sizing info */
	ifnt->hgt = extents->max_logical_extent.height;
	ifnt->wid = extents->max_logical_extent.width;
	ifnt->twid = extents->max_logical_extent.width;

	/* Success */
	return (0);
}


/**
 * Init an infofnt by its Name
 *
 * Inputs:
 *	name: The name of the requested Font
 */
static errr Infofnt_init_data(const char *name)
{
	XFontSet fs;
	char **missing;
	int missing_count;

	/*** Load the info Fresh, using the name ***/

	/* If the name is not given, report an error */
	if (!name) return (-1);

	fs = XCreateFontSet(Metadpy->dpy, name, &missing, &missing_count, NULL);

	/* The load failed, try to recover */
	if (!fs) return (-1);
	if (missing_count)
		XFreeStringList(missing);

	/*** Init the font ***/

	/* Wipe the thing */
	(void)memset(Infofnt, 0, sizeof(infofnt));

	/* Attempt to prepare it */
	if (Infofnt_prepare(fs)) {
		/* Free the font */
		XFreeFontSet(Metadpy->dpy, fs);

		/* Fail */
		return (-1);
	}

	/* Save a copy of the font name */
	Infofnt->name = string_make(name);

	/* Mark it as nukable */
	Infofnt->nuke = 1;

	/* HACK - force all fonts to be printed character by character */
	Infofnt->mono = 1;

	/* Success */
	return (0);
}


/**
 * Standard Text
 */
static errr Infofnt_text_std(int x, int y, const wchar_t *str, int len)
{
	int i;
	int w, h;

	term_data *td = (term_data*)(Term->data);

	/*** Do a brief info analysis ***/

	/* Do nothing if the string is null */
	if (!str || !*str) return (-1);

	/* Get the length of the string */
	if (len < 0) len = wcslen(str);

	/*** Decide where to place the string, vertically ***/

	/* Ignore Vertical Justifications */
	y = (y * td->tile_hgt) + Infowin->oy;


	/*** Decide where to place the string, horizontally ***/

	/* Line up with x at left edge of column 'x' */
	x = (x * td->tile_wid) + Infowin->ox;

	/*** Erase the background ***/

	/* The total width will be 'len' chars * standard width */
	w = len * td->tile_wid;

	/* Simply do 'td->tile_hgt' (a single row) high */
	h = td->tile_hgt;

	/* Fill the background */
	XFillRectangle(Metadpy->dpy, Infowin->win, clr[COLOUR_DARK]->gc,
				   x, y, w, h);


	/*** Actually draw 'str' onto the infowin ***/
	y += Infofnt->asc;


	/*** Handle the fake mono we can enforce on fonts ***/

	/* Monotize the font */
	if (Infofnt->mono) {
		/* Do each character */
		for (i = 0; i < len; ++i) {
			/* Note that the Infoclr is set up to contain the Infofnt */
			XwcDrawImageString(Metadpy->dpy, Infowin->win, Infofnt->fs,
							   Infoclr->gc, x + i * td->tile_wid + Infofnt->off,
							   y, str + i, 1);
		}
	} else {
		/* Note that the Infoclr is set up to contain the Infofnt */
		XwcDrawImageString(Metadpy->dpy, Infowin->win, Infofnt->fs, Infoclr->gc,
		                 x, y, str, len);
	}

	/* Success */
	return (0);
}


/**
 * Painting where text would be
 */
static errr Infofnt_text_non(int x, int y, const wchar_t *str, int len)
{
	int w, h;

	term_data *td = (term_data*)(Term->data);

	/*** Find the width ***/

	/* Negative length is a flag to count the characters in str */
	if (len < 0) len = wcslen(str);

	/* The total width will be 'len' chars * standard width */
	w = len * td->tile_wid;

	/*** Find the X dimensions ***/

	/* Line up with x at left edge of column 'x' */
	x = x * td->tile_wid + Infowin->ox;

	/*** Find other dimensions ***/

	/* Simply do 'td->tile_hgt' (a single row) high */
	h = td->tile_hgt;

	/* Simply do "at top" in row 'y' */
	y = y * h + Infowin->oy;

	/*** Actually 'paint' the area ***/

	/* Just do a Fill Rectangle */
	XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, w, h);

	/* Success */
	return (0);
}



/*************************************************************************/


/**
 * ------------------------------------------------------------------------
 * Angband specific code follows... (ANGBAND)
 * ------------------------------------------------------------------------ */


/**
 * Cursor color
 */
static infoclr *xor;


/**
 * Color info (unused, red, green, blue).
 */
static uint8_t color_table_x11[MAX_COLORS][4];


/**
 * The number of term data structures
 */
#define MAX_TERM_DATA 8

/**
 * The array of term data structures
 */
static term_data data[MAX_TERM_DATA];


/**
 * Path to the X11 settings file
 */
static const char *x11_prefs = "x11-settings.prf";
static char settings[1024];



/**
 * Remember the number of terminal windows open
 */
static int term_windows_open;



/**
 * Process a keypress event
 */
static void react_keypress(XKeyEvent *ev)
{
	int n, ch = 0;
	metadpy *m = Metadpy;

	KeySym ks;

	char buf[128];

	/* Extract "modifier flags" */
	int mc = (ev->state & ControlMask) ? true : false;
	int ms = (ev->state & ShiftMask) ? true : false;
	int mo = (ev->state & m->alt_mask) ? true : false;
	int mx = (ev->state & m->super_mask) ? true : false;
	int kp = false;

	uint8_t mods = (mo ? KC_MOD_ALT : 0) | (mx ? KC_MOD_META : 0);

	/* Check for "normal" keypresses */
	n = XLookupString(ev, buf, 125, &ks, NULL);
	buf[n] = '\0';

	/* Ignore modifier keys by themselves */
	if (IsModifierKey(ks)) return;

	switch (ks) {
		case XK_BackSpace: ch = KC_BACKSPACE; break;
		case XK_Tab: ch = KC_TAB; break;
		case XK_Return: ch = KC_ENTER; break;
		case XK_Escape: ch = ESCAPE; break;

		case XK_Delete: ch = KC_DELETE; break;
		case XK_Home: ch = KC_HOME; break;
		case XK_Left: ch = ARROW_LEFT; break;
		case XK_Up: ch = ARROW_UP; break;
		case XK_Right: ch = ARROW_RIGHT; break;
		case XK_Down: ch = ARROW_DOWN; break;
		case XK_Page_Up: ch = KC_PGUP; break;
		case XK_Page_Down: ch = KC_PGDOWN; break;
		case XK_End: ch = KC_END; break;
		case XK_Insert: ch = KC_INSERT; break;
		case XK_Pause: ch = KC_PAUSE; break;
		case XK_Break: ch = KC_BREAK; break;

		/* keypad */
		case XK_KP_0: ch = '0'; kp = true; break;
		case XK_KP_1: ch = '1'; kp = true; break;
		case XK_KP_2: ch = '2'; kp = true; break;
		case XK_KP_3: ch = '3'; kp = true; break;
		case XK_KP_4: ch = '4'; kp = true; break;
		case XK_KP_5: ch = '5'; kp = true; break;
		case XK_KP_6: ch = '6'; kp = true; break;
		case XK_KP_7: ch = '7'; kp = true; break;
		case XK_KP_8: ch = '8'; kp = true; break;
		case XK_KP_9: ch = '9'; kp = true; break;

		case XK_KP_Decimal: ch = '.'; kp = true; break;
		case XK_KP_Divide: ch = '/'; kp = true; break;
		case XK_KP_Multiply: ch = '*'; kp = true; break;
		case XK_KP_Subtract: ch = '-'; kp = true; break;
		case XK_KP_Add: ch = '+'; kp = true; break;
		case XK_KP_Enter: ch = KC_ENTER; kp = true; break;
		case XK_KP_Equal: ch = '='; kp = true; break;

		case XK_KP_Delete: ch = KC_DELETE; kp = true; break;
		case XK_KP_Home: ch = KC_HOME; kp = true; break;
		case XK_KP_Left: ch = ARROW_LEFT; kp = true; break;
		case XK_KP_Up: ch = ARROW_UP; kp = true; break;
		case XK_KP_Right: ch = ARROW_RIGHT; kp = true; break;
		case XK_KP_Down: ch = ARROW_DOWN; kp = true; break;
		case XK_KP_Page_Up: ch = KC_PGUP; kp = true; break;
		case XK_KP_Page_Down: ch = KC_PGDOWN; kp = true; break;
		case XK_KP_End: ch = KC_END; kp = true; break;
		case XK_KP_Insert: ch = KC_INSERT; kp = true; break;
		case XK_KP_Begin: ch = KC_BEGIN; kp = true; break;

		case XK_F1: ch = KC_F1; break;
		case XK_F2: ch = KC_F2; break;
		case XK_F3: ch = KC_F3; break;
		case XK_F4: ch = KC_F4; break;
		case XK_F5: ch = KC_F5; break;
		case XK_F6: ch = KC_F6; break;
		case XK_F7: ch = KC_F7; break;
		case XK_F8: ch = KC_F8; break;
		case XK_F9: ch = KC_F9; break;
		case XK_F10: ch = KC_F10; break;
		case XK_F11: ch = KC_F11; break;
		case XK_F12: ch = KC_F12; break;
		case XK_F13: ch = KC_F13; break;
		case XK_F14: ch = KC_F14; break;
		case XK_F15: ch = KC_F15; break;
	}

	if (kp) mods |= KC_MOD_KEYPAD;

	if (ch) {
		if (mc) mods |= KC_MOD_CONTROL;
		if (ms) mods |= KC_MOD_SHIFT;
		Term_keypress(ch, mods);
		return;
	} else if (n && !IsSpecialKey(ks)) {
		keycode_t code = buf[0];

		if (mc && MODS_INCLUDE_CONTROL(code)) mods |= KC_MOD_CONTROL;
		if (ms && MODS_INCLUDE_SHIFT(code)) mods |= KC_MOD_SHIFT;

		Term_keypress(code, mods);
	}
}


/**
 * Find the square a particular pixel is part of.
 */
static void pixel_to_square(int * const x, int * const y,
                            const int ox, const int oy)
{
	term_data *td = (term_data*)(Term->data);

	(*x) = (ox - Infowin->ox) / td->tile_wid;
	(*y) = (oy - Infowin->oy) / td->tile_hgt;
}




/**
 * Process events
 */
static errr CheckEvent(bool wait)
{
	term_data *old_td = (term_data*)(Term->data);

	XEvent xev_body, *xev = &xev_body;

	term_data *td = NULL;
	infowin *iwin = NULL;

	int i;
	int window = 0;

	/* Do not wait unless requested */
	if (!wait && !XPending(Metadpy->dpy)) return (1);

	/* Wait in 0.02s increments while updating animations every 0.2s */
	i = 0;
	while (!XPending(Metadpy->dpy)) {
		if (i == 0) idle_update();
		usleep(20000);
		i = (i + 1) % 10;
	}

	/* Load the Event */
	XNextEvent(Metadpy->dpy, xev);


	/* Notice new keymaps */
	if (xev->type == MappingNotify) {
		XRefreshKeyboardMapping(&xev->xmapping);
		return 0;
	}


	/* Scan the windows */
	for (i = 0; i < MAX_TERM_DATA; i++) {
		if (xev->xany.window == data[i].win->win) {
			td = &data[i];
			iwin = td->win;
			window = i;
			break;
		}
	}

	/* Unknown window */
	if (!td || !iwin) return (0);

	/* Activate the Term */
	Term_activate(&td->t);

	/* Activate the window */
	Infowin_set(iwin);

	/* Switch on the Type */
	switch (xev->type)
	{
		case ButtonPress:
		{
			bool press = (xev->type == ButtonPress);

			int z = 0;

			/* Where is the mouse */
			int x = xev->xbutton.x;
			int y = xev->xbutton.y;

			/* Which button is involved */
			if (xev->xbutton.button == Button1) z = 1;
			else if (xev->xbutton.button == Button2) z = 2;
			else if (xev->xbutton.button == Button3) z = 3;
			else if (xev->xbutton.button == Button4) z = 4;
			else if (xev->xbutton.button == Button5) z = 5;
			else z = 0;

			/* Save a byte in ui-term/Term_mousepress for some reason */
			uint32_t state = ((XButtonEvent*) xev)->state;
			if(state & ShiftMask) {
				z |= (KC_MOD_SHIFT << 4);
			}
			if(state & ControlMask) {
				z |= (KC_MOD_CONTROL << 4);
			}
			if(state & Mod1Mask) {
				z |= (KC_MOD_ALT << 4);
			}

			/* The co-ordinates are only used in Angband format. */
			pixel_to_square(&x, &y, x, y);
			if (press) Term_mousepress(x, y, z);

			break;
		}

		case KeyPress:
		{
			/* Use "old" term */
			Term_activate(&old_td->t);

			/* Process the key */
			react_keypress(&(xev->xkey));

			break;
		}

		case Expose:
		{
			int x1, x2, y1, y2;

			x1 = (xev->xexpose.x - Infowin->ox) / td->tile_wid;
			x2 = (xev->xexpose.x + xev->xexpose.width - Infowin->ox) /
				td->tile_wid;

			y1 = (xev->xexpose.y - Infowin->oy) / td->tile_hgt;
			y2 = (xev->xexpose.y + xev->xexpose.height - Infowin->oy) /
				td->tile_hgt;

			Term_redraw_section(x1, y1, x2, y2);

			break;
		}

		case MapNotify:
		{
			Infowin->mapped = 1;
			Term->mapped_flag = true;
			break;
		}

		case UnmapNotify:
		{
			Infowin->mapped = 0;
			Term->mapped_flag = false;
			break;
		}

		/* Move and/or Resize */
		case ConfigureNotify:
		{
			int cols, rows, wid, hgt, force_resize;

			int ox = Infowin->ox;
			int oy = Infowin->oy;

			/* Save the new Window Parms */
			Infowin->x = xev->xconfigure.x;
			Infowin->y = xev->xconfigure.y;
			Infowin->w = xev->xconfigure.width;
			Infowin->h = xev->xconfigure.height;

			/* Determine "proper" number of rows/cols */
			cols = ((Infowin->w - (ox + ox)) / td->tile_wid);
			rows = ((Infowin->h - (oy + oy)) / td->tile_hgt);

			/* Minimal size */
			if (cols < 1) cols = 1;
			if (rows < 1) rows = 1;

			if (window == 0) {
				/* Hack the main window must be at least 80x24 */
				force_resize = false;
				if (cols < 80) {
					cols = 80;
					force_resize = true;
				}
				if (rows < 24) {
					rows = 24;
					force_resize = true;
				}

				/* Resize the windows if any "change" is needed */
				if (force_resize) {
					/* Desired size of window */
					wid = cols * td->tile_wid + (ox + ox);
					hgt = rows * td->tile_hgt + (oy + oy);

					/* Resize window */
					Infowin_set(td->win);
					Infowin_resize(wid, hgt);
				}
			}

			/* Resize the Term (if needed) */
			(void)Term_resize(cols, rows);

			break;
		}
	}

	/* Activate the old term */
	Term_activate(&old_td->t);

	/* Activate the proper window */
	Infowin_set(old_td->win);

	/* Success */
	return (0);
}


/**
 * Handle "activation" of a term
 */
static errr Term_xtra_x11_level(int v)
{
	term_data *td = (term_data*)(Term->data);

	/* Handle "activate" */
	if (v) {
		/* Activate the window */
		Infowin_set(td->win);

		/* Activate the font */
		Infofnt_set(td->fnt);
	}

	/* Success */
	return (0);
}


/**
 * React to changes
 */
static errr Term_xtra_x11_react(void)
{
	int i;

	if (Metadpy->color) {
		XSetWindowAttributes xattr;

		/* Check the colors */
		for (i = 0; i < MAX_COLORS; i++) {
			if ((color_table_x11[i][0] != angband_color_table[i][0]) ||
			    (color_table_x11[i][1] != angband_color_table[i][1]) ||
			    (color_table_x11[i][2] != angband_color_table[i][2]) ||
			    (color_table_x11[i][3] != angband_color_table[i][3])) {
				Pixell pixel;

				/* Save new values */
				color_table_x11[i][0] = angband_color_table[i][0];
				color_table_x11[i][1] = angband_color_table[i][1];
				color_table_x11[i][2] = angband_color_table[i][2];
				color_table_x11[i][3] = angband_color_table[i][3];

				/* Create pixel */
				pixel = create_pixel(Metadpy->dpy,
						     color_table_x11[i][1],
						     color_table_x11[i][2],
						     color_table_x11[i][3]);

				/* Change the foreground */
				Infoclr_set(clr[i]);
				Infoclr_change_fg(pixel);

				if (i == COLOUR_DARK) {
					int j;

					Metadpy->bg = pixel;
					/* Change the background */
					for (j = 0; j < MAX_COLORS; ++j) {
						Infoclr_set(clr[j]);
						Infoclr_change_bg(pixel);
					}
				} else if (i == COLOUR_WHITE) {
					Metadpy->fg = pixel;
				} else if (i == COLOUR_SHADE) {
					int j;

					/*
					 * For all colors, modify the variant
					 * that uses COLOUR_SHADE as the
					 * background.
					 */
					for (j = BG_DARK * MAX_COLORS;
							j < (BG_DARK + 1)
							* MAX_COLORS; ++j) {
						Infoclr_set(clr[j]);
						Infoclr_change_bg(pixel);
					}
				}

				/*
				 * Also modify the variants of this color
				 * which uses the color itself as the
				 * background or COLOUR_SHADE as the background.
				 */
				Infoclr_set(clr[i + BG_SAME * MAX_COLORS]);
				Infoclr_change_fg(pixel);
				Infoclr_change_bg(pixel);
				Infoclr_set(clr[i + BG_DARK * MAX_COLORS]);
				Infoclr_change_fg(pixel);
			}
		}

		xattr.background_pixel = Metadpy->bg;
		for (i = 0; i < ANGBAND_TERM_MAX; ++i) {
			term_data *td;

			if (!angband_term[i]) continue;
			td = (term_data*)(angband_term[i]->data);
			if (!td || !td->win) continue;
			XChangeWindowAttributes(Metadpy->dpy, td->win->win,
				CWBackPixel, &xattr);
		}
	}

	/* Success */
	return (0);
}


/**
 * Handle a "special request"
 */
static errr Term_xtra_x11(int n, int v)
{
	/* Handle a subset of the legal requests */
	switch (n)
	{
		/* Make a noise */
		case TERM_XTRA_NOISE: Metadpy_do_beep(); return (0);

		/* Flush the output XXX XXX */
		case TERM_XTRA_FRESH: Metadpy_update(1, 0, 0); return (0);

		/* Process random events XXX */
		case TERM_XTRA_BORED: return (CheckEvent(0));

		/* Process Events XXX */
		case TERM_XTRA_EVENT: return (CheckEvent(v));

		/* Flush the events XXX */
		case TERM_XTRA_FLUSH: while (!CheckEvent(false)); return (0);

		/* Handle change in the "level" */
		case TERM_XTRA_LEVEL: return (Term_xtra_x11_level(v));

		/* Clear the screen */
		case TERM_XTRA_CLEAR: Infowin_wipe(); return (0);

		/* Delay for some milliseconds */
		case TERM_XTRA_DELAY:
			if (v > 0) usleep(1000 * v);
			return (0);

		/* React to changes */
		case TERM_XTRA_REACT: return (Term_xtra_x11_react());
	}

	/* Unknown */
	return (1);
}


/**
 * Draw the cursor as a rectangular outline
 */
static errr Term_curs_x11(int x, int y)
{
	term_data *td = (term_data*)(Term->data);

	XDrawRectangle(Metadpy->dpy, Infowin->win, xor->gc,
				   x * td->tile_wid + Infowin->ox,
				   y * td->tile_hgt + Infowin->oy,
				   td->tile_wid - 1, td->tile_hgt - 1);

	/* Success */
	return (0);
}


/**
 * Draw the double width cursor as a rectangular outline
 */
static errr Term_bigcurs_x11(int x, int y)
{
	term_data *td = (term_data*)(Term->data);

	XDrawRectangle(Metadpy->dpy, Infowin->win, xor->gc,
				   x * td->tile_wid + Infowin->ox,
				   y * td->tile_hgt + Infowin->oy,
				   td->tile_wid2 - 1, td->tile_hgt - 1);

	/* Success */
	return (0);
}


/**
 * Erase some characters.
 */
static errr Term_wipe_x11(int x, int y, int n)
{
	/* Erase (use black) */
	Infoclr_set(clr[COLOUR_DARK]);

	/* Mega-Hack -- Erase some space */
	Infofnt_text_non(x, y, L"", n);

	/* Success */
	return (0);
}


/**
 * Draw some textual characters.
 */
static errr Term_text_x11(int x, int y, int n, int a, const wchar_t *s)
{
	/* Draw the text */
	Infoclr_set(clr[(a / MULT_BG) * MAX_COLORS + (a % MAX_COLORS)]);

	/* Draw the text */
	Infofnt_text_std(x, y, s, n);

	/* Success */
	return (0);
}




static void save_prefs(void)
{
	ang_file *fff;
	int i;

	/* Open the settings file */
	fff = file_open(settings, MODE_WRITE, FTYPE_TEXT);
	if (!fff) return;

	/* Header */
	file_putf(fff, "# %s X11 settings\n\n", VERSION_NAME);

	/* Number of term windows to open */
	file_putf(fff, "TERM_WINS=%d\n\n", term_windows_open);

	/* Save window prefs */
	for (i = 0; i < MAX_TERM_DATA; i++) {
		term_data *td = &data[i];

		if (!td->t.mapped_flag) continue;

		/* Header */
		file_putf(fff, "# Term %d\n", i);

		/*
		 * This doesn't seem to work under various WMs
		 * since the decoration messes the position up
		 *
		 * Use saved window positions.
		 * This means that we won't remember ingame repositioned
		 * windows, but also means that WMs won't screw predefined
		 * positions up. -CJN-
		 */

		/* Window specific location (x) */
		file_putf(fff, "AT_X_%d=%d\n", i, td->win->x_save);

		/* Window specific location (y) */
		file_putf(fff, "AT_Y_%d=%d\n", i, td->win->y_save);

		/* Window specific cols */
		file_putf(fff, "COLS_%d=%d\n", i, td->t.wid);

		/* Window specific rows */
		file_putf(fff, "ROWS_%d=%d\n", i, td->t.hgt);

		/* Window specific inner border offset (ox) */
		file_putf(fff, "IBOX_%d=%d\n", i, td->win->ox);

		/* Window specific inner border offset (oy) */
		file_putf(fff, "IBOY_%d=%d\n", i, td->win->oy);

		/* Window specific font name */
		file_putf(fff, "FONT_%d=%s\n", i, td->fnt->name);

		/* Window specific tile width */
		file_putf(fff, "TILE_WIDTH_%d=%d\n", i, td->tile_wid);

		/* Window specific tile height */
		file_putf(fff, "TILE_HEIGHT_%d=%d\n", i, td->tile_hgt);

		/* Footer */
		file_putf(fff, "\n");
	}

	/* Close */
	file_close(fff);
}


/**
 * Initialize a term_data
 */
static errr term_data_init(term_data *td, int i)
{
	term *t = &td->t;

	const char *name = angband_term_name[i];

	const char *font;

	int x = 0;
	int y = 0;

	int cols = 80;
	int rows = 24;

	int ox = 1;
	int oy = 1;

	int wid, hgt, num;

	const char *str;

	int val;

	XClassHint *ch;

	char res_name[20];
	char res_class[20];

	XSizeHints *sh;

	XWMHints *wmh;

	ang_file *fff;

	char buf[1024];
	char cmd[40];
	char font_name[256];

	/* Get default font for this term */
	font = get_default_font(i);

	/* Open the file */
	fff = file_open(settings, MODE_READ, FTYPE_TEXT);

	/* File exists */
	if (fff) {
		/* Process the file */
		while (file_getl(fff, buf, sizeof(buf))) {
			/* Skip "empty" lines */
			if (!buf[0]) continue;

			/* Skip "blank" lines */
			if (isspace((unsigned char)buf[0])) continue;

			/* Skip comments */
			if (buf[0] == '#') continue;

			/* Window specific location (x) */
			strnfmt(cmd, sizeof(cmd), "AT_X_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				x = (str != NULL) ? atoi(str + 1) : -1;
				continue;
			}

			/* Window specific location (y) */
			strnfmt(cmd, sizeof(cmd), "AT_Y_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				y = (str != NULL) ? atoi(str + 1) : -1;
				continue;
			}

			/* Window specific cols */
			strnfmt(cmd, sizeof(cmd), "COLS_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				val = (str != NULL) ? atoi(str + 1) : -1;
				if (val > 0) cols = val;
				continue;
			}

			/* Window specific rows */
			strnfmt(cmd, sizeof(cmd), "ROWS_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				val = (str != NULL) ? atoi(str + 1) : -1;
				if (val > 0) rows = val;
				continue;
			}

			/* Window specific inner border offset (ox) */
			strnfmt(cmd, sizeof(cmd), "IBOX_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				val = (str != NULL) ? atoi(str + 1) : -1;
				if (val > 0) ox = val;
				continue;
			}

			/* Window specific inner border offset (oy) */
			strnfmt(cmd, sizeof(cmd), "IBOY_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				val = (str != NULL) ? atoi(str + 1) : -1;
				if (val > 0) oy = val;
				continue;
			}

			/* Window specific font name */
			strnfmt(cmd, sizeof(cmd), "FONT_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				if (str != NULL) {
					my_strcpy(font_name, str + 1, sizeof(font_name));
					font = font_name;
				}
				continue;
			}

			/* Window specific tile width */
			strnfmt(cmd, sizeof(cmd), "TILE_WIDTH_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				val = (str != NULL) ? atoi(str + 1) : -1;
				if (val > 0) td->tile_wid = val;
				continue;
			}

			/* Window specific tile height */
			strnfmt(cmd, sizeof(cmd), "TILE_HEIGHT_%d", i);

			if (prefix(buf, cmd)) {
				str = strstr(buf, "=");
				val = (str != NULL) ? atoi(str + 1) : -1;
				if (val > 0) td->tile_hgt = val;
				continue;
			}
		}

		/* Close */
		file_close(fff);
	}

	/*
	 * Env-vars overwrite the settings in the settings file
	 */

	/* Window specific location (x) */
	strnfmt(buf, sizeof(buf), "ANGBAND_X11_AT_X_%d", i);
	str = getenv(buf);
	val = (str != NULL) ? atoi(str) : -1;
	if (val > 0) x = val;

	/* Window specific location (y) */
	strnfmt(buf, sizeof(buf), "ANGBAND_X11_AT_Y_%d", i);
	str = getenv(buf);
	val = (str != NULL) ? atoi(str) : -1;
	if (val > 0) y = val;

	/* Window specific cols */
	strnfmt(buf, sizeof(buf), "ANGBAND_X11_COLS_%d", i);
	str = getenv(buf);
	val = (str != NULL) ? atoi(str) : -1;
	if (val > 0) cols = val;

	/* Window specific rows */
	strnfmt(buf, sizeof(buf), "ANGBAND_X11_ROWS_%d", i);
	str = getenv(buf);
	val = (str != NULL) ? atoi(str) : -1;
	if (val > 0) rows = val;

	/* Window specific inner border offset (ox) */
	strnfmt(buf, sizeof(buf), "ANGBAND_X11_IBOX_%d", i);
	str = getenv(buf);
	val = (str != NULL) ? atoi(str) : -1;
	if (val > 0) ox = val;

	/* Window specific inner border offset (oy) */
	strnfmt(buf, sizeof(buf), "ANGBAND_X11_IBOY_%d", i);
	str = getenv(buf);
	val = (str != NULL) ? atoi(str) : -1;
	if (val > 0) oy = val;

	/* Window specific font name */
	strnfmt(buf, sizeof(buf), "ANGBAND_X11_FONT_%d", i);
	str = getenv(buf);
	if (str) font = str;

	/* Hack the main window must be at least 80x24 */
	if (!i) {
		if (cols < 80) cols = 80;
		if (rows < 24) rows = 24;
	}

	/* Prepare the standard font */
	td->fnt = mem_zalloc(sizeof(infofnt));
	Infofnt_set(td->fnt);
	if (Infofnt_init_data(font))
		quit_fmt("Couldn't load the requested font. (%s)", font);

	/* Use proper tile size */
	if (td->tile_wid <= 0) td->tile_wid = td->fnt->twid;
	if (td->tile_hgt <= 0) td->tile_hgt = td->fnt->hgt;

	/* Don't allow bigtile mode - one day maybe NRM */
	td->tile_wid2 = td->tile_wid;

	/* Key buffer size */
	num = ((i == 0) ? 1024 : 16);

	/* Assume full size windows */
	wid = cols * td->tile_wid + (ox + ox);
	hgt = rows * td->tile_hgt + (oy + oy);

	/* Create a top-window */
	td->win = mem_zalloc(sizeof(infowin));
	Infowin_set(td->win);
	Infowin_init_top(x, y, wid, hgt, 0, Metadpy->fg, Metadpy->bg);

	/* Ask for certain events */
	Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask
			 | ButtonPressMask);

	/* Set the window name */
	Infowin_set_name(name);

	/* Save the inner border */
	Infowin->ox = ox;
	Infowin->oy = oy;

	/* Make Class Hints */
	ch = XAllocClassHint();

	if (ch == NULL) quit("XAllocClassHint failed");

	my_strcpy(res_name, name, sizeof(res_name));
	res_name[0] = tolower((unsigned char)res_name[0]);
	ch->res_name = res_name;

	my_strcpy(res_class, "Angband", sizeof(res_class));
	ch->res_class = res_class;

	XSetClassHint(Metadpy->dpy, Infowin->win, ch);

	/* Make Size Hints */
	sh = XAllocSizeHints();

	/* Oops */
	if (sh == NULL) quit("XAllocSizeHints failed");

	if (x || y)
		sh->flags = USPosition;
	else
		sh->flags = 0;

	/* Main window has a differing minimum size */
	if (i == 0) {
		/* Main window min size is 80x24 */
		sh->flags |= (PMinSize | PMaxSize);
		sh->min_width = 80 * td->tile_wid + (ox + ox);
		sh->min_height = 24 * td->tile_hgt + (oy + oy);
		sh->max_width = 255 * td->tile_wid + (ox + ox);
		sh->max_height = 255 * td->tile_hgt + (oy + oy);
	} else {
		/* Other windows */
		sh->flags |= (PMinSize | PMaxSize);
		sh->min_width = td->tile_wid + (ox + ox);
		sh->min_height = td->tile_hgt + (oy + oy);
		sh->max_width = 255 * td->tile_wid + (ox + ox);
		sh->max_height = 255 * td->tile_hgt + (oy + oy);
	}

	/* Resize increment */
	sh->flags |= PResizeInc;
	sh->width_inc = td->tile_wid;
	sh->height_inc = td->tile_hgt;

	/* Base window size */
	sh->flags |= PBaseSize;
	sh->base_width = (ox + ox);
	sh->base_height = (oy + oy);

	/* Use the size hints */
	XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh);

	/* WMHints */
	wmh = XAllocWMHints();

	if(wmh == NULL) quit("XAllocWMHints failed");

	wmh->flags |= WindowGroupHint;

	if(i == 0) {
		// root points to itself
		wmh->window_group = td->win->win;
	} else {
		// others point to root
		wmh->window_group = data[0].win->win;
	}
	XSetWMHints(Metadpy->dpy, Infowin->win, wmh);
	XFree(wmh);

	/* Map the window */
	Infowin_map();

	/* Set pointers to allocated data */
	td->sizeh = sh;
	td->classh = ch;

	/* Move the window to requested location */
	if ((x >= 0) && (y >= 0)) Infowin_impell(x, y);


	/* Initialize the term */
	term_init(t, cols, rows, num);

	/* Use a "soft" cursor */
	t->soft_cursor = true;

	/* Differentiate between BS/^h, Tab/^i, etc. */
	t->complex_input = true;

	/* Hooks */
	t->xtra_hook = Term_xtra_x11;
	t->curs_hook = Term_curs_x11;
	t->bigcurs_hook = Term_bigcurs_x11;
	t->wipe_hook = Term_wipe_x11;
	t->text_hook = Term_text_x11;

	/* Save the data */
	t->data = td;

	/* Activate (important) */
	Term_activate(t);

	/* Success */
	return (0);
}


const char help_x11[] = "Basic X11, subopts -d<display> -n<windows> -x<file>";

static void hook_quit(const char *str)
{
	int i;

	/* Unused */
	(void)str;

	save_prefs();

	/* Free allocated data */
	for (i = 0; i < term_windows_open; i++) {
		term_data *td = &data[i];
		term *t = &td->t;

		/* Free size hints */
		XFree(td->sizeh);

		/* Free class hints */
		XFree(td->classh);

		/* Free fonts */
		Infofnt_set(td->fnt);
		(void)Infofnt_nuke();
		mem_free(td->fnt);

		/* Free window */
		Infowin_set(td->win);
		(void)Infowin_nuke();
		mem_free(td->win);

		/* Free term */
		(void)term_nuke(t);
	}

	/* Free colors */
	Infoclr_set(xor);
	(void)Infoclr_nuke();
	mem_free(xor);

	for (i = 0; i < MAX_COLORS * BG_MAX; ++i) {
		Infoclr_set(clr[i]);
		(void)Infoclr_nuke();
		mem_free(clr[i]);
	}

	/* Close link to display */
	(void)Metadpy_nuke();
}


/*
 * Initialization function for an "X11" module to Angband
 */
errr init_x11(int argc, char **argv)
{
	int i;

	const char *dpy_name = "";

	int num_term = -1;

	ang_file *fff;

	char buf[1024];
	const char *str;
	int val;

	/* Parse args */
	for (i = 1; i < argc; i++) {
		if (prefix(argv[i], "-d"))
		{
			dpy_name = &argv[i][2];
			continue;
		}

		if (prefix(argv[i], "-n")) {
			num_term = atoi(&argv[i][2]);
			if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA;
			else if (num_term < 1) num_term = 1;
			continue;
		}

		if (prefix(argv[i], "-x")) {
			x11_prefs = argv[i] + 2;
			continue;
		}

		plog_fmt("Ignoring option: %s", argv[i]);
	}


	if (num_term == -1) {
		num_term = 1;

		/* Build the filename */
		(void)path_build(settings, sizeof(settings), ANGBAND_DIR_USER,
						 "x11-settings.prf");

		/* Open the file */
		fff = file_open(settings, MODE_READ, FTYPE_TEXT);

		/* File exists */
		if (fff) {
			/* Process the file */
			while (file_getl(fff, buf, sizeof(buf))) {
				/* Skip "empty" lines */
				if (!buf[0]) continue;
	
				/* Skip "blank" lines */
				if (isspace((unsigned char)buf[0])) continue;
	
				/* Skip comments */
				if (buf[0] == '#') continue;
	
				/* Number of terminal windows */
				if (prefix(buf, "TERM_WINS")) {
					str = strstr(buf, "=");
					val = (str != NULL) ? atoi(str + 1) : -1;
					if (val > 0) num_term = val;
					continue;
				}
			}
	
			/* Close */
			(void)file_close(fff);
		}
	}


	/* Init the Metadpy if possible */
	if (Metadpy_init_name(dpy_name)) return (-1);

	/* Remember the number of terminal windows */
	term_windows_open = num_term;

	/* Prepare cursor color */
	xor = mem_zalloc(sizeof(infoclr));
	Infoclr_set(xor);
	Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0);


	/* Prepare normal colors */
	for (i = 0; i < MAX_COLORS * BG_MAX; ++i) {
		Pixell pixel;

		clr[i] = mem_zalloc(sizeof(infoclr));

		Infoclr_set(clr[i]);

		/* Acquire Angband colors */
		color_table_x11[i % MAX_COLORS][0] =
			angband_color_table[i % MAX_COLORS][0];
		color_table_x11[i % MAX_COLORS][1] =
			angband_color_table[i % MAX_COLORS][1];
		color_table_x11[i % MAX_COLORS][2] =
			angband_color_table[i % MAX_COLORS][2];
		color_table_x11[i % MAX_COLORS][3] =
			angband_color_table[i % MAX_COLORS][3];

		/* Default to monochrome */
		pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg);

		/* Handle color 
		   This block of code has added support for background colours
                   (from Sil) */
		if (Metadpy->color) {
			Pixell backpixel;
			/* Create pixel */
			pixel = create_pixel(Metadpy->dpy,
					     color_table_x11[i % MAX_COLORS][1],
					     color_table_x11[i % MAX_COLORS][2],
					     color_table_x11[i % MAX_COLORS][3]);
			switch (i / MAX_COLORS)
			{
				case BG_BLACK:
					/* Default Background */
					Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
					break;
				case BG_SAME:
					/* Background same as foreground */
					backpixel = create_pixel(Metadpy->dpy,
											 color_table_x11[i % MAX_COLORS][1],
											 color_table_x11[i % MAX_COLORS][2],
											 color_table_x11[i % MAX_COLORS][3]);
					Infoclr_init_ppn(pixel, backpixel, "cpy", 0);
					break;
				case BG_DARK:
					/* Highlight Background */
					backpixel = create_pixel(Metadpy->dpy,
											 color_table_x11[COLOUR_SHADE][1],
											 color_table_x11[COLOUR_SHADE][2],
											 color_table_x11[COLOUR_SHADE][3]);
					Infoclr_init_ppn(pixel, backpixel, "cpy", 0);
					break;
			}
		} else {
			/* Handle monochrome */
		    Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
		}
	}


	/* Initialize the windows */
	for (i = 0; i < num_term; i++) {
		term_data *td = &data[i];

		/* Initialize the term_data */
		term_data_init(td, i);

		/* Save global entry */
		angband_term[i] = Term;
	}

	/* Raise the "Angband" window */
	Infowin_set(data[0].win);
	Infowin_raise();

	/* Activate the "Angband" window screen */
	Term_activate(&data[0].t);


	/* Activate hook */
	quit_aux = hook_quit;

	/* Success */
	return (0);
}

#endif /* USE_X11 */


