/*****************************************************************
Copyright 1992 by Silicon Graphics Incorporated, Mountain View, CA,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        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 SGI or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

SGI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
SGI 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.

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

/*
 * xawclient.c - X11/Xaw client interface to hearts
 * 
 * Author:	Mike Yang (mikey@sgi.com)
 *		Silicon Graphics, Inc.
 * Date:	Fri Jan 3 1992
 * Copyright (c) 1992 Mike Yang
 */

#include <stdlib.h>
#include <malloc.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "misc.h"
#include "defs.h"
#include "local.h"
#include "client.h"
#include "gfx.h"
#ifdef SYSV
#include <sys/termio.h>
#endif
#include <X11/cursorfont.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Text.h>
#include <X11/Xaw/Simple.h>

#ifdef X11R4
#define XawChainTop XtChainTop
#define XawChainBottom XtChainBottom
#define XawChainLeft XtChainLeft
#define XawChainRight XtChainRight
#endif

#define NUM_TABLES_ROWS	2
#define NUM_TABLES_COLS	4
#define NUM_TABLES	(NUM_TABLES_ROWS*NUM_TABLES_COLS)

static int count;
static Arg args[20];

typedef struct _CardInfo {
  int rank;
  int suit;
  struct _CardInfo *next;
} CardInfo;

typedef struct {
  CardInfo *head;
} SuitInfo;

SuitInfo my_hand[MAX_SUIT+1];

XtAppContext appContext;
Widget toplevel, form, displayFrame, displayArea, playArea, scoreArea;
Widget messageLabel, messageArea, messageEntry;
Widget commandArea, showButton = NULL, autoButton = NULL;
Widget quitButton, helpButton;
Widget displayLabels, roundLabel, leadLabel, textArea, playLabelArea;
Widget scorePlayer, scoreScore, scoreTotal;
Widget playerLabels[5], scoreLabels[5], totalLabels[5];
Widget playLabels[4];
Widget optionForm, helpDialog = NULL, playForm, optionHeader;
Widget gameBox, anotherGame, newGame, moreGames, quitGame, helpGame;
Widget gamesHeader, gamesFrame, gamesRC, gamesMessage;
Boolean showOld, *anotherPtr;
CardInfo playCards[4], lastCards[4];
char playNames[4][256], lastNames[4][256];
XtInputId distId = None, dealerId = None;
Boolean readingCard;
Cursor cardCursor = None;
Dimension globalTableHeight;

resizeWidget(w, width, height)
Widget w;
Dimension width, height;
{
  Dimension bw;
  Position x, y;

  count = 0;
  if (!width) {
    XtSetArg(args[count], XtNwidth, &width);  count++;
  }
  if (!height) {
    XtSetArg(args[count], XtNheight, &height);  count++;
  }
  XtSetArg(args[count], XtNborderWidth, &bw);  count++;
  XtSetArg(args[count], XtNx, &x);  count++;
  XtSetArg(args[count], XtNy, &y);  count++;
  XtGetValues(w, args, count);
  count = 0;
  XtSetArg(args[count], XtNwidth, width);  count++;
  XtSetArg(args[count], XtNheight, height);  count++;
  XtSetValues(w, args, count);
  XtConfigureWidget(w, x, y, width, height, bw);
}

updateLabel(w)
Widget w;
{
  XEvent event;

  while (XCheckTypedWindowEvent(XtDisplay(w), XtWindow(w), Expose, &event)) {
    XtDispatchEvent(&event);
  }
}

updateDisplay()
{
  int i;

  for (i=0; i<4; i++) {
    updateLabel(playLabels[i]);
  }
  updateLabel(roundLabel);
  updateLabel(leadLabel);
}

Rank
convertRank(rank)
int rank;
{
  if (rank == 13) {
    return Ace;
  } else {
    return rank;
  }
}

Suit
convertSuit(suit)
int suit;
{
  Suit s;

  switch (suit) {
  case SPADES:
    s = Spade;
    break;
  case HEARTS:
    s = Heart;
    break;
  case DIAMONDS:
    s = Diamond;
    break;
  case CLUBS:
    s = Club;
    break;
  }
  return s;
}

void
showPlayArea(cards, names)
CardInfo *cards;
char names[4][256];
{
  int i;

  for (i=0; i<4; i++) {
    if (cards[i].rank) {
      paint_card(XtWindow(playArea), i*STACK_WIDTH, 0,
		 convertRank(cards[i].rank), convertSuit(cards[i].suit), 0);
    } else {
      XClearArea(XtDisplay(playArea), XtWindow(playArea), i*STACK_WIDTH, 0,
		 STACK_WIDTH, 0, False);
    }
    count = 0;
    if (cards[i].rank) {
      XtSetArg(args[count], XtNlabel, names[i]);  count++;
    } else {
      XtSetArg(args[count], XtNlabel, " ");  count++;
    }
    XtSetValues(playLabels[i], args, count);
  }
}

void
setShowOld(v)
Boolean v;
{
  if (showOld != v) {
    showOld = v;
    if (showOld) {
      showPlayArea(lastCards, lastNames);
    } else {
      showPlayArea(playCards, playNames);
    }
  }
}

void
setReadingCard(v)
Boolean v;
{
  if (readingCard != v) {
    readingCard = v;
    if (cardCursor == None) {
      cardCursor = XCreateFontCursor(XtDisplay(toplevel), XC_hand1);
    }
    if (XtIsRealized(displayArea)) {
      if (readingCard) {
	XDefineCursor(XtDisplay(displayArea), XtWindow(displayArea),
		      cardCursor);
      } else {
	XUndefineCursor(XtDisplay(displayArea), XtWindow(displayArea));
      }
      XFlush(XtDisplay(displayArea));
    }
    if (!readingCard && showOld) {
      setShowOld(False);
    }
  }
}

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

void
exposeDisplayArea(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  Suit suit;

  for (suit=CLUBS; suit<=SPADES; suit++) {
    print_cards(suit);
  }
}

