/*
 * solitaire.c - A simple solitaire card game for X Windows. 
 * Written by David W. Burleigh, April 1987, for the 
 * Workstation Sales Support Training.
 * 
 */
/*
 * Converted to handle B & W displays 
 * Dave Lemke, 
 * 7/13/87, 
 * UCDavis Computer Science
 * 
 * 
 * Args: 
 * hostname:display -r	
 * -r	flag to turn off black cards reversed in B & W 
 *	- makes them simpler to read
 */
/*
 * converted to X11, 7/16/87 - 8/6/87 Dave Lemke
 */
/*
 * Final conversion work, optimizing, cleanup:
 * Dave Lemke
 * lemke@sun.com
 * Wed Jul 20 21:52:00 PDT 1988
 */

/************************************************************
Copyright 1988 by Sun Microsystems, Inc. Mountain View, CA.

		All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Sun or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific prior written permission. Sun and M.I.T. 
make no representations about the suitability of this software for 
any purpose. It is provided "as is" without any express or implied warranty.

SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. IN NO EVENT SHALL SUN BE LIABLE FOR ANY SPECIAL, INDIRECT
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
OR PERFORMANCE OF THIS SOFTWARE.
************************************************************/

#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/X10.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <strings.h>
#include "cards.bm"		/* Bitmaps for playing cards */
#include "draw.bm"		/* Bitmap for back of card deck */
#include "heart.bm"		/* Bitmaps for suits */
#include "club.bm"
#include "diamond.bm"
#include "spade.bm"
#include "cursor.bm"		/* Bitmap for cursor */
#include "restart.bm"		/* Bitmap for Restart button */
#include "flip.bm"		/* Bitmap for Flip button - Added JB */
#include "exit.bm"		/* Bitmap for Exit button */

#include "gray3.bitmap"	/* bitmap for B & W background */

#define	Border_width	3
#define	Defwidth	360	/* Default dimensions for window */
#define	Defheight	500
#define	Offset		25	/* y offset for stacking cards */

#define	Hearts		0
#define	Diamonds	1
#define	Clubs		2
#define	Spades		3

#define	Ace		1
#define	King		13

#define	Draw		52	/* Array index for draw window */

int ndeck;			/* Number of cards in deck - Added JB */

struct Card {
	Window          self;	/* window id */
	short           suit;	/* Hearts,Diamonds,Clubs,Spades */
	short           value;	/* Ace,2,3,4,5,6,7,8,9,10,Jack,Queen,King */
	short           x, y;	/* Origin coordinates */
	short           stack;	/* Indicates which stack the card is in */
	struct Card    *prev;	/* Points to previous stacked card */
	struct Card    *next;	/* Points to next stacked card */
	XImage          image;	/* windows image */
}               deck[Draw];
struct Card     card_window;

struct {			/* Locations for stacking card windows */
	short           x, y;
	short           count;
}               stack[12] = {
	59, 9, 52, 
	9, 79, 0, 
	59, 79, 0, 
	109, 79, 0,
	159, 79, 0, 
	209, 79, 0, 
	259, 79, 0, 
	309, 79, 0,
	129, 9, 0, 
	179, 9, 0, 
	229, 9, 0, 
	279, 9, 0
};

#define	CNULL	(struct Card *) 0

#define	TRUE	1
#define	FALSE	0

short           iscolor = False;
short           rflag = 42;

short           order[Draw][2];	/* Shuffling array */

XAssocTable    *Key;		/* Pointer to the association table for
				 * matching windows to data structures */

static XColor   foreground = {0L, 65535, 65535, 65535};
static XColor   bground = {0L, 0, 0, 0};

XColor          Red, Green, Black, White;
Window          Table, Restart, Exit;	/* Display window and button ids */
Window		Flip;			/* Added JB */
GC              gc, rgc, Redgc, out_gc;
XImage          res_image, exit_image;
XImage		flip_image;		/* Added JB */
Pixmap          background, draw_pixmap;
Pixmap		icon_pixmap;
Atom		protocol_atom, kill_atom;

Bool	have_card = False;
Bool	no_drag = False;

#define	Usage	{fprintf(stderr, "xsol: [-d display] [-r] [-nodrag]\n"); exit(0);}

Display        *dpy;
int             screen;

