/*  Motti -- a strategy game
    Copyright (C) 1999 Free Software Foundation

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include <config.h>

/* This file has general X init routines. The O'Reilly's Xlib
   Programming manual by Adrian Nye has been used as a guide.  */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xatom.h>

#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

/* What architechure doesn't have gethostname???  Autoscan suggested this.  */
#ifdef HAVE_GETHOSTNAME
# include <unistd.h>
#endif

#include "xwin.h"
#include "map.h"
#include "xinit.h"
#include "wrappers.h"
#include "comm.h"

int global_argc = 0;
char **global_argv = 0;

static void create_DB (struct x_info_struct *);
static int alloc_colors (struct x_info_struct *);
static void open_windows (struct x_info_struct *);
static enum mouse_action parse_mouse_string (const char *);
static void init_DB_tab (struct x_info_struct *);
static void * x_handler (void *);
#if 0
static void create_DB (struct x_info_struct *);
#endif
static int alloc_Xrm_color (struct x_info_struct *, const char *,
			    const char *, const char *, XColor *);
#if 0
static int alloc_colors (struct x_info_struct *);
#endif
static void make_map_stipples (struct x_info_struct *,Pixmap, Pixmap, int,
			       int);
static void make_att_buttons (struct x_info_struct *);
static void make_dis_att_buttons (struct x_info_struct *);
static void make_att_pixs (struct x_info_struct *);
static void make_def_pixs (struct x_info_struct *);
static void make_gue_pixs (struct x_info_struct *);
static void calculate_extents (struct x_info_struct *, const Font);
static void alloc_gcs (struct x_info_struct *);
static void set_hints (struct x_info_struct *, const int, const int,
		       const int);

extern int
x_game_init (disp_name, x_info)
     const char *disp_name;
     struct x_info_struct *x_info;
{
  x_info->display = XOpenDisplay (disp_name);
  if (!x_info->display)
    {
      if (n_connect == 1 && !*disp_name)
	return 0;
      else
	die ("can't open display %s", disp_name);
    }
  x_info->fd = ConnectionNumber (x_info->display);

  create_DB (x_info);
  if (!alloc_colors (x_info))
    die ("error allocating colors");
  open_windows (x_info);
  return 1;
}

static enum mouse_action
parse_mouse_string (act_name)
     const char *act_name;
{
  register enum mouse_action i;
  const char *all_acts[] = {
    "mark", "select", "act", "last_place", "attack", "defend",
    "guerilla", "reset", "smart", "halfsmart", "none"
  };
  for (i = mark; i <= no_act; i++)
    if (!strcasecmp (all_acts[i], act_name))
      return i;
  return no_act;
}

static void
init_DB_tab (x_info)
     struct x_info_struct *x_info;
{
  enum types 
  {
    type_short, type_string, type_mouse, type_geometry, end
  };
  struct val_tab
  {
    const enum types type;
    void *ptr;
    const char *name, *class;
    union
    {
      const int val;
      const char *str;
      const enum mouse_action mouse;
    } def;
  };
  struct val_tab vals[] =
  {
    {type_short, &x_info->db_tab.but_width, "motti.button.width",
     "Motti.Button.Width", 80},
    {type_short, &x_info->db_tab.but_height, "motti.button.height",
     "Motti.Button.Height", 20},
    {type_geometry, &x_info->db_tab.geom_str, "motti.geometry",
     "Motti.Geometry", 0},
    {type_short, &x_info->db_tab.turnwin_width, "motti.turnwindow.width",
     "Motti.Turnwindow.Width", 40},
    {type_short, &x_info->db_tab.turnwin_height,
     "motti.turnwindow.height", "Motti.Turnwindow.Height", 20},
    {type_string, &x_info->db_tab.cross_left_font,
     "motti.turnwindow.font", "Motti.Font", 0},
    {type_short, &x_info->db_tab.map_square_size, "motti.map.squaresize",
     "Motti.Map.Squaresize", 20},
    {type_short, &x_info->db_tab.x_margin, "motti.x.margin",
     "Motti.Margin", 10},
    {type_short, &x_info->db_tab.y_margin, "motti.y.margin",
     "Motti.Margin", 10},
    {type_short, &x_info->db_tab.sel_width, "motti.selected.width",
     "Motti.Width", 3},
    {type_mouse, &x_info->db_tab.mouse[0], "motti.mousebutton1",
     "Motti.Mousebutton1", mark},
    {type_mouse, &x_info->db_tab.mouse[1], "motti.mousebutton2",
     "Motti.Mousebutton2", sel},
    {type_mouse, &x_info->db_tab.mouse[2], "motti.mousebutton3",
     "Motti.Mousebutton3", act},
    {type_mouse, &x_info->db_tab.mouse[3], "motti.mousebutton4",
     "Motti.Mousebutton4", no_act},
    {type_mouse, &x_info->db_tab.mouse[4], "motti.mousebutton5",
     "Motti.Mousebutton5", no_act},
    {type_string, &x_info->db_tab.end_msg, "motti.gameend.mesg",
     "Motti.Message", 0},
    {type_string, &x_info->db_tab.end_font, "motti.gameend.font",
     "Motti.Font", 0},
    {end, NULL, "", "", 0}
  };
  struct val_tab *val_ptr = vals;
  vals[5].def.str = "-adobe-helvetica-medium-r-normal--20-0-75-75-p-0-iso8859-1";
  vals[15].def.str = "Game won by player %d.";
  vals[16].def.str = "-adobe-utopia-medium-i-normal--34-0-0-100-p-0-iso8859-1";