void
displaySelect(w, client_data, event)
Widget w;
XtPointer client_data;
XEvent *event;
{
  int suit, card, count;
  CardInfo *ptr, *cardSelected;
  char rch, sch;
  static char rnames[] = " 23456789TJQKA";

  cardSelected = NULL;
  if (readingCard &&
      event->type == ButtonPress && event->xbutton.button == Button1) {
    suit = (event->xbutton.x/STACK_WIDTH)+1;
    card = (event->xbutton.y/CARD_DELTA)+1;
    if (event->xbutton.x % STACK_WIDTH > CARD_WIDTH) {
      /* clicked on space between stacks */
    } else if (suit > 4) {
      /* clicked outside stacks */
    } else {
      ptr = my_hand[suit].head;
      count = 0;
      while (count < card && ptr->next) {
	count++;
	ptr = ptr->next;
      }
      if (count == card) {
	cardSelected = ptr;
      } else if (count < card &&
		 event->xbutton.y-(count-1)*CARD_DELTA < CARD_HEIGHT) {
	/* clicked on last card */
	cardSelected = ptr;
      } else {
	/* clicked below stack */
      }
      if (!ptr->rank) {
	cardSelected = NULL;
      }
    }
  }
  if (cardSelected) {
    rch = rnames[cardSelected->rank];
    switch (cardSelected->suit) {
    case SPADES:
      sch = 's';
      break;
    case HEARTS:
      sch = 'h';
      break;
    case DIAMONDS:
      sch = 'd';
      break;
    case CLUBS:
      sch = 'c';
      break;
    }
    send_card(rch, sch);
    setReadingCard(False);
    erase_window(TEXT_WINDOW);
  } else {
    XBell(XtDisplay(toplevel), 0);
  }
}

void
show_button(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  if (readingCard) {
    setShowOld(!showOld);
  }
}

void
auto_play(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  if (readingCard) {
    send_auto();
    setReadingCard(False);
    erase_window(TEXT_WINDOW);
  }
}

void
quit_game(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  wimp_out();
}

void
help_away(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  XtPopdown(helpDialog);
}

void
help_me(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  Widget form, text, okButton, label;
  char filename[256];
  int fildes;
  struct stat buf;
  char *data;

  if (!helpDialog) {
    count = 0;
    helpDialog = XtCreatePopupShell("xawhearts help", topLevelShellWidgetClass,
				    toplevel, args, count);
    count = 0;
    XtSetArg(args[count], XtNborderWidth, 0);  count++;
    form = XtCreateManagedWidget("helpForm", formWidgetClass, helpDialog,
				 args, count);

    count = 0;
    XtSetArg(args[count], XtNscrollVertical, True);  count++;
    XtSetArg(args[count], XtNheight, 200);  count++;
    XtSetArg(args[count], XtNwidth, 700);  count++;
    text = XtCreateManagedWidget("helpText", asciiTextWidgetClass, form,
				 args, count);

    count = 0;
    XtSetArg(args[count], XtNborderWidth, 0);  count++;
    XtSetArg(args[count], XtNfromVert, text);  count++;
    XtSetArg(args[count], XtNvertDistance, 10);  count++;
    XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
    XtSetArg(args[count], XtNlabel, "Xawhearts, an Xaw interface to hearts.  Mike Yang, Silicon Graphics, mikey@sgi.com");  count++;
    label = XtCreateManagedWidget("label", labelWidgetClass, form,
				  args, count);

    count = 0;
    XtSetArg(args[count], XtNfromVert, text);  count++;
    XtSetArg(args[count], XtNvertDistance, 10);  count++;
    XtSetArg(args[count], XtNfromHoriz, label);  count++;
    XtSetArg(args[count], XtNhorizDistance, 10);  count++;
    XtSetArg(args[count], XtNleft, XawChainRight);  count++;
    XtSetArg(args[count], XtNright, XawChainRight);  count++;
    XtSetArg(args[count], XtNtop, XawChainBottom);  count++;
    XtSetArg(args[count], XtNbottom, XawChainBottom);  count++;
    XtSetArg(args[count], XtNlabel, "OK");  count++;
    okButton = XtCreateManagedWidget("helpOK", commandWidgetClass, form,
				     args, count);
    XtAddCallback(okButton, XtNcallback, help_away, NULL);

    sprintf(filename, "%s/%s", HEARTSLIB, INSTRUCT);
    if ((fildes = open(filename, O_RDONLY)) != -1) {
      fstat(fildes, &buf);
      data = (char *) malloc((size_t) buf.st_size+1);
      read(fildes, data, (unsigned) buf.st_size);
      data[buf.st_size] = '\0';
      close(fildes);
      count = 0;
      XtSetArg(args[count], XtNstring, data);  count++;
      XtSetValues(text, args, count);
      free(data);
    } else {
      count = 0;
      XtSetArg(args[count], XtNstring, "Help file missing.\n");  count++;
      XtSetValues(text, args, count);
    }
  }
  XtPopup(helpDialog, XtGrabNone);
}

void
exposePlayArea(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  if (showOld) {
    showPlayArea(lastCards, lastNames);
  } else {
    showPlayArea(playCards, playNames);
  }
}

void
another_game(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  *anotherPtr = TRUE;
  if (!first_game) {
    joined = TRUE;
  }
}

void
new_game(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  start_new_game();
  joined = TRUE;
}

void
more_games(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
  if (table_count > NUM_TABLES) {
    if ((cur_screen_table += NUM_TABLES) > table_count) {
      cur_screen_table = 1;
    }
    show_tables(cur_screen_table);
  }
}

void
tableSelect(w, client_data, event)
Widget w;
XtPointer client_data;
XEvent *event;
{
  int i;
  Widget *widgets;
  table_ptr cur_ptr;

  if (table_count && first_game &&
      event->type == ButtonPress && event->xbutton.button == Button1) {
    cur_ptr = first_table;
    for (i=0; i<table_count; i++) {
      if (cur_ptr->data && !cur_ptr->closed) {
	widgets = (Widget *) cur_ptr->data;
	if (w == widgets[1]) {
	  join_game(cur_ptr->table_id);
	  joined = TRUE;
	  return;
	}
      }
      cur_ptr = cur_ptr->next_table;
    }
  } else if (XtIsSensitive(anotherGame)) {
    another_game(w, NULL, NULL);
  }
}

void
send_msg(w, event, params, num_params)
Widget w;
XEvent *event;
String *params;
Cardinal *num_params;
{
  char *msg;

  count = 0;
  XtSetArg(args[count], XtNstring, &msg);  count++;
  XtGetValues(messageEntry, args, count);
  send_message(msg);
  count = 0;
  XtSetArg(args[count], XtNstring, "");  count++;
  XtSetValues(messageEntry, args, count);
}

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

void
dealerInput(client_data, source, id)
XtPointer client_data;
int *source;
XtInputId *id;
{
  fd_type read_fd;
  int nready;
  struct timeval timeout;

  timeout.tv_sec = 0;
  timeout.tv_usec = 0;
  fd_init(dealer_socket, &read_fd);
  while (dealerId &&
	 (nready = select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0,
			  &timeout)) && nready > 0) {
    do_socket();
  }
}