main(argc, argv)
	int             argc;	/* Number of arguments  */
	char           *argv[];	/* Array of pointers to arguments          */
{
	register int    i;
	Pixmap          cursor_bitmap;
	struct Card    *from = (struct Card *) 0,
		       *to = (struct Card *) 0;	/* User-selected cards */
	char           *display = NULL;
	XImage          timage;
	char           *progname;
	XSizeHints	hints;

	progname = argv[0];
	for (i = 1; i < argc; i++) {
		if (index(argv[i], ':'))
			display = argv[i];
		else if (strncmp(argv[i], "-d", 2) == 0)	{
			if (argv[++i])
				display = argv[i];
			else Usage;
		}
		else if (strncmp(argv[i], "-r", 2) == 0)
			rflag = 1;
		else if (strncmp(argv[i], "+r", 2) == 0)
			rflag = 0;
		else if (strncmp(argv[i], "-n", 2) == 0)
			no_drag = True;
		else
			Usage;
	}

	if (!(dpy = XOpenDisplay(display))) {
		fprintf(stderr, "%s: Can't open display.\n", progname);
		exit(1);
	}
	screen = DefaultScreen(dpy);

	gc = XCreateGC(dpy, RootWindow(dpy, screen), 0, NULL);
	rgc = XCreateGC(dpy, RootWindow(dpy, screen), 0, NULL);
	Redgc = XCreateGC(dpy, RootWindow(dpy, screen), 0, NULL);
	out_gc = XCreateGC(dpy, RootWindow(dpy, screen), 0, NULL);

	XSetForeground(dpy, gc, BlackPixel(dpy, screen));
	XSetForeground(dpy, rgc, WhitePixel(dpy, screen));

	XSetBackground(dpy, gc, WhitePixel(dpy, screen));
	XSetBackground(dpy, rgc, BlackPixel(dpy, screen));

	XSetForeground(dpy, out_gc, BlackPixel(dpy, screen));
	XSetFunction(dpy, out_gc, GXxor);
	XSetSubwindowMode(dpy, out_gc, IncludeInferiors);

	if (DisplayCells(dpy, screen) > 2)
		iscolor = TRUE;
	if (iscolor) {
		XAllocNamedColor(dpy, DefaultColormap(dpy, screen),
				 "red", &Red, &Red);
		XAllocNamedColor(dpy, DefaultColormap(dpy, screen),
				 "green", &Green, &Green);
		XAllocNamedColor(dpy, DefaultColormap(dpy, screen),
				 "black", &Black, &Black);
		XAllocNamedColor(dpy, DefaultColormap(dpy, screen),
				 "white", &White, &White);
		XSetForeground(dpy, Redgc, Red.pixel);
		XSetBackground(dpy, Redgc, White.pixel);
		if (rflag == 42)
			rflag = 0;
	}
	if (!iscolor && rflag == 42)
		rflag = 1;
		
	/* Fill in parent window parameters */

	if (!iscolor) {
		background = MakePixmap(dpy, screen, RootWindow(dpy, screen),
			    gray3_bits, gray3_width, gray3_height, &timage);
	}
	/* Store pixmaps for tiling areas */

	draw_pixmap = MakePixmap(dpy, screen, RootWindow(dpy, screen),
			       draw_bits, draw_width, draw_height, &timage);

	icon_pixmap = XCreatePixmap(dpy, RootWindow(dpy, screen),
		32, 32, DefaultDepth(dpy, screen));

	{
	XImage	temp;
	GC	redgc = (iscolor) ? Redgc : gc;
	GC	icon_gc, redicon_gc;

	icon_gc = XCreateGC(dpy, icon_pixmap, 0, NULL);
	redicon_gc = XCreateGC(dpy, icon_pixmap, 0, NULL);
	if (iscolor)	{
	    XSetForeground(dpy, icon_gc, Green.pixel);
	    XFillRectangle(dpy, icon_pixmap, icon_gc, 0, 0, 32, 32);
	    XSetForeground(dpy, redicon_gc, Red.pixel);
	    XSetBackground(dpy, redicon_gc, Green.pixel);
	    XSetForeground(dpy, icon_gc, BlackPixel(dpy, screen));
	    XSetBackground(dpy, icon_gc, Green.pixel);
	} else	{
	    XSetForeground(dpy, icon_gc, WhitePixel(dpy, screen));
	    XSetBackground(dpy, icon_gc, WhitePixel(dpy, screen));
	    XSetBackground(dpy, redicon_gc, WhitePixel(dpy, screen));
	    XFillRectangle(dpy, icon_pixmap, icon_gc, 0, 0, 32, 32);
	    XSetForeground(dpy, icon_gc, BlackPixel(dpy, screen));
	    XSetForeground(dpy, redicon_gc, BlackPixel(dpy, screen));
	}

	MakeImage(spade_width, spade_height, spade_bits, &temp);
	XPutImage(dpy, icon_pixmap, icon_gc, &temp, 0, 0, 2, 2, 
		spade_width, spade_height);
	MakeImage(heart_width, heart_height, heart_bits, &temp);
	XPutImage(dpy, icon_pixmap, redicon_gc, &temp, 0, 0, 17, 2, 
		heart_width, heart_height);
	MakeImage(club_width, club_height, club_bits, &temp);
	XPutImage(dpy, icon_pixmap, icon_gc, &temp, 0, 0, 17, 17, 
		club_width, club_height);
	MakeImage(diamond_width, diamond_height, diamond_bits, &temp);
	XPutImage(dpy, icon_pixmap, redicon_gc, &temp, 0, 0, 2, 17, 
		diamond_width, diamond_height);
	XFreeGC(dpy, icon_gc);
	XFreeGC(dpy, redicon_gc);
	}

	/* Try to create the display window and icon window */

	Table = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 100, 100,
		 Defwidth, Defheight, Border_width, BlackPixel(dpy, screen),
				    BlackPixel(dpy, screen));
	if (iscolor)
		XSetWindowBackground(dpy, Table, Green.pixel);
	else
		XSetWindowBackgroundPixmap(dpy, Table, background);

	protocol_atom = XInternAtom(dpy, "WM_PROTOCOLS", False);
	kill_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
	XSetWMProtocols(dpy, Table, &kill_atom, 1);

	if (!Table) {
		fprintf(stderr, "%s: can't create display window.\n", progname);
		exit(1);
	}
	
	hints.flags = PSize | PMinSize | PMaxSize;
	/* doesn't like to be resized */
	hints.width = hints.min_width = hints.max_width = Defwidth;
	hints.height = hints.min_height = hints.max_height = Defheight;

	XSetStandardProperties(dpy, Table, "Xsol", "Xsol", icon_pixmap,
		argv, argc, &hints);

	/* Create windows for Restart and Exit buttons */

	Restart = XCreateSimpleWindow(dpy, Table, 0, Defheight - restart_height,
		restart_width, restart_height, 1, BlackPixel(dpy, screen), 
		WhitePixel(dpy, screen));
	MakeImage(restart_width, restart_height, restart_bits, &res_image);

	Exit = XCreateSimpleWindow(dpy, Table, Defwidth - exit_width,
		Defheight - exit_height, exit_width, exit_height,
		1, BlackPixel(dpy, screen), WhitePixel(dpy, screen));
	MakeImage(exit_width, exit_height, exit_bits, &exit_image);