  while (val_ptr->type != end)
    {
      char *str_type;
      XrmValue value;
      if (XrmGetResource (x_info->DB, val_ptr->name, val_ptr->class,
			  &str_type, &value))
	{
	  switch (val_ptr->type)
	    {
	      char *str;
	    case type_short:
	      *((short *) val_ptr->ptr) = (short) atoi (value.addr);
	      break;
	    case type_string:
	    case type_geometry:
	      str = (char *) my_malloc ((strlen (value.addr)+1) *
					 sizeof (char));
	      strcpy (str, value.addr);
	      *((char **) val_ptr->ptr) = str;
	      break;
	    case type_mouse:
	      *((enum mouse_action *) val_ptr->ptr) =
		parse_mouse_string (value.addr);
	      if (*((enum mouse_action *) val_ptr->ptr) > no_act)
		die ("invalid mouse action: %s", value.addr);
	      break;
	    }
	}
      else
	{
	  switch (val_ptr->type)
	    {
	    case type_short:
	      *((short *) val_ptr->ptr) = (short) val_ptr->def.val;
	      break;
	    case type_string:
	    case type_geometry:
	      *((const char **) val_ptr->ptr) = val_ptr->def.str;
	      break;
	    case type_mouse:
	      *((enum mouse_action *) val_ptr->ptr) =
		val_ptr->def.mouse;
	      break;
	    }
	  if (val_ptr->type != type_geometry)
	    fprintf (stderr, "motti: using hard-coded default resource value for %s\n", val_ptr->name);
	}
      val_ptr++;
    }
}

static void
create_DB (x_info)
     struct x_info_struct *x_info;
{
  char filename[1024] = APPDEF;
  char *environment;

  /* These are defined now for later use.  */
  x_info->screen_num = DefaultScreen (x_info->display);
  x_info->depth = DefaultDepth (x_info->display, x_info->screen_num);

  /* First get the application defaults.  */
  x_info->DB = XrmGetFileDatabase (filename);

  /* Next get the server defaults.  */
  if (XResourceManagerString (x_info->display) != NULL)
    {
      XrmDatabase serverDB;
      serverDB = XrmGetStringDatabase (XResourceManagerString
				       (x_info->display));
      XrmMergeDatabases(serverDB, &x_info->DB);
    }
  else
    XrmCombineFileDatabase ("~/.Xdefaults", &x_info->DB, True);

  environment = getenv ("XENVIRONMENT");
#if HAVE_GETHOSTNAME
  if (!environment)
    {
      environment = filename;
      strcpy (filename, "~/.Xdefaults-");
      gethostname (filename+13, 1011);
    }
#else
  if (environment)
#endif
    XrmCombineFileDatabase (environment, &x_info->DB, True);
  init_DB_tab (x_info);
}

static int
alloc_Xrm_color (x_info, instance, class, deflt, color)
     struct x_info_struct *x_info;
     const char *instance, *class;
     const char *deflt;
     XColor *color;
{
  char *msg = NULL;
  const char *color_name;
  XrmValue value;
  XColor nullcol;
  char *str_type;

  if (XrmGetResource (x_info->DB, instance, class, &str_type, &value))
    color_name = value.addr;
  else
    {
      if (deflt)
	{
	  color_name = deflt;
	  fprintf (stderr, "motti: using hard-coded default color \"%s\" for %s\n",
		   deflt, instance);
	}
      else
	{
	  fprintf (stderr, "motti: can't find color: %s\n", class);
	  return 0;
	}
    }

  while (!XAllocNamedColor (x_info->display, x_info->colormap, color_name,
			    color, &nullcol))
    {
      if (!x_info->own_colormap) /* Don't try to get another colormap.  */
	{
	  /* I've decided to use this function instead of displaying an error
	     and exiting, since motti might have several connections open, and
	     each one of them would then have to be told explicitly with a
	     command line argument to create an own colormap.  */
	  x_info->colormap = XCopyColormapAndFree (x_info->display,
						   x_info->colormap);
	  x_info->own_colormap = 1;	/* Now have own colormap.  */
	  /* Let the user know we have taken the liberty to allocate a new
	     colormap.  */
	  fprintf (stderr, "motti: created a new colormap for display \"%s\"\n",
		   x_info->display);
	  continue;	/* Try again, with an own colormap.  */
	}
      else
	{
	  fprintf (stderr, "motti: can't allocate a color: %s\n", color_name);
	  return 0;
	}
    }
  return 1;
}