void
distInput(client_data, source, id)
XtPointer client_data;
int *source;
XtInputId *id;
{
  fd_type read_fd;
  int nready;
  struct timeval timeout;

  timeout.tv_sec = 0;
  timeout.tv_usec = 0;
  fd_init(dist_socket, &read_fd);
  while (distId &&
	 (nready = select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0,
			  &timeout)) && nready > 0) {
    do_dist();
  }
}

Widget
buildScoreLabels(parent, header, array)
Widget parent;
char *header;
Widget *array;
{
  Widget rc, last;
  Dimension width;
  int each;

  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  rc = XtCreateManagedWidget("scoresColumn", formWidgetClass, parent,
			     args, count);
  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  if (array == playerLabels) {
    XtSetArg(args[count], XtNjustify, XtJustifyLeft);  count++;
  } else {
    XtSetArg(args[count], XtNjustify, XtJustifyRight);  count++;
  }
  XtSetArg(args[count], XtNlabel, header);  count++;
  array[0] = XtCreateManagedWidget("header", labelWidgetClass, rc,
				   args, count);
  count = 0;
  XtSetArg(args[count], XtNwidth, &width);  count++;
  XtGetValues(array[0], args, count);
  last = array[0];
  for (each=1; each<=4; each++) {
    count = 0;
    XtSetArg(args[count], XtNborderWidth, 0);  count++;
    if (array == playerLabels) {
      XtSetArg(args[count], XtNjustify, XtJustifyLeft);  count++;
    } else {
      XtSetArg(args[count], XtNjustify, XtJustifyRight);  count++;
    }
    XtSetArg(args[count], XtNlabel, " ");  count++; 
    XtSetArg(args[count], XtNfromVert, last);  count++;
    XtSetArg(args[count], XtNwidth, width);  count++;
    array[each] = XtCreateManagedWidget("label", labelWidgetClass, rc,
					args, count);
    last = array[each];
  }
  return rc;
}

void
buildScoreArea(parent)
Widget parent;
{
  Widget rc, p, s, t;

  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  rc = XtCreateManagedWidget("scoreArea", formWidgetClass, parent,
			     args, count);
  p = buildScoreLabels(rc, "Player:      ", playerLabels);
  s = buildScoreLabels(rc, "Score:", scoreLabels);
  count = 0;
  XtSetArg(args[count], XtNfromHoriz, p);  count++;
  XtSetValues(s, args, count);
  t = buildScoreLabels(rc, "Total:", totalLabels);
  count = 0;
  XtSetArg(args[count], XtNfromHoriz, s);  count++;
  XtSetValues(t, args, count);
}

void
buildCommandArea(parent)
Widget parent;
{
  Widget rc;

  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  rc = XtCreateManagedWidget("commandArea", formWidgetClass, parent,
			     args, count);
  count = 0;
  XtSetArg(args[count], XtNlabel, "Toggle Last");  count++;
  showButton = XtCreateManagedWidget("showButton", commandWidgetClass, rc,
				     args, count);
  XtAddCallback(showButton, XtNcallback, show_button, NULL);
  count = 0;
  XtSetArg(args[count], XtNlabel, "Auto Play");  count++;
  XtSetArg(args[count], XtNfromHoriz, showButton);  count++;
  autoButton = XtCreateManagedWidget("autoButton", commandWidgetClass, rc,
				     args, count);
  XtAddCallback(autoButton, XtNcallback, auto_play, NULL);
  count = 0;
  XtSetArg(args[count], XtNlabel, "Quit");  count++;
  XtSetArg(args[count], XtNfromHoriz, autoButton);  count++;
  quitButton = XtCreateManagedWidget("quitButton", commandWidgetClass, rc,
				     args, count);
  XtAddCallback(quitButton, XtNcallback, quit_game, NULL);
  count = 0;
  XtSetArg(args[count], XtNlabel, "Help");  count++;
  XtSetArg(args[count], XtNfromHoriz, quitButton);  count++;
  helpButton = XtCreateManagedWidget("helpButton", commandWidgetClass, rc,
				     args, count);
  XtAddCallback(helpButton, XtNcallback, help_me, NULL);
}

void
buildInterface(parent)
Widget parent;
{
  Widget form;

  static String textTranslations = "<Key>Return:doreturn()";
  static XtActionsRec actionTable[] = {
    {"doreturn", send_msg},
    {NULL, NULL},
  };

  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  form = XtCreateManagedWidget("form", formWidgetClass, parent, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  XtSetArg(args[count], XtNdefaultDistance, STACK_SPACING);  count++;
  displayFrame = XtCreateManagedWidget("displayFrame", formWidgetClass, form,
				       args, count);

  count = 0;
  XtSetArg(args[count], XtNvertDistance, 10);  count++;
  XtSetArg(args[count], XtNhorizDistance, 10);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNwidth, 4*STACK_WIDTH-STACK_SPACING+2);  count++;
  XtSetArg(args[count], XtNheight, 12*CARD_DELTA+CARD_HEIGHT+2);  count++;
  displayArea = XtCreateManagedWidget("displayArea", simpleWidgetClass,
				      displayFrame, args, count);
  XtAddEventHandler(displayArea, ExposureMask, False, exposeDisplayArea, NULL);
  XtAddEventHandler(displayArea, ButtonPressMask, False, displaySelect, NULL);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNfromVert, displayFrame);  count++;
  XtSetArg(args[count], XtNvertDistance, 10);  count++;
/* adjust for display frame margin */
  XtSetArg(args[count], XtNhorizDistance, 10);  count++;
  XtSetArg(args[count], XtNwidth, 4*STACK_WIDTH-STACK_SPACING+2);  count++;
  XtSetArg(args[count], XtNheight, CARD_HEIGHT+2);  count++;
  playArea = XtCreateManagedWidget("playArea", formWidgetClass,
				   form, args, count);
  XtAddEventHandler(playArea, ExposureMask, False, exposePlayArea, NULL);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNfromVert, playArea);  count++;
  XtSetArg(args[count], XtNvertDistance, 10);  count++;