/* ++	Create window for Flip button - Added by Jon Brinkmann 7/20/90 */

	Flip = XCreateSimpleWindow(dpy, Table, (Defwidth - flip_width) / 2,
		Defheight - flip_height, flip_width, flip_height, 1,
		BlackPixel(dpy, screen), WhitePixel(dpy, screen));
	MakeImage(flip_width, flip_height, flip_bits, &flip_image);
	if (!(Restart && Flip && Exit)) {
/* -- */
/*	if (!(Restart && Exit)) { */
		fprintf(stderr, "%s: can't create button subwindows.\n", 
			progname);
		exit(1);
	}

	/* Associate the cursor with the display window */
	cursor_bitmap = XCreateBitmapFromData(dpy, RootWindow(dpy, screen),
		cursor_bits, cursor_width, cursor_height);

	XDefineCursor(dpy, Table, XCreatePixmapCursor(dpy, cursor_bitmap,
		cursor_bitmap, &bground, &foreground, 0, 0));

	/* Initialize subwindow structures and create card subwindows */

	if (!InitCardWindows(Table)) {
		fprintf(stderr, "%s: Can't create subwindows.\n", progname);
		exit(1);
	}
	/* Tell the X-window server which events we're interested in */

	XSelectInput(dpy, card_window.self, ButtonPressMask);
	/* table sucks buttons so they don't slide through to parent
	 * when player slips off card */
	XSelectInput(dpy, Table, ExposureMask | ButtonPressMask |
		((no_drag) ? PointerMotionMask : 0));
	XSelectInput(dpy, Restart, ExposureMask | ButtonPressMask);
	XSelectInput(dpy, Flip, ExposureMask | ButtonPressMask); /* Added JB */
	XSelectInput(dpy, Exit, ExposureMask | ButtonPressMask);

	DealCards();		/* Shuffle cards and deal to playing field */

	XMapWindow(dpy, Table);
	XMapWindow(dpy, Restart);
	XMapWindow(dpy, Flip);	/* Added JB */
	XMapWindow(dpy, Exit);
	XMapWindow(dpy, card_window.self);

	while (1) {		/* Process events */
		XEvent          event;
		int	oldx, oldy;
		int	mx, my;
		int	num_cards;
		struct Card	*t;

		XNextEvent(dpy, &event);

		switch (event.type) {

		case ClientMessage:
		    {
		    XClientMessageEvent	*ev = (XClientMessageEvent *)&event;

		    if (ev->message_type == protocol_atom && 
			ev->data.l[0] == kill_atom)
			exit(0);
		    }
		    break;
		case Expose:
			DisplayCard(event.xexpose.window);
			break;
		case ButtonPress:
		    if (!have_card)	{
			SelectCard(event.xbutton.window, &from);
			if (from)	{
				/* count cards */
				for (num_cards = 0, t = from; t->next; 
				    t = t->next, num_cards++)
					;
				oldx = oldy = -1;
				mx = event.xbutton.x;
				my = event.xbutton.y;
				}
			}
		    else	{
			if (from != CNULL)	/* only w/ no_drag */
				/* erase old outline */
				draw_outline(oldx, oldy, num_cards);
				have_card = False;
				MoveCard(event.xbutton.window, 
					&from, &to);
				}
			break;
		case MotionNotify:
			if (have_card)	{
				draw_outline(oldx, oldy, num_cards);
				oldx = ((XMotionEvent *) &(event))->x_root - mx;
				oldy = ((XMotionEvent *) &(event))->y_root - my;
				draw_outline(oldx, oldy, num_cards);
				}
			break;
		case ButtonRelease:	/* only w/o nodrag */
			if (from != CNULL)	{
				/* erase old outline */
				draw_outline(oldx, oldy, num_cards);
				have_card = False;
				MoveCard(event.xbutton.window, &from, &to);
				}
			break;
		}
	}
}