static int
alloc_colors (x_info)
     struct x_info_struct *x_info;
{
  int i;
  XVisualInfo visual_info;

  /* Search for the best visual.  */
  for (i = 5; !XMatchVisualInfo (x_info->display, x_info->screen_num,
				 x_info->depth, i, &visual_info); i--);
  /* Would anybody feel like volunteering to write some monochrome visual
     stuff?  */
  if (i < StaticColor)
    return 0;

  x_info->colormap = DefaultColormap (x_info->display, x_info->screen_num);
  x_info->own_colormap = 0;

  x_info->player_col = (XColor *) my_malloc (sizeof(XColor)*game_map.players);
  x_info->occupied_col = (XColor *) my_malloc
    (sizeof(XColor)*game_map.players);

  if (!(alloc_Xrm_color (x_info, "motti.background", "Motti.Background",
			"white", &x_info->misc_col[bg_col])
	&& alloc_Xrm_color (x_info, "motti.foreground", "Motti.Foreground",
			    "black", &x_info->misc_col[fg_col])
	&& alloc_Xrm_color (x_info, "motti.color.map.seacolor",
			    "Motti.Map.Seacolor", "RGBi:0/0/0.05",
			    &x_info->misc_col[map_bg_col])
	&& alloc_Xrm_color (x_info, "motti.color.capital",
			    "Motti.Color.Capital", "gray50",
			    &x_info->misc_col[capital_col])
	&& alloc_Xrm_color (x_info, "motti.color.cross",
			    "Motti.Color.Cross", "gray70",
			    &x_info->misc_col[cross_col])
	&& alloc_Xrm_color (x_info, "motti.button.foreground",
			    "Motti.Foreground", "black",
			    &x_info->misc_col[but_fg])
	&& alloc_Xrm_color (x_info, "motti.button.background",
			    "Motti.Background", "white",
			    &x_info->misc_col[but_bg])
	&& alloc_Xrm_color (x_info, "motti.color.cross_left",
			    "Motti.Foreground", "black",
			    &x_info->misc_col[cross_left])
	&& alloc_Xrm_color (x_info, "motti.color.turnwin_border",
			    "Motti.Background", "black",
			    &x_info->misc_col[turn_win_border])
	&& alloc_Xrm_color (x_info, "motti.color.endmsg.fontcol",
			    "Motti.Foreground", "black",
			    &x_info->misc_col[end_font_col])
	&& alloc_Xrm_color (x_info, "motti.color.endmsg.background",
			    "Motti.Background", "white",
			    &x_info->misc_col[end_bg_col])
	&& alloc_Xrm_color (x_info, "motti.color.endmsg.border",
			    "Motti.Foreground", "black",
			    &x_info->misc_col[end_bor_col])))
    return 0;

#define MAXDEFCOLPLAYER 4
  for (i = 1; i <= game_map.players; i++)
    {
      const char *def_col[] =
      {
	"DarkRed", "Red",
	"DarkBlue", "Blue",
	"DarkGreen", "Green",
	"DarkGoldenrod4", "DarkGoldenrod1",
	""
      };
      char instancename[25];
      sprintf (instancename, "motti.player%i.color", i);
      if (!alloc_Xrm_color (x_info, instancename, "Motti.Player.Color",
			    def_col[i <= MAXDEFCOLPLAYER ? (i-1)*2 : 4],
			    &x_info->player_col[i-1]))
	return 0;

      sprintf (instancename, "motti.player%iOcc.color", i);
      if (!alloc_Xrm_color (x_info, instancename, "Motti.PlayerOcc.Color",
			    def_col[i <= MAXDEFCOLPLAYER ? (i-1)*2+1 : 4],
			    &x_info->occupied_col[i-1]))
	return 0;
    }
  XrmDestroyDatabase (x_info->DB);	/* Not needed after this.  */
  return 1;
#undef MAXDEFCOLPLAYER
}