/* adjust for display frame margin */
  XtSetArg(args[count], XtNhorizDistance, 10);  count++;
  playLabelArea = XtCreateManagedWidget("playLabelArea", formWidgetClass,
					form, args, count);

  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, " ");  count++;
  XtSetArg(args[count], XtNwidth, CARD_WIDTH);  count++;
  XtSetArg(args[count], XtNresize, False);  count++;
  playLabels[0] = XtCreateManagedWidget("playLabel", labelWidgetClass,
					playLabelArea, args, count);
  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, " ");  count++;
  XtSetArg(args[count], XtNwidth, CARD_WIDTH);  count++;
  XtSetArg(args[count], XtNfromHoriz, playLabels[0]);  count++;
  XtSetArg(args[count], XtNhorizDistance, STACK_SPACING);  count++;
  XtSetArg(args[count], XtNresize, False);  count++;
  playLabels[1] = XtCreateManagedWidget("playLabel", labelWidgetClass,
					playLabelArea, args, count);
  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, " ");  count++;
  XtSetArg(args[count], XtNwidth, CARD_WIDTH);  count++;
  XtSetArg(args[count], XtNfromHoriz, playLabels[1]);  count++;
  XtSetArg(args[count], XtNhorizDistance, STACK_SPACING);  count++;
  XtSetArg(args[count], XtNresize, False);  count++;
  playLabels[2] = XtCreateManagedWidget("playLabel", labelWidgetClass,
					playLabelArea, args, count);
  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, " ");  count++;
  XtSetArg(args[count], XtNwidth, CARD_WIDTH);  count++;
  XtSetArg(args[count], XtNfromHoriz, playLabels[2]);  count++;
  XtSetArg(args[count], XtNhorizDistance, STACK_SPACING);  count++;
  XtSetArg(args[count], XtNresize, False);  count++;
  playLabels[3] = XtCreateManagedWidget("playLabel", labelWidgetClass,
					playLabelArea, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainRight);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNfromHoriz, displayFrame);  count++;
  XtSetArg(args[count], XtNhorizDistance, 10);  count++;
  displayLabels = XtCreateManagedWidget("displayLabels", formWidgetClass,
					form, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainRight);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, "New hand...");  count++;
  XtSetArg(args[count], XtNjustify, XtJustifyLeft);  count++;
  XtSetArg(args[count], XtNwidth, 10);  count++;
  roundLabel = XtCreateManagedWidget("roundLabel", labelWidgetClass,
				     displayLabels, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainRight);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, "Round: 1");  count++;
  XtSetArg(args[count], XtNjustify, XtJustifyLeft);  count++;
  XtSetArg(args[count], XtNfromVert, roundLabel);  count++;
  XtSetArg(args[count], XtNwidth, 10);  count++;
  leadLabel = XtCreateManagedWidget("roundLabel", labelWidgetClass,
				    displayLabels, args, count);

  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainRight);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, " ");  count++;
  XtSetArg(args[count], XtNjustify, XtJustifyLeft);  count++;
  XtSetArg(args[count], XtNfromVert, leadLabel);  count++;
  XtSetArg(args[count], XtNwidth, 10);  count++;
  textArea = XtCreateManagedWidget("textArea", labelWidgetClass,
				    displayLabels, args, count);
  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  scoreArea = XtCreateManagedWidget("scoreArea", formWidgetClass,
				    form, args, count);
  XtManageChild(scoreArea);
  buildScoreArea(scoreArea);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, "Messages:");  count++;
  XtSetArg(args[count], XtNfromVert, scoreArea);  count++;
  XtSetArg(args[count], XtNvertDistance, 10);  count++;
  XtSetArg(args[count], XtNfromHoriz, displayFrame);  count++;
  XtSetArg(args[count], XtNhorizDistance, 10);  count++;
  XtSetArg(args[count], XtNjustify, XtJustifyLeft);  count++;
  messageLabel = XtCreateManagedWidget("messageLabel", labelWidgetClass,
				       form, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainRight);  count++; 
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainBottom);  count++;
  XtSetArg(args[count], XtNscrollVertical, True);  count++;
/* The height is important, so that the buttons line up with the bottom
   of the screen.  Unfortunately, I can't figure out how to do the
   form constraints to make this happen cleanly. */
  XtSetArg(args[count], XtNheight, 430);  count++;
  XtSetArg(args[count], XtNwidth, 500);  count++;
  XtSetArg(args[count], XtNfromVert, messageLabel);  count++;
  XtSetArg(args[count], XtNfromHoriz, displayFrame);  count++;
  XtSetArg(args[count], XtNhorizDistance, 10);  count++;
  messageArea = XtCreateManagedWidget("messageArea", asciiTextWidgetClass,
				      form, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainRight);  count++;
  XtSetArg(args[count], XtNtop, XawChainBottom);  count++;
  XtSetArg(args[count], XtNbottom, XawChainBottom);  count++;
  XtSetArg(args[count], XtNwidth, 500);  count++;
  XtSetArg(args[count], XtNfromVert, messageArea);  count++;
  XtSetArg(args[count], XtNfromHoriz, displayFrame);  count++;
  XtSetArg(args[count], XtNhorizDistance, 10);  count++;
  XtSetArg(args[count], XtNeditType, XawtextEdit);  count++;
  messageEntry = XtCreateManagedWidget("messageEntry", asciiTextWidgetClass,
				      form, args, count);
  XtOverrideTranslations(messageEntry,
			 XtParseTranslationTable(textTranslations));
  XtAppAddActions(XtWidgetToApplicationContext(messageEntry),
		  actionTable, XtNumber(actionTable));

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainBottom);  count++;
  XtSetArg(args[count], XtNbottom, XawChainBottom);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNfromVert, messageEntry);  count++;
  XtSetArg(args[count], XtNvertDistance, 10);  count++;
  XtSetArg(args[count], XtNfromHoriz, displayFrame);  count++;
  XtSetArg(args[count], XtNhorizDistance, 10);  count++;
  commandArea = XtCreateManagedWidget("commandArea", formWidgetClass,
				      form, args, count);
  buildCommandArea(commandArea);
}