InitCardWindows(Table)		/* Fill in subwindow structures */
	Window          Table;

{
	register int    i;
	XAssocTable    *XCreateAssocTable();
	XSetWindowAttributes watt;
	Pixmap	pmap;

	/* Initialize OpaqueFrame structures and shuffling array */

	watt.backing_store = Always;
	for (i = 0; i < Draw; i++) {
		deck[i].x = stack[0].x;
		deck[i].y = stack[0].y;
		deck[i].stack = 0;
		deck[i].next = deck[i].prev = CNULL;
		deck[i].self = XCreateSimpleWindow(dpy, Table, 
			deck[i].x, deck[i].y, card_width, card_height, 
			0, (Pixmap) 0, WhitePixel(dpy, screen));
		XSelectInput(dpy, deck[i].self,
			ExposureMask | ButtonPressMask |
			((no_drag) ? 0 :
			(ButtonReleaseMask | OwnerGrabButtonMask 
			| Button1MotionMask)));
		XChangeWindowAttributes(dpy, deck[i].self, 
			CWBackingStore, &watt);
		MakeImage(card_width, card_height, card_bits[i],
			&(deck[i].image));
	}
	card_window.x = card_window.y = 9;
	card_window.self = XCreateSimpleWindow(dpy, Table, 
		card_window.x, card_window.y, 
		(card_width - 4), (card_height - 4), 
		2, BlackPixel(dpy, 0), (Pixmap) 0);
	XSetWindowBackgroundPixmap(dpy, card_window.self, draw_pixmap);

	/* Make a table for associating window ids to Card structures */

	if (!(Key = XCreateAssocTable(16)))	/* 16-bucket hash table */
		return (0);

	for (i = 0; i < Draw; i++) {

		/* Associate address of card with card's window id */
		XMakeAssoc(dpy, Key, deck[i].self, &deck[i]);
	}

	/* Initialize the cards to their suits and values */

	for (i = 0; i < 13; i++) {
		deck[i].suit = Hearts;
		deck[i].value = i + 1;
	}
	for (; i < 26; i++) {
		deck[i].suit = Diamonds;
		deck[i].value = i - 12;
	}
	for (; i < 39; i++) {
		deck[i].suit = Clubs;
		deck[i].value = i - 25;
	}
	for (; i < Draw; i++) {
		deck[i].suit = Spades;
		deck[i].value = i - 38;
	}

	srandom((int) time(0));	/* Seed the random number generator with the
				 * system time */

	return (1);
}