static void
make_map_stipples (x_info, capital, cross, width, height)
     struct x_info_struct *x_info;
     Pixmap capital, cross;
     int width, height;
{
  XGCValues gcvalues;

  gcvalues.line_width = 1;
  x_info->tempgc = XCreateGC (x_info->display, capital, GCLineWidth,
			      &gcvalues);

  XSetForeground (x_info->display, x_info->tempgc, 1);
  XFillRectangle (x_info->display, capital, x_info->tempgc, 0, 0, width+1,
		  height+1);
  XSetForeground (x_info->display, x_info->tempgc, 0);
  XFillRectangle (x_info->display, capital, x_info->tempgc, width/3, height/3,
		  width/2, height/2);

  XFillRectangle (x_info->display, cross, x_info->tempgc, 0, 0, width+1,
		  height+1);
  XSetForeground (x_info->display, x_info->tempgc, 1);
  XDrawLine (x_info->display, cross, x_info->tempgc, width/6, height/6,
	     (5*width)/6, (5*height)/6);
  XDrawLine (x_info->display, cross, x_info->tempgc, (5*width)/6, height/6,
	     width/6, (5*height)/6);
}

#include "bitmaps/att"
#include "bitmaps/def"
#include "bitmaps/gue"

static void
make_att_buttons (x_info)
     struct x_info_struct *x_info;
{
  int mark_pos, mark_top, mark_height, mark_space, mark_width;
  int pos_fourth; 
  int i = 0;

  mark_top = x_info->db_tab.but_height / 3;
  mark_height = mark_top;
  mark_space = (x_info->db_tab.but_width/2 - att_width/2) / 5;
  mark_pos = mark_space;
  mark_width = 4;
  pos_fourth = x_info->db_tab.but_width/2 + att_width/2 + mark_space;

  XSetFunction (x_info->display, x_info->tempgc, GXcopy);
  XSetFillStyle (x_info->display, x_info->tempgc, FillSolid);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_fg].pixel);
  while (1)
    {
      XFillRectangle (x_info->display, x_info->att_pix[i], x_info->tempgc,
		      mark_pos, mark_top, mark_width, mark_height);
      if (i < 5)
	XCopyArea (x_info->display, x_info->att_pix[i], x_info->att_pix[i+1],
		   x_info->tempgc, 0, 0, x_info->db_tab.but_width,
		   x_info->db_tab.but_height, 0, 0);
      else
	return;
      if (++i == 3)
	mark_pos = pos_fourth;
      else
	mark_pos += mark_space;
    }
}

static void
make_dis_att_buttons (x_info)
     struct x_info_struct *x_info;
{
  register int i;
  XSetFunction (x_info->display, x_info->tempgc, GXcopyInverted);
  for (i = 0; i < CROSS_MAX; i++)
    XCopyArea (x_info->display, x_info->att_pix[i], x_info->d_att_pix[i+1],
	       x_info->tempgc, 0, 0, x_info->db_tab.but_width,
	       x_info->db_tab.but_height, 0, 0);
  XSetFunction (x_info->display, x_info->tempgc, GXcopy);
}

static void
make_att_pixs (x_info)
     struct x_info_struct *x_info;
{
  Pixmap pix;

  x_info->att_pix[0] = XCreatePixmap (x_info->display, x_info->main_win,
				      x_info->db_tab.but_width,
				      x_info->db_tab.but_height,
				      x_info->depth);
  x_info->att_pix[1] = XCreatePixmap (x_info->display, x_info->main_win,
				      x_info->db_tab.but_width,
				      x_info->db_tab.but_height,
				      x_info->depth);
  x_info->att_pix[2] = XCreatePixmap (x_info->display, x_info->main_win,
				      x_info->db_tab.but_width,
				      x_info->db_tab.but_height,
				      x_info->depth);
  x_info->att_pix[3] = XCreatePixmap (x_info->display, x_info->main_win,
				      x_info->db_tab.but_width,
				      x_info->db_tab.but_height,
				      x_info->depth);
  x_info->att_pix[4] = XCreatePixmap (x_info->display, x_info->main_win,
				      x_info->db_tab.but_width,
				      x_info->db_tab.but_height,
				      x_info->depth);
  x_info->att_pix[5] = XCreatePixmap (x_info->display, x_info->main_win,
				      x_info->db_tab.but_width,
				      x_info->db_tab.but_height,
				      x_info->depth);

  x_info->d_att_pix[0] = XCreatePixmap (x_info->display, x_info->main_win,
					x_info->db_tab.but_width,
					x_info->db_tab.but_height,
					x_info->depth);
  if (n_connect > 1)
    {
      x_info->d_att_pix[1] = XCreatePixmap (x_info->display, x_info->main_win,
					    x_info->db_tab.but_width,
					    x_info->db_tab.but_height,
					    x_info->depth);
      x_info->d_att_pix[2] = XCreatePixmap (x_info->display, x_info->main_win,
					    x_info->db_tab.but_width,
					    x_info->db_tab.but_height,
					    x_info->depth);
      x_info->d_att_pix[3] = XCreatePixmap (x_info->display, x_info->main_win,
					    x_info->db_tab.but_width,
					    x_info->db_tab.but_height,
					    x_info->depth);
      x_info->d_att_pix[4] = XCreatePixmap (x_info->display, x_info->main_win,
					    x_info->db_tab.but_width,
					    x_info->db_tab.but_height,
					    x_info->depth);
      x_info->d_att_pix[5] = XCreatePixmap (x_info->display, x_info->main_win,
					    x_info->db_tab.but_width,
					    x_info->db_tab.but_height,
					    x_info->depth);
      x_info->d_att_pix[6] = XCreatePixmap (x_info->display, x_info->main_win,
					    x_info->db_tab.but_width,
					    x_info->db_tab.but_height,
					    x_info->depth);
    }