void
buildOptionInterface(parent)
Widget parent;
{
  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  optionForm = XtCreateManagedWidget("optionForm", formWidgetClass,
				     parent, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNlabel, "Welcome to the Game of Hearts");  count++;
  XtSetArg(args[count], XtNjustify, XtJustifyCenter);  count++;
  XtSetArg(args[count], XtNresize, False);  count++;
  optionHeader = XtCreateManagedWidget("optionHeader", labelWidgetClass,
				       optionForm, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNfromVert, optionHeader);  count++;
  XtSetArg(args[count], XtNvertDistance, 20);  count++;
  XtSetArg(args[count], XtNjustify, XtJustifyCenter);  count++;
  if (MIKEYJ) {
    XtSetArg(args[count], XtNlabel,
	     "(the Jack of Diamonds scores -10)");  count++;
  } else {
    XtSetArg(args[count], XtNlabel, " ");  count++;
  }
  gamesMessage= XtCreateManagedWidget("gamesMessage", labelWidgetClass,
				      optionForm, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNfromVert, gamesMessage);  count++;
  XtSetArg(args[count], XtNvertDistance, 20);  count++;
  XtSetArg(args[count], XtNjustify, XtJustifyLeft);  count++;
  XtSetArg(args[count], XtNlabel, "Current Games:");  count++;
  gamesHeader = XtCreateManagedWidget("gamesHeader", labelWidgetClass,
				      optionForm, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainLeft);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainBottom);  count++;
  XtSetArg(args[count], XtNfromVert, gamesHeader);  count++;
  XtSetArg(args[count], XtNdefaultDistance, 0);  count++;
  gamesFrame = XtCreateManagedWidget("gamesFrame", formWidgetClass,
				     optionForm, args, count);

  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
  XtSetArg(args[count], XtNright, XawChainRight);  count++;
  XtSetArg(args[count], XtNtop, XawChainTop);  count++;
  XtSetArg(args[count], XtNbottom, XawChainBottom);  count++;
  XtSetArg(args[count], XtNdefaultDistance, 0);  count++;
  XtSetArg(args[count], XtNwidth, 10);  count++;
  XtSetArg(args[count], XtNheight, 10);  count++;
  gamesRC = XtCreateManagedWidget("gamesRC", formWidgetClass,
				  gamesFrame, args, count);

  count = 0;
  XtSetArg(args[count], XtNleft, XawChainRight);  count++;
  XtSetArg(args[count], XtNright, XawChainRight);  count++;
  XtSetArg(args[count], XtNtop, XawChainBottom);  count++;
  XtSetArg(args[count], XtNbottom, XawChainBottom);  count++;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  XtSetArg(args[count], XtNfromVert, gamesFrame);  count++;
  XtSetArg(args[count], XtNvertDistance, 10);  count++;
  gameBox = XtCreateManagedWidget("gameBox", formWidgetClass,
				  optionForm, args, count);

  count = 0;
  XtSetArg(args[count], XtNlabel, "Another Game");  count++;
  anotherGame = XtCreateManagedWidget("anotherGame", commandWidgetClass,
				      gameBox, args, count);
  XtAddCallback(anotherGame, XtNcallback, another_game, NULL);

  count = 0;
  XtSetArg(args[count], XtNlabel, "New Game");  count++;
  XtSetArg(args[count], XtNfromHoriz, anotherGame);  count++;
  newGame = XtCreateManagedWidget("newGame", commandWidgetClass,
				  gameBox, args, count);
  XtAddCallback(newGame, XtNcallback, new_game, NULL);

  count = 0;
  XtSetArg(args[count], XtNlabel, "More Games");  count++;
  XtSetArg(args[count], XtNfromHoriz, newGame);  count++;
  moreGames = XtCreateManagedWidget("moreGames", commandWidgetClass,
				    gameBox, args, count);
  XtAddCallback(moreGames, XtNcallback, more_games, NULL);

  count = 0;
  XtSetArg(args[count], XtNlabel, "Quit");  count++;
  XtSetArg(args[count], XtNfromHoriz, moreGames);  count++;
  quitGame = XtCreateManagedWidget("quitGame", commandWidgetClass,
				   gameBox, args, count);
  XtAddCallback(quitGame, XtNcallback, quit_game, NULL);

  count = 0;
  XtSetArg(args[count], XtNlabel, "Help");  count++;
  XtSetArg(args[count], XtNfromHoriz, quitGame);  count++;
  helpGame = XtCreateManagedWidget("helpGame", commandWidgetClass,
				   gameBox, args, count);
  XtAddCallback(helpGame, XtNcallback, help_me, NULL);

  playForm = form;
}

init(argc_p, argv)
int *argc_p;
char **argv;
{
  char ch, *pager, buffer[128];
  int suit;
  CardInfo *card;

  toplevel = XtInitialize(argv[0], "Xawhearts", NULL, 0, argc_p, argv);
  appContext = XtWidgetToApplicationContext(toplevel);

  count = 0;
  XtSetArg(args[count], XtNborderWidth, 0);  count++;
  form = XtCreateManagedWidget("form", formWidgetClass,
			       toplevel, args, count);

  buildInterface(form);
  buildOptionInterface(form);
  gfx_init(XtDisplay(toplevel), XScreenNumberOfScreen(XtScreen(toplevel)));

  XtRealizeWidget(toplevel);
  fixupLayout();
  XtAddEventHandler(form, StructureNotifyMask, False,
		    (XtEventHandler) fixupLayout, NULL);

  for (suit=CLUBS; suit<=SPADES; suit++) {
    card = (CardInfo *) malloc(sizeof(CardInfo));
    card->rank = card->suit = 0;
    card->next = NULL;
    my_hand[suit].head = card;
  }
  playCards[0].rank = 0;
  playCards[1].rank = 0;
  playCards[2].rank = 0;
  playCards[3].rank = 0;
  playNames[0][0] = '\0';
  playNames[1][0] = '\0';
  playNames[2][0] = '\0';
  playNames[3][0] = '\0';
  lastCards[0].rank = 0;
  lastCards[1].rank = 0;
  lastCards[2].rank = 0;
  lastCards[3].rank = 0;
  lastNames[0][0] = '\0';
  lastNames[1][0] = '\0';
  lastNames[2][0] = '\0';
  lastNames[3][0] = '\0';
  setReadingCard(False);
}

print_cards(suit)
int suit;
{
  CardInfo *ptr;
  int y;
  Rank r;
  Suit s;

  s = convertSuit(suit);
  ptr = my_hand[suit].head;
  y = 0;
  while (ptr->next) {
    ptr = ptr->next;
    r = convertRank(ptr->rank);
    paint_card(XtWindow(displayArea), (suit-1)*STACK_WIDTH, y, r, s,
	       ptr->next ? CARD_DELTA : 0);
    y += CARD_DELTA;
  }
  if (y) {
    XClearArea(XtDisplay(displayArea), XtWindow(displayArea),
	       (suit-1)*STACK_WIDTH, y+CARD_HEIGHT-CARD_DELTA+1 /* bw */,
	       STACK_WIDTH, 0, False);
  } else {
    XClearArea(XtDisplay(displayArea), XtWindow(displayArea),
	       (suit-1)*STACK_WIDTH, 0,
	       STACK_WIDTH, 0, False);
  }
}

void
displayMessage(msg)
char *msg;
{
  count = 0;
  XtSetArg(args[count], XtNlabel, msg);  count++;
  XtSetValues(textArea, args, count);
}