DealCards()
{
	register int    i, j, k;
	int             compare();

	for (i = 0; i < Draw; i++) {
		order[i][0] = i;	/* Moved here from InitCardWindows JB */
		order[i][1] = random();	/* Fill the shuffling array with
					 * random numbers */
	}

	qsort((char *) order, Draw, 4, compare);	/* Shuffle by sorting the
						 * random numbers */

	/* Deal the cards to the field stacks */

	ndeck = Draw;			/* Added JB */

	for (i = 1; i < 8; i++) {
		for (j = i; j < 8; j++) {
			/* Get index of next card in deck */
			k = order[--stack[0].count][0];	
			ndeck--;				/* Added JB */
			/* Move it to its new stack */
			XMoveWindow(dpy, deck[k].self,
				stack[j].x, stack[j].y);
			if (j == i) /* Make it visible if its on the top */
				XMapRaised(dpy, deck[k].self);	
			deck[k].x = stack[j].x;	/* Update locator fields */
			deck[k].y = stack[j].y;
			deck[k].stack = j;	/* Update stack number */
			++stack[j].count;	/* Update card count in stack */
		}
	}

	/* map later, so we don't give any hints about what's in each stack */
	for (i = 0; i < Draw; i++)	{
		/* don't map cards in draw deck */
/*		if (deck[i].y != stack[0].y)	*/
		if (deck[i].stack != 0)
			XMapWindow(dpy, deck[i].self);
		}
	/* map first draw */
	XMapRaised(dpy, deck[order[--stack[0].count][0]].self);
}

compare(arg1, arg2)		/* Compare low-order word only, for use in
				 * qsort */
	long           *arg1, *arg2;

{
#if defined(vax) || defined(i386)
	return ((*arg2 & 0xffff0000) - (*arg1 & 0xffff0000));
#else
	return ((*arg2 & 0xffff) - (*arg1 & 0xffff));
#endif
}

DisplayCard(window)		/* Service ExposeWindow events */
	Window          window;

{
	if (window == Restart)
		XPutImage(dpy, Restart, gc, &res_image, 0, 0, 0, 0,
			  restart_width, restart_height);
/* ++	Added by Jon Brinkmann 7/20/90 */
	else if (window == Flip)
		XPutImage(dpy, Flip, gc, &flip_image, 0, 0, 0, 0,
			  flip_width, flip_height);
/* -- */
	else if (window == Exit)
		XPutImage(dpy, Exit, gc, &exit_image, 0, 0, 0, 0, 
			exit_width, exit_height);
	else if (window == Table)
		;		/* noop (for now) */
	else {
		struct Card    *c;

		/* Find the card structure that corresponds to the window */

		c = (struct Card *) XLookUpAssoc(dpy, Key, window);

		/* Fill the card window with its bitmap */

		if (iscolor)
		    if (rflag)
			XPutImage(dpy, window, (c->suit < 2) ? Redgc : rgc, 
				&(c->image), 0, 0, 0, 0, 
				card_width, card_height);
		    else
			XPutImage(dpy, window, (c->suit < 2) ? Redgc : gc, 
				&(c->image), 0, 0, 0, 0, 
				card_width, card_height);
		else if (rflag)
			XPutImage(dpy, window, (c->suit < 2) ? gc : rgc, 
				&(c->image), 0, 0, 0, 0, 
				card_width, card_height);
		else
			XPutImage(dpy, window, gc, &(c->image), 
				0, 0, 0, 0, card_width, card_height);
	}
	return;
}