  /* The depth of the gc has to be 1.  */
  XFreeGC (x_info->display, x_info->tempgc);
  x_info->tempgc = XCreateGC (x_info->display, x_info->main_win, 0, NULL);

  /* Draw the disabled attack button pixmap.  */
  XSetFillStyle (x_info->display, x_info->tempgc, FillSolid);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_fg].pixel);
  XFillRectangle (x_info->display, x_info->d_att_pix[0], x_info->tempgc, 0, 0,
		  x_info->db_tab.but_width+1,
		  x_info->db_tab.but_height+1);
  pix = XCreateBitmapFromData (x_info->display, x_info->main_win, att_bits,
			       att_width, att_height);
  XSetFillStyle (x_info->display, x_info->tempgc, FillStippled);
  XSetStipple (x_info->display, x_info->tempgc, pix);
  XSetTSOrigin (x_info->display, x_info->tempgc, x_info->db_tab.but_width/2 -
		att_width/2, x_info->db_tab.but_height/2 - att_height/2);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_bg].pixel);
  XFillRectangle (x_info->display, x_info->d_att_pix[0], x_info->tempgc,
		  x_info->db_tab.but_width/2 - att_width/2,
		  x_info->db_tab.but_height/2 - att_height/2, att_width,
		  att_height);

  /* Draw the attack button pixmap.  */
  XSetFillStyle (x_info->display, x_info->tempgc, FillSolid);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_bg].pixel);
  XFillRectangle (x_info->display, x_info->att_pix[0], x_info->tempgc, 0, 0,
		  x_info->db_tab.but_width+1, x_info->db_tab.but_height+1);
  XSetFillStyle (x_info->display, x_info->tempgc, FillStippled);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_fg].pixel);
  XFillRectangle (x_info->display, x_info->att_pix[0], x_info->tempgc,
		  x_info->db_tab.but_width/2 - att_width/2,
		  x_info->db_tab.but_height/2 - att_height/2, att_width,
		  att_height);

  XFreePixmap (x_info->display, pix);

  /* Draw attack buttons with number of remaining attacks.  */
  make_att_buttons (x_info);
  /* Make inverse attack buttons with information about remaining attacks.  */
  if (n_connect > 1)
    make_dis_att_buttons (x_info);
}

static void
make_def_pixs (x_info)
     struct x_info_struct *x_info;
{
  Pixmap pix;

  x_info->def_pix = XCreatePixmap (x_info->display, x_info->main_win,
				   x_info->db_tab.but_width,
				   x_info->db_tab.but_height, x_info->depth);
  x_info->d_def_pix = XCreatePixmap (x_info->display, x_info->main_win,
				     x_info->db_tab.but_width,
				     x_info->db_tab.but_height,
				     x_info->depth);

  /* Draw the defend button.  */
  XSetFillStyle (x_info->display, x_info->tempgc, FillSolid);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_bg].pixel);
  XFillRectangle (x_info->display, x_info->def_pix, x_info->tempgc, 0, 0,
		  x_info->db_tab.but_width+1, x_info->db_tab.but_height+1);
  pix = XCreateBitmapFromData (x_info->display, x_info->main_win, def_bits,
			       def_width, def_height);
  XSetTSOrigin (x_info->display, x_info->tempgc, x_info->db_tab.but_width/2 -
		def_width/2, x_info->db_tab.but_height/2 - def_height/2);
  XSetFillStyle (x_info->display, x_info->tempgc, FillStippled);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_fg].pixel);
  XSetStipple (x_info->display, x_info->tempgc, pix);
  XFillRectangle (x_info->display, x_info->def_pix, x_info->tempgc,
		  x_info->db_tab.but_width/2 - def_width/2,
		  x_info->db_tab.but_height/2 - def_height/2, def_width,
		  def_height);

  /* Draw the disabled defend button.  */
  XSetFillStyle (x_info->display, x_info->tempgc, FillSolid);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_fg].pixel);
  XFillRectangle (x_info->display, x_info->d_def_pix, x_info->tempgc, 0, 0,
		  x_info->db_tab.but_width+1, x_info->db_tab.but_height+1);
  XSetFillStyle (x_info->display, x_info->tempgc, FillStippled);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_bg].pixel);
  XFillRectangle (x_info->display, x_info->d_def_pix, x_info->tempgc,
		  x_info->db_tab.but_width/2 - def_width/2,
		  x_info->db_tab.but_height/2 - def_height/2, def_width,
		  def_height);

  XFreePixmap (x_info->display, pix);
}