void
positionTables()
{
  int i, ct;
  table_ptr cur_ptr;
  Widget *widgets;
  Dimension width, height, tableHeight, tableWidth, spacing;
  Dimension marginWidth, marginHeight, bw;
  Position x, y;

  ct = 0;
  cur_ptr = first_table;
  /* make the gamesRC as large as the gamesFrame */
  count = 0;
  XtSetArg(args[count], XtNwidth, &width);  count++;
  XtSetArg(args[count], XtNheight, &height);  count++;
  XtGetValues(gamesFrame, args, count);
  resizeWidget(gamesRC, width, height);
  marginWidth = 10;
  marginHeight = 10;
  spacing = 10;
  for (i=0; i<table_count; i++) {
    if (cur_ptr->data) {
      widgets = (Widget *) cur_ptr->data;
      if (XtIsManaged(widgets[0])) {
	count = 0;
	XtSetArg(args[count], XtNheight, &tableHeight);  count++;
	XtSetArg(args[count], XtNborderWidth, &bw);  count++;
	XtGetValues(widgets[0], args, count);
	tableHeight = globalTableHeight;
	if (width >
	    2*marginWidth+NUM_TABLES_COLS*4+(NUM_TABLES_COLS-1)*spacing) {
	  tableWidth = (width-2*marginWidth-NUM_TABLES_COLS*4-
			(NUM_TABLES_COLS-1)*spacing)/NUM_TABLES_COLS;
	  y = marginHeight+tableHeight*(ct/NUM_TABLES_COLS);
	  x = marginWidth+(ct % NUM_TABLES_COLS)*(tableWidth+spacing);
	  XtConfigureWidget(widgets[0], x, y, tableWidth, tableHeight, bw);
	  XtConfigureWidget(widgets[1], 0, 0, tableWidth, tableHeight, 0);
	  ct++;
	}
      }
    }
    cur_ptr = cur_ptr->next_table;
  }

}

fixupLayout()
{
  Dimension formWidth, formHeight, width, height, bw;
  Position x, y;
  int hd, dd;

  /*****************************************************************/
  /* Grrr.  I hate Xaw.  The form widget is not expressive enough. */
  /* Or, I just can't figure it out.
  /*****************************************************************/

  /* make option form as big as play form */
  count = 0;
  XtSetArg(args[count], XtNwidth, &formWidth);  count++;
  XtSetArg(args[count], XtNheight, &formHeight);  count++;
  XtGetValues(playForm, args, count);
  resizeWidget(optionForm, formWidth-2, formHeight-2);

  /* make the labels and table container as wide as the option form */
  count = 0;
  XtSetArg(args[count], XtNwidth, &formWidth);  count++;
  XtSetArg(args[count], XtNdefaultDistance, &dd);  count++;
  XtGetValues(optionForm, args, count);
  count = 0;
  XtSetArg(args[count], XtNhorizDistance, &hd);  count++;
  XtGetValues(optionHeader, args, count);
  resizeWidget(optionHeader, formWidth-2*hd-2*dd, 0);
  resizeWidget(gamesMessage, formWidth-2*hd-2*dd, 0);
  resizeWidget(gamesHeader, formWidth-2*hd-2*dd, 0);
  count = 0;
  XtSetArg(args[count], XtNborderWidth, &bw);  count++;
  XtGetValues(gamesFrame, args, count);
  resizeWidget(gamesFrame, formWidth-2*hd-2*dd-2*bw, 0);

  /* Expand labels area, move score area to the right of the play form */
  count = 0;
  XtSetArg(args[count], XtNwidth, &formWidth);  count++;
  XtSetArg(args[count], XtNdefaultDistance, &dd);  count++;
  XtGetValues(playForm, args, count);
  count = 0;
  XtSetArg(args[count], XtNwidth, &width);  count++;
  XtSetArg(args[count], XtNheight, &height);  count++;
  XtSetArg(args[count], XtNx, &x);  count++;
  XtSetArg(args[count], XtNy, &y);  count++;
  XtSetArg(args[count], XtNborderWidth, &bw);  count++;
  XtGetValues(scoreArea, args, count);
/* I have no idea why I need this fudge factor to line things up... */
#define FUDGE_FACTOR 8
  count = 0;
  XtSetArg(args[count], XtNx, formWidth-width-dd-2*bw-FUDGE_FACTOR);  count++;
  XtSetValues(scoreArea, args, count);
  XtConfigureWidget(scoreArea, formWidth-width-dd-2*bw-FUDGE_FACTOR, y,
		    width, height, bw);
  count = 0;
  XtSetArg(args[count], XtNwidth, &width);  count++;
  XtGetValues(scoreArea, args, count);
  count = 0;
  XtSetArg(args[count], XtNx, &x);  count++;
  XtGetValues(displayLabels, args, count);
  if (x > formWidth-width-dd-2*bw-FUDGE_FACTOR) {
    resizeWidget(displayLabels, 1, 0);
  } else {
    resizeWidget(displayLabels, formWidth-width-dd-2*bw-x-FUDGE_FACTOR, 0);
  }

  /* move the play buttons to the lower right of the play form */
  count = 0;
  XtSetArg(args[count], XtNwidth, &formWidth);  count++;
  XtSetArg(args[count], XtNdefaultDistance, &dd);  count++;
  XtGetValues(playForm, args, count);
  count = 0;
  XtSetArg(args[count], XtNwidth, &width);  count++;
  XtSetArg(args[count], XtNheight, &height);  count++;
  XtSetArg(args[count], XtNx, &x);  count++;
  XtSetArg(args[count], XtNy, &y);  count++;
  XtSetArg(args[count], XtNborderWidth, &bw);  count++;
  XtGetValues(commandArea, args, count);
  count = 0;
  XtSetArg(args[count], XtNx, formWidth-width-dd-2*bw);  count++;
  XtSetValues(commandArea, args, count);
  XtConfigureWidget(commandArea, formWidth-width-dd-2*bw, y,
		    width, height, bw);

  positionTables();
}

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

enter_card(rank, suit)
int rank, suit;
{
  CardInfo *card, *ptr;

  card = (CardInfo *) malloc(sizeof(CardInfo));
  card->rank = rank;
  card->suit = suit;
  ptr = my_hand[suit].head;
  while (ptr->next && ptr->next->rank > rank) {
    ptr = ptr->next;
  }
  card->next = ptr->next;
  ptr->next = card;
  print_cards(suit);
}

remove_card(rank, suit)
int rank, suit;
{
  CardInfo *card, *ptr;

  ptr = my_hand[suit].head;
  while (ptr->next) {
    if (ptr->next->rank == rank) {
      card = ptr->next;
      ptr->next = card->next;
      free((char *) card);
      break;
    } else {
      ptr = ptr->next;
    }
  }
  print_cards(suit);
}