SelectCard(window, pfrom)	/* Service ButtonPressed events */
	Window          window;
	struct Card   **pfrom;

{
	if (window == Restart)	{
		NewGame();
		}
/* ++	Added by Jon Brinkmann 7/20/90 */
	else if (window == Flip)	{
		if (stack[0].count <= 0)	{
			if (ndeck > 1) XMapWindow(dpy, card_window.self); /*	Remove deck */
			stack[0].count = ndeck;
			while (stack[0].count)
				XUnmapWindow(dpy, deck[order[--stack[0].count][0]].self);
			stack[0].count = ndeck;
/*	Display top card */
			XMapRaised(dpy, deck[order[--stack[0].count][0]].self);
			return;
		} else	XBell(dpy, 50);
	}
/* -- */
	else if (window == Table)
		*pfrom = (struct Card *) 0;
	else if (window == Exit)
		exit(0);
	else if (window == card_window.self) {  /* Draw next card, if any */
		if (stack[0].count)     {
			XMapRaised(dpy, deck[order[--stack[0].count][0]].self);
			if (stack[0].count == 0)        {
				/* pull draw stack card */
				XUnmapWindow(dpy, card_window.self);
				}
			}
		else
			/* should never happen */
			XBell(dpy, 50);
	} else {		/* Select an exposed card */
		register int    i;
		struct Card    *from, *to;

		from = *pfrom = (struct Card *) XLookUpAssoc(dpy, Key, window);

		if (from->stack > 7) {	/* Can't move a card from the home
					 * stacks */
			XBell(dpy, 50);
			*pfrom = NULL;
			return;
		} else {	/* Aces and Kings are moved automatically
				 * upon selection; others are moved at
				 * ButtonReleased event */

			if (from->value == Ace) {/* Move Ace to home area */
				/* Skip non-vacant stacks */
				for (i = 8; stack[i].count; i++)
					;

				/*
				 * Reset card location fields and stack
				 * counters
				 */

				updateOrder(from);	/* Added JB */

				if (from->stack)
					--stack[from->stack].count;
				++stack[i].count;
				from->stack = i;
				from->x = stack[i].x;
				from->y = stack[i].y;

				/*
				 * Move the card window, and signal that
				 * we've done it
				 */

				XRaiseWindow(dpy, window);
				XMoveWindow(dpy, window, from->x, from->y);
				*pfrom = NULL;
				return;
			} else if (from->value == King) {	
			/* Move King to the first vacant column */

				if (from->stack) {
					if (stack[from->stack].count == 1)
						return;
				}
				/* Look for a vacancy */
				for (i = 1; ((i < 8) && stack[i].count); i++)
					;
				if (i < 8) {

					/*
					 * Reset card location fields and
					 * stack counters
					 */

					updateOrder(from);	/* Added JB */

					if (from->stack)
						--stack[from->stack].count;
					++stack[i].count;
					from->stack = i;
					from->x = stack[i].x;
					from->y = stack[i].y;

					/*
					 * Move the card window, and any
					 * cards stacked upon it
					 */

					XRaiseWindow(dpy, window);
					XMoveWindow(dpy, window, 
						from->x, from->y);

					if (from->next != CNULL) {
						to = from;
						from = from->next;
						Restack(&from, &to);
					}
					*pfrom = NULL;
					return;
				}
			}
		else
			have_card = True;
		}
	}
}

MoveCard(window, pfrom, pto)
	Window		window;
	struct Card   **pfrom, **pto;

{
	struct Card    *from, *to;	/* Locals to make syntax less funky */

	from = *pfrom;
	to = *pto = (struct Card *) XLookUpAssoc(dpy, Key, window);

	if (to == NULL)
		return;