static void
make_gue_pixs (x_info)
     struct x_info_struct *x_info;
{
  Pixmap pix;

  x_info->gue_pix = XCreatePixmap (x_info->display, x_info->main_win,
				   x_info->db_tab.but_width,
				   x_info->db_tab.but_height, x_info->depth);
  x_info->d_gue_pix = XCreatePixmap (x_info->display, x_info->main_win,
				     x_info->db_tab.but_width,
				     x_info->db_tab.but_height,
				     x_info->depth);

  /* Draw the defend button.  */
  XSetFillStyle (x_info->display, x_info->tempgc, FillSolid);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_bg].pixel);
  XFillRectangle (x_info->display, x_info->gue_pix, x_info->tempgc, 0, 0,
		  x_info->db_tab.but_width+1, x_info->db_tab.but_height+1);
  pix = XCreateBitmapFromData (x_info->display, x_info->main_win, gue_bits,
			       gue_width, gue_height);
  XSetFillStyle (x_info->display, x_info->tempgc, FillStippled);
  XSetTSOrigin (x_info->display, x_info->tempgc, x_info->db_tab.but_width/2 -
		gue_width/2, x_info->db_tab.but_height/2 - gue_height/2);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_fg].pixel);
  XSetStipple (x_info->display, x_info->tempgc, pix);
  XFillRectangle (x_info->display, x_info->gue_pix, x_info->tempgc,
		  x_info->db_tab.but_width/2 - gue_width/2,
		  x_info->db_tab.but_height/2 - gue_height/2, gue_width,
		  gue_height);

  /* Draw the disabled defend button.  */
  XSetFillStyle (x_info->display, x_info->tempgc, FillSolid);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_fg].pixel);
  XFillRectangle (x_info->display, x_info->d_gue_pix, x_info->tempgc, 0, 0,
		  x_info->db_tab.but_width+1, x_info->db_tab.but_height+1);
  XSetFillStyle (x_info->display, x_info->tempgc, FillStippled);
  XSetForeground (x_info->display, x_info->tempgc,
		  x_info->misc_col[but_bg].pixel);
  XFillRectangle (x_info->display, x_info->d_gue_pix, x_info->tempgc,
		  x_info->db_tab.but_width/2 - gue_width/2,
		  x_info->db_tab.but_height/2 - gue_height/2, gue_width,
		  gue_height);

  XFreePixmap (x_info->display, pix);
}

static void
calculate_extents (x_info, font)
     struct x_info_struct *x_info;
     const Font font;
{
  register int i;
  for (i = 0; i <= CROSS_MAX+1; i++)
    {
      int null;
      const char *nums[] = {"6", "5", "4", "3", "2", "1", "0", "*"};
      XCharStruct charstruct;
      XQueryTextExtents (x_info->display, font, nums[i], 1, &null, &null,
			 &null, &charstruct);
      x_info->turnwin_num_base[i].x = x_info->db_tab.turnwin_width/2 -
	(charstruct.width-charstruct.lbearing)/2;
      x_info->turnwin_num_base[i].y = x_info->db_tab.turnwin_height/2 -
	(charstruct.ascent+charstruct.descent)/2 + charstruct.ascent;
    }      
}

static void
alloc_gcs (x_info)
     struct x_info_struct *x_info;
{
  XGCValues gcval = {0};
  Pixmap capital_stipple, cross_stipple;

  capital_stipple = XCreatePixmap (x_info->display, x_info->main_win,
				   x_info->db_tab.map_square_size,
				   x_info->db_tab.map_square_size, 1);
  cross_stipple = XCreatePixmap (x_info->display, x_info->main_win,
				 x_info->db_tab.map_square_size,
				 x_info->db_tab.map_square_size, 1);

  make_map_stipples (x_info, capital_stipple, cross_stipple,
		     x_info->db_tab.map_square_size,
		     x_info->db_tab.map_square_size);

  /* These three are set to draw capitals. */
  gcval.foreground = x_info->misc_col[capital_col].pixel;
  gcval.fill_style = FillStippled;
  gcval.stipple = capital_stipple;
  x_info->capitalgc = XCreateGC (x_info->display, x_info->main_win,
				 GCForeground | GCFillStyle | GCStipple,
				 &gcval);

  gcval.foreground = x_info->misc_col[cross_col].pixel;
  gcval.stipple = cross_stipple;
  x_info->crossgc = XCreateGC (x_info->display, x_info->main_win, GCForeground
			       | GCFillStyle | GCStipple, &gcval);