erase_window(window_num)
int window_num;
{
  if (window_num == PLAY_WINDOW) {
/* if PLAY_WINDOW then save it for showing last */
    lastCards[0] = playCards[0];
    lastCards[1] = playCards[1];
    lastCards[2] = playCards[2];
    lastCards[3] = playCards[3];
    strcpy(lastNames[0], playNames[0]);
    strcpy(lastNames[1], playNames[1]);
    strcpy(lastNames[2], playNames[2]);
    strcpy(lastNames[3], playNames[3]);
    playCards[0].rank = 0;
    playCards[1].rank = 0;
    playCards[2].rank = 0;
    playCards[3].rank = 0;
    playNames[0][0] = '\0';
    playNames[1][0] = '\0';
    playNames[2][0] = '\0';
    playNames[3][0] = '\0';
    XClearArea(XtDisplay(playArea), XtWindow(playArea), 0, 0,
	       0, 0, False);
    count = 0;
    XtSetArg(args[count], XtNlabel, " ");  count++;
    XtSetValues(playLabels[0], args, count);
    XtSetValues(playLabels[1], args, count);
    XtSetValues(playLabels[2], args, count);
    XtSetValues(playLabels[3], args, count);
  } else if (window_num == TEXT_WINDOW) {
    displayMessage("");
  }
}

read_card()
{
  setReadingCard(True);
}

play_card(player, rch, sch, msg)
int player, rch, sch;
char *msg;
{
  playCards[player-1].rank = get_rank(rch);
  playCards[player-1].suit = get_suit(sch);
  if (!showOld) {
    paint_card(XtWindow(playArea), (player-1)*STACK_WIDTH, 0,
	       convertRank(playCards[player-1].rank),
	       convertSuit(playCards[player-1].suit),
	       0);
  }
  if (*msg != '\0') {
    strcpy(playNames[player-1], msg);
    if (!showOld) {
      count = 0;
      XtSetArg(args[count], XtNlabel, msg);  count++;
      XtSetValues(playLabels[player-1], args, count);
    }
  }
  XSync(XtDisplay(playArea), False);
  updateDisplay();
  sleep(1);
}

score_points(player, pts, total_pts, msg)
int player, pts, total_pts;
char *msg;
{
  char str[256];

  count = 0;
  XtSetArg(args[count], XtNlabel, msg);  count++;
  XtSetValues(playerLabels[player], args, count);
  sprintf(str, "%d  ", pts);
  count = 0;
  XtSetArg(args[count], XtNlabel, str);  count++;
  XtSetValues(scoreLabels[player], args, count);
  sprintf(str, "%d  ", total_pts);
  count = 0;
  XtSetArg(args[count], XtNlabel, str);  count++;
  XtSetValues(totalLabels[player], args, count);
}

display_message(window_num, msg)
int window_num;
char *msg;
{
  char *str, *str2;

  if (window_num == MESG_WINDOW || window_num == PLAY_WINDOW) {
    count = 0;
    XtSetArg(args[count], XtNstring, &str);  count++;
    XtGetValues(messageArea, args, count);
    str2 = malloc(strlen(str)+strlen(msg)+2);
    strcpy(str2, str);
    strcat(str2, msg);
    strcat(str2, "\n");
    count = 0;
    XtSetArg(args[count], XtNstring, str2);  count++;
    XtSetValues(messageArea, args, count);
    free(str2);
  } else {
    count = 0;
    XtSetArg(args[count], XtNlabel, msg);  count++;
    if (window_num == TEXT_WINDOW) {
      XtSetValues(textArea, args, count);
    } else if (window_num == ROUND_WINDOW) {
      XtSetValues(roundLabel, args, count);
    } else if (window_num == LEAD_WINDOW) {
      XtSetValues(leadLabel, args, count);
    }
  }
}

game_is_over()
{
  Boolean buttonPressed;
  XEvent event;

  display_message(MESG_WINDOW,
		  "--------------------------------------------------------");
  display_message(MESG_WINDOW, "");
  display_message(MESG_WINDOW, "Click the left mouse button to continue...");

  buttonPressed = False;
  XGrabPointer(XtDisplay(messageArea), XtWindow(messageArea), True,
	       ButtonPressMask, GrabModeAsync, GrabModeAsync,
	       XtWindow(messageArea), None, CurrentTime);
  while (!buttonPressed) {
    if (XCheckTypedWindowEvent(XtDisplay(messageArea),
			       XtWindow(messageArea),
			       ButtonPress, &event)) {
      XtDispatchEvent(&event);
      XUngrabPointer(XtDisplay(messageArea), event.xbutton.time);
      XSync(XtDisplay(messageArea), False);
      buttonPressed = True;
    }
  }
}

start_game()
{
  XLowerWindow(XtDisplay(optionForm), XtWindow(optionForm));
  XSync(XtDisplay(optionForm), False);
  showOld = False;

  playCards[0].rank = 0;
  playCards[1].rank = 0;
  playCards[2].rank = 0;
  playCards[3].rank = 0;
  playNames[0][0] = '\0';
  playNames[1][0] = '\0';
  playNames[2][0] = '\0';
  playNames[3][0] = '\0';
  lastCards[0].rank = 0;
  lastCards[1].rank = 0;
  lastCards[2].rank = 0;
  lastCards[3].rank = 0;
  lastNames[0][0] = '\0';
  lastNames[1][0] = '\0';
  lastNames[2][0] = '\0';
  lastNames[3][0] = '\0';

  count = 0;
  XtSetArg(args[count], XtNstring, "");  count++;
  XtSetValues(messageArea, args, count);
}

/*
 * Scan input for redraw screen requests or ':' messages.
 * Also scan socket for incoming commands.
 */
scan()
{
  static XEvent event;

  XtAppNextEvent(appContext, &event);
  XtDispatchEvent(&event);
}

terminate()
{
  XtDestroyWidget(toplevel);
}

/*
 * show_table - display table which is position table_pos on screen.
 */