	if (to->next == CNULL) {/* Can't move here unless it's top card */
		if (to->stack > 7) {	/* Try to move card to home area */
			if ((from->next == CNULL) && (to->suit == from->suit)
				&& (to->value == (from->value - 1))) {

				updateOrder(from);	/* Added JB */

				XRaiseWindow(dpy, from->self);
				XMoveWindow(dpy, from->self, to->x, to->y);
				/* Fix underlying card's forward pointer */
				if (from->prev != CNULL)
					(from->prev)->next = CNULL;	
				from->prev = to;	/* Fix back pointer */
				from->x = to->x;
				from->y = to->y;
				if (from->stack)
					--stack[from->stack].count;
				++stack[to->stack].count;
				from->stack = to->stack;

				return;
			}
		} else if (to->stack) {	/* Try to restack the card */
			if ((to->value == (from->value + 1)) &&
			    (((to->suit < 2) && (from->suit > 1)) ||
			     ((to->suit > 1) && (from->suit < 2)))) {

				updateOrder(from);	/* Added JB */

				to->next = from;
				/* Fix underlying card's forward pointer */
				if (from->prev != CNULL)
					(from->prev)->next = CNULL;	
				from->prev = to;	/* Fix back pointer */
				Restack(&from, &to);	/* Move this card and
							 * any stacked on top of
							 * it */
				return;
			}
		}
	}
	XBell(dpy, 50);		/* Illegal move */
	return;
}

Restack(pfrom, pto)		/* Recursive function to restack cards */
	struct Card   **pfrom, **pto;

{
	struct Card    *from, *to;

	from = *pfrom;
	to = *pto;
	from->x = to->x;
	from->y = to->y + Offset;
	XMoveWindow(dpy, from->self, from->x, from->y);
	XRaiseWindow(dpy, from->self);
	if (from->stack)
		--stack[from->stack].count;
	++stack[to->stack].count;
	from->stack = to->stack;
	if (from->next != CNULL) {
		to = from;
		from = from->next;
		Restack(&from, &to);
	}
	return;
}

/* ++	Added by Jon Brinkmann 7/20/90 */
#define	CardIndex(card)	13 * card->suit + card->value - 1

#define	Suite(index)	(index) / 13 + 1
#define	Value(index)	(index) % 13 + 1
static char *suite[4] = {"Hearts", "Diamonds", "Clubs", "Spades"};
static char *value[13]= {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10",
	"Jack", "Queen", "King"};
#define	Ssuite(index)	suite[(index) / 13]
#define	Svalue(index)	value[(index) % 13]

updateOrder(card)
	struct Card   *card;
{
	register int i, j;

	if (card->stack == 0) {
		j = CardIndex(card);
		for (i = stack[0].count; i < ndeck; i++) {
			if (j == order[i][0]) {
/*				printf ("%d %d Card %d %d (%s of %s) taken\n",
					stack[0].count, ndeck, i, j,
					Svalue(j), Ssuite(j));	*/
				for (j = i; j < ndeck; j++)
					order[j][0] = order[j + 1][0];
				ndeck--;
/*				for (j = i; j < ndeck; j++)
					printf ("%2d %2d (%s of %s)\n", j, order[j][0],
						Svalue(order[j][0]), Ssuite(order[j][0]));	*/
				return;
			}
		}	
		fprintf (stderr, "Can't find card!\n");
		exit(1);
	}
	return;
}
/* --	*/
NewGame()
{				/* Re-initialize the card deck, shuffle and
				 * deal */
	register int    i;

	XUnmapSubwindows(dpy, Table);
	XMapRaised(dpy, card_window.self);
	XMapRaised(dpy, Restart);
	XMapRaised(dpy, Flip);	/* Added JB */
	XMapRaised(dpy, Exit);

	/* Relocate all cards to the draw stack */

	for (i = 0; i < Draw; i++) {
		deck[i].x = stack[0].x;
		deck[i].y = stack[0].y;
		deck[i].stack = 0;
		deck[i].prev = deck[i].next = CNULL;
		XMoveWindow(dpy, deck[i].self, stack[0].x, stack[0].y);
	}

	stack[0].count = Draw;
	for (i = 1; i < 12; i++)
		stack[i].count = 0;

	DealCards();

	return;
}

draw_outline(x, y, n)
int	x, y, n;
{
	if ((x == -1) && (y == -1))
		return;
	XDrawRectangle(dpy, RootWindow(dpy, screen), out_gc, x, y, 
		card_width, card_height + n * 25);
}