  /* No fields need to be set, since foreground color is changed every
     time */
  /* Note to myself: change this to make BW window. */
  x_info->mapgc = XCreateGC (x_info->display, x_info->main_win, 0, &gcval);

  /* This gc is used to draw the amount of crosses left to the turn
     window.  */
  gcval.foreground = x_info->misc_col[cross_left].pixel;
  gcval.background = x_info->occupied_col[game_map.turn-1].pixel;
  gcval.font = XLoadFont (x_info->display, x_info->db_tab.cross_left_font);
  if (!gcval.font)
    die ("can't load font: %s", x_info->db_tab.cross_left_font);
  calculate_extents (x_info, gcval.font);
  x_info->turngc = XCreateGC (x_info->display, x_info->main_win, GCBackground
			      | GCForeground | GCFont, &gcval);
}

static void
set_hints (x_info, min_width, min_height, geom_specified)
     struct x_info_struct *x_info;
     const int min_width, min_height, geom_specified;
{
  Atom save_atom;
  XWMHints *wm_hints;
  XClassHint *class_hints;
  XTextProperty window_name_prop, icon_name_prop;
  /* Add version number.  */
  char *window_name = VERSION_STR;
  char *icon_name = "motti";
  XSizeHints *size_hints;

  if (!XStringListToTextProperty (&window_name, 1, &window_name_prop)
      || !XStringListToTextProperty (&icon_name, 1, &icon_name_prop))
    die ("can't allocate memory");

  /* Allocate needed structures for properties */
  size_hints = XAllocSizeHints ();
  wm_hints = XAllocWMHints ();
  class_hints = XAllocClassHint ();

  if (geom_specified)
    size_hints->flags = USPosition | PSize | PMinSize;
  else
    size_hints->flags = PSize | PMinSize;
  x_info->main_win_width = size_hints->min_width = min_width;
  x_info->main_win_height = size_hints->min_height = min_height;

  wm_hints->initial_state = NormalState;
  wm_hints->input = False;

  /* TODO: These shouldn't be hardcoded, they should instead be settable with
     '-name'.  Motti doesn't however use Xt argument style, but GNU style.
     There's a problem with this approach anyway, since motti is designed to
     open several displays.  I haven't thought yet of an elegant way to
     handle this situation.  */
  class_hints->res_name = "motti";
  class_hints->res_class = "motti";

  XSetWMProperties (x_info->display, x_info->main_win, &window_name_prop,
		    &icon_name_prop, global_argv, global_argc, size_hints,
		    wm_hints, class_hints);

  /* Free all allocated structures */
  XFree (size_hints);
  XFree (wm_hints);
  XFree (class_hints);
  XFree (window_name_prop.value);
  XFree (icon_name_prop.value);

  save_atom = XInternAtom (x_info->display, "WM_DELETE_WINDOW", False);
  XSetWMProtocols (x_info->display, x_info->main_win, &save_atom, 1);
}

static void
open_windows (x_info)
     struct x_info_struct *x_info;
{
  XSetWindowAttributes win_attribs;
  register int i;
  int x = 0, y = 0, width, height, geom_specified, y_below_buttons;

  /* There must be room for action buttons above the map.  */
  y_below_buttons = x_info->db_tab.y_margin*2 +
    (x_info->db_tab.but_height > x_info->db_tab.turnwin_height ?
     x_info->db_tab.but_height : x_info->db_tab.turnwin_height);

  if (x_info->db_tab.geom_str)
    {
      XParseGeometry (x_info->db_tab.geom_str, &x, &y, &width, &height);
      geom_specified = 1;
    }
  else
    {
      int map_width, but_width;
      geom_specified = 0;
      but_width = x_info->db_tab.x_margin*5 + x_info->db_tab.but_width*3 +
	x_info->db_tab.turnwin_width;
      map_width = x_info->db_tab.x_margin*2 + game_map.width *
	x_info->db_tab.map_square_size;
      width = but_width > map_width ? but_width : map_width;
      height = y_below_buttons + x_info->db_tab.y_margin + game_map.height *
	x_info->db_tab.map_square_size;
    }

  win_attribs.background_pixel = x_info->misc_col[bg_col].pixel;
  win_attribs.colormap = x_info->colormap;
  x_info->main_win = XCreateWindow (x_info->display,
				    RootWindow(x_info->display,
					       x_info->screen_num), x, y,
				    width, height, 0, CopyFromParent,
				    InputOutput, CopyFromParent,
				    CWBackPixel|CWColormap, &win_attribs);

  alloc_gcs (x_info);
  make_att_pixs (x_info);
#if 0
  make_att_buttons (x_info);
#endif
  make_def_pixs (x_info);
  make_gue_pixs (x_info);
  XFreeGC (x_info->display, x_info->tempgc);