show_table(cur_ptr, table_pos)
table_ptr	cur_ptr;
int		table_pos;
{
  Dimension width, height, marginWidth, marginHeight, spacing;
  Widget *widgets;
  char str[256];
  int each;

  count = 0;
  XtSetArg(args[count], XtNwidth, &width);  count++;
  XtSetArg(args[count], XtNheight, &height);  count++;
  marginWidth = 10;
  marginHeight = 10;
  spacing = 10;
  XtGetValues(gamesRC, args, count);

  if (!cur_ptr->data) {
    widgets = (Widget *) malloc(8*sizeof(Widget));
    count = 0;
    XtSetArg(args[count], XtNresize, False);  count++;
    XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
    XtSetArg(args[count], XtNright, XawChainLeft);  count++;
    XtSetArg(args[count], XtNtop, XawChainTop);  count++;
    XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
    XtSetArg(args[count], XtNdefaultDistance, 0);  count++;
    XtSetArg(args[count], XtNwidth, 10);  count++;
    XtSetArg(args[count], XtNheight, 10);  count++;
    widgets[0] = XtCreateManagedWidget("tableFrame", formWidgetClass,
				       gamesRC, args, count);
    count = 0;
    XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
    XtSetArg(args[count], XtNright, XawChainRight);  count++;
    XtSetArg(args[count], XtNtop, XawChainTop);  count++;
    XtSetArg(args[count], XtNbottom, XawChainBottom);  count++;
    XtSetArg(args[count], XtNborderWidth, 0);  count++;
    XtSetArg(args[count], XtNresize, False);  count++;
    XtSetArg(args[count], XtNdefaultDistance, 0);  count++;
    XtSetArg(args[count], XtNwidth, 10);  count++;
    XtSetArg(args[count], XtNheight, 10);  count++;
    widgets[1] = XtCreateManagedWidget("tableW", formWidgetClass,
				       widgets[0], args, count);
    XtAddEventHandler(widgets[1], ButtonPressMask, False, tableSelect, NULL);
    globalTableHeight = 0;
    for (each=0; each<6; each++) {
      count = 0;
      XtSetArg(args[count], XtNlabel, " ");  count++;
      XtSetArg(args[count], XtNborderWidth, 0);  count++;
      XtSetArg(args[count], XtNresize, False);  count++;
      XtSetArg(args[count], XtNleft, XawChainLeft);  count++;
      XtSetArg(args[count], XtNright, XawChainRight);  count++;
      XtSetArg(args[count], XtNtop, XawChainTop);  count++;
      XtSetArg(args[count], XtNbottom, XawChainTop);  count++;
      XtSetArg(args[count], XtNwidth, 10);  count++;
      if (each) {
	XtSetArg(args[count], XtNfromVert, widgets[each+1]);  count++;
      }
      widgets[each+2] = XtCreateManagedWidget("tableLabel", labelWidgetClass,
					      widgets[1], args, count);
      count = 0;
      XtSetArg(args[count], XtNheight, &height);  count++;
      XtGetValues(widgets[each+2], args, count);
      globalTableHeight += height;
    }
    cur_ptr->data = (char *) widgets;
  } else {
    widgets = (Widget *) cur_ptr->data;
    XtManageChild(widgets[0]);
  }

  sprintf(str, "Table %d", table_pos+1);
  count = 0;
  XtSetArg(args[count], XtNlabel, str);  count++;
  XtSetValues(widgets[2], args, count);

  if (cur_ptr->closed) {
    count = 0;
    XtSetArg(args[count], XtNlabel, "<game over>");  count++;
    XtSetValues(widgets[3], args, count);
    count = 0;
    XtSetArg(args[count], XtNlabel, " ");  count++;
    XtSetValues(widgets[4], args, count);
    XtSetValues(widgets[5], args, count);
    XtSetValues(widgets[6], args, count);
    XtSetValues(widgets[7], args, count);
  } else {
    if (!cur_ptr->hand) {
      sprintf(str, "<starting>");
    } else {
      sprintf(str, "Hand: %d  Round: %d", cur_ptr->hand, cur_ptr->round);
    }
    count = 0;
    XtSetArg(args[count], XtNlabel, str);  count++;
    XtSetValues(widgets[3], args, count);

    for (each=0; each<4; each++) {
      count = 0;
      XtSetArg(args[count], XtNlabel, cur_ptr->player_name[each]);  count++;
      XtSetValues(widgets[4+each], args, count);
    }
  }

  /* Make the row column the right width */
  count = 0;
  XtSetArg(args[count], XtNwidth,
	   (width-2*marginWidth-NUM_TABLES_COLS*4-
	    (NUM_TABLES_COLS-1)*spacing)/NUM_TABLES_COLS);  count++;
  for (each=2; each<8; each++) {
    XtSetValues(widgets[each], args, count);
  }
}

/*
 * show_tables - display up to NUM_TABLES tables starting with table # start_id.
 */
show_tables(start_id)
int	start_id;
{
  table_ptr cur_ptr;
  int cur_id, i;
  char str[256];
  Widget *widgets;
  
  XtSetSensitive(moreGames, (table_count > 8));
  cur_ptr = first_table;
  for (i=0; i<table_count; i++) {
    if (cur_ptr->data) {
      widgets = (Widget *) cur_ptr->data;
      XtUnmanageChild(widgets[0]);
    }
    cur_ptr = cur_ptr->next_table;
  }
  count = 0;
  if (table_count) {
    sprintf(str, "Current Games (page %d of %d):",
	    (start_id + NUM_TABLES-1) / NUM_TABLES,
	    (table_count + NUM_TABLES-1) / NUM_TABLES);
    XtSetArg(args[count], XtNlabel, str);  count++;
  } else {
    XtSetArg(args[count], XtNlabel, "Current Games:");  count++;
  }
  XtSetValues(gamesHeader, args, count);
  cur_ptr = first_table;
  for (cur_id = 1; cur_id < start_id; cur_id++) {
    cur_ptr = cur_ptr->next_table;
  }
  XtUnmanageChild(gamesRC);
  for (cur_id = 0; (cur_id < NUM_TABLES) && cur_ptr;
       cur_ptr = cur_ptr->next_table) {
    show_table(cur_ptr, cur_id++);
  }
  positionTables();
  XtManageChild(gamesRC);
}

option_scan(another)
char *another;
{
  XEvent event;

  anotherPtr = another;
  *anotherPtr = FALSE;
  XtAppNextEvent(appContext, &event);
  XtDispatchEvent(&event);
}

option_init()
{
  if (distId) {
    XtRemoveInput(distId);
  }
  distId = XtAppAddInput(appContext, dist_socket,
			 (XtPointer) XtInputReadMask, distInput, NULL);
  XtSetSensitive(anotherGame, !first_game);
  XtSetSensitive(newGame, first_game);
  XRaiseWindow(XtDisplay(optionForm), XtWindow(optionForm));
  XSync(XtDisplay(optionForm), False);
}

init_socket()
{
  if (dealerId) {
    XtRemoveInput(dealerId);
  }
  dealerId = XtAppAddInput(appContext, dealer_socket,
			   (XtPointer) XtInputReadMask, dealerInput, NULL);
}

close_socket(s)
int s;
{
  if (s == dealer_socket && dealerId) {
    XtRemoveInput(dealerId);
    dealerId = None;
  } else if (s == dist_socket && distId) {
    XtRemoveInput(distId);
    distId = None;
  }
}

option_clear()
{
  table_ptr cur_ptr;
  int i;
  Widget *widgets;

  cur_ptr = first_table;
  for (i=0; i<table_count; i++) {
    if (cur_ptr->data) {
      widgets = (Widget *) cur_ptr->data;
      XtDestroyWidget(widgets[0]);
      cur_ptr->data = NULL;
    }
    cur_ptr = cur_ptr->next_table;
  }
}