  win_attribs.border_pixel = x_info->misc_col[but_fg].pixel;
  x_info->but_win[ATT] =
    XCreateWindow (x_info->display, x_info->main_win, x_info->db_tab.x_margin,
		   x_info->db_tab.y_margin, x_info->db_tab.but_width,
		   x_info->db_tab.but_height, 1, CopyFromParent, InputOutput,
		   CopyFromParent, CWBorderPixel, &win_attribs);

  x_info->but_win[DEF] =
    XCreateWindow (x_info->display, x_info->main_win, x_info->db_tab.but_width
		   + x_info->db_tab.x_margin*2,	x_info->db_tab.y_margin,
		   x_info->db_tab.but_width, x_info->db_tab.but_height, 1,
		   CopyFromParent, InputOutput, CopyFromParent,
		   CWBorderPixel, &win_attribs);

  x_info->but_win[GUE] =
    XCreateWindow (x_info->display, x_info->main_win,
		   x_info->db_tab.but_width*2 + x_info->db_tab.x_margin*3,
		   x_info->db_tab.y_margin, x_info->db_tab.but_width,
		   x_info->db_tab.but_height, 1, CopyFromParent, InputOutput,
		   CopyFromParent, CWBorderPixel, &win_attribs);

  x_info->turn_win =
    XCreateSimpleWindow (x_info->display, x_info->main_win,
			 x_info->db_tab.but_width*3 +
			 x_info->db_tab.x_margin*4, x_info->db_tab.y_margin,
			 x_info->db_tab.turnwin_width,
			 x_info->db_tab.turnwin_height, 1,
			 x_info->misc_col[turn_win_border].pixel,
			 x_info->player_col[game_map.turn-1].pixel);

  win_attribs.border_pixel = x_info->misc_col[fg_col].pixel;
  win_attribs.background_pixel = x_info->misc_col[map_bg_col].pixel;
  x_info->map_win =
    XCreateWindow (x_info->display, x_info->main_win, x_info->db_tab.x_margin,
		   y_below_buttons, game_map.width *
		   x_info->db_tab.map_square_size, game_map.height *
		   x_info->db_tab.map_square_size, 2, CopyFromParent,
		   InputOutput, CopyFromParent, CWBorderPixel | CWBackPixel,
		   &win_attribs);

  set_hints (x_info, width, height, geom_specified);

  x_info->last_sel = MODE_ATT;
  x_info->last_active = -1;
  XSelectInput (x_info->display, x_info->main_win, 0);
  XSelectInput (x_info->display, x_info->turn_win, ExposureMask);
  XSelectInput (x_info->display, x_info->map_win, ExposureMask);

  x_info->uses_def_mode = 0;
  for (i = 0; i < 5; i++)
    if (x_info->db_tab.mouse[i] == act)
      {
	x_info->uses_def_mode = 1;
	break;
      }

  XMapWindow (x_info->display, x_info->map_win);
  XMapWindow (x_info->display, x_info->turn_win);
  XMapWindow (x_info->display, x_info->but_win[ATT]);
  XMapWindow (x_info->display, x_info->but_win[DEF]);
  XMapWindow (x_info->display, x_info->but_win[GUE]);
  XMapWindow (x_info->display, x_info->main_win);
}

#if 0
#include "bitmaps/map_pointer_active"
#include "bitmaps/map_pointer_disabled"
#include "bitmaps/map_pointer_mask"
Cursor active_cursor, disabled_cursor;

/* TODO: Make this work.  */
extern void
create_cursors ()
{
  Pixmap mask, active, disabled;
  XColor white, black;
  Colormap default_colormap;

  default_colormap = DefaultColormap (display, screen_num);
  white.pixel = WhitePixel (display, screen_num);
  black.pixel = BlackPixel (display, screen_num);
  XQueryColor (display, default_colormap, &white);
  XQueryColor (display, default_colormap, &black);

  mask = XCreateBitmapFromData (display, main_win,
				map_pointer_mask_bits,
				map_pointer_mask_width,
				map_pointer_mask_height);
  active = XCreateBitmapFromData (display, main_win,
				  map_pointer_active_bits,
				  map_pointer_active_width,
				  map_pointer_active_height);
  disabled = XCreateBitmapFromData (display, main_win,
				    map_pointer_disabled_bits,
				    map_pointer_disabled_width,
				    map_pointer_disabled_height);

  active_cursor = XCreatePixmapCursor (display, active, mask, &black,
				       &white,
				       map_pointer_active_x_hot,
				       map_pointer_active_y_hot);
  disabled_cursor = XCreatePixmapCursor (display, disabled, mask,
					 &black, &white,
					 map_pointer_disabled_x_hot,
					 map_pointer_disabled_y_hot);

  XFreePixmap (display, mask);
  XFreePixmap (display, active);
  XFreePixmap (display, disabled);
}

#endif
