/*
 * FILE: xshogi.c
 *
 *     Implementation of the X interface for GNU shogi (xshogi).
 *
 * ------------------------------------------------------------------------
 * xshogi is based on XBoard -- an Xt/Athena user interface for GNU Chess.
 *
 * Original authors:                                Dan Sears, Chris Sears
 * Enhancements (Version 2.0 and following):        Tim Mann
 * Modifications to XShogi (Version 1.0):           Matthias Mutz
 * Enhancements to XShogi (Version 1.1):            Matthias Mutz
 * Modified implementation of ISS mode for XShogi:  Matthias Mutz
 * Current maintainer:                              Michael C. Vanier
 *
 * XShogi borrows its piece bitmaps from CRANES Shogi.
 *
 * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
 * Enhancements Copyright 1992 Free Software Foundation, Inc.
 * Enhancements for XShogi Copyright 1993, 1994, 1995 Matthias Mutz
 * Copyright (c) 1999 Michael Vanier and the Free Software Foundation
 *
 * The following terms apply to Digital Equipment Corporation's copyright
 * interest in XBoard:
 * ------------------------------------------------------------------------
 * 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 name of Digital not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * DIGITAL 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.
 * ------------------------------------------------------------------------
 *
 * This file is part of GNU shogi.
 *
 * GNU shogi 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.
 *
 * GNU shogi 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 GNU shogi; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * ------------------------------------------------------------------------
 *
 */

#include "config.h"

#ifdef X_DISPLAY_MISSING
#error You cannot compile xshogi if X windows is unavailable!
#endif

#include "sysdeps.h"

#define XBOARD_VERSION "2.0/2.1"

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/ioctl.h>

#include <time.h>
#include <pwd.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/List.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
#include <X11/cursorfont.h>

#include "../version.h"
#include "xshogi.h"

#define BUF_SIZE 1024
#define BOARD    1
#define MOVES    2

#include "bitmaps.h"  /* Piece bitmaps. */
#include "xshogifn.h" /* Forward declarations. */

#define off_board(x) (x < 2 || x > BOARD_SIZE + 1)


/**********************************************************************
 *
 * Global variables, structs etc.
 *
 **********************************************************************/

/*
 * NOTE: XShogi depends on Xt R4 or higher
 */

int xtVersion = XtSpecificationRelease;

XtIntervalId firstProgramXID = 0, secondProgramXID = 0,
    readGameXID = 0, timerXID = 0, blinkSquareXID = 0;

XtAppContext appContext;

Boolean (*fileProc) (char *name);

FILE *fromFirstProgFP, *toFirstProgFP, *fromSecondProgFP,
    *toSecondProgFP, *gameFileFP, *lastMsgFP;

int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0,
    firstProgramPID = 0,
    secondProgramPID = 0, fromX = -1,
    fromY = -1, firstMove = True, flipView = False,
    xshogiDebug = True, commentUp = False, filenameUp = False,
    whitePlaysFirst = False, startedFromSetupPosition = False,
    searchTime = 0, pmFromX = -1, pmFromY = -1,
    blackFlag = False, whiteFlag = False, maybeThinking = False,
    filemodeUp = False;

int at_least_gnushogi_1_2p03 = False;

int firstSendTime = 2, secondSendTime = 2; /* 0 = don't, 1 = do,
                                              2 = test first */

MatchMode matchMode         = MatchFalse;
GameMode  gameMode          = BeginningOfGame;
GameMode  lastGameMode      = BeginningOfGame;
GameMode  pausePreviousMode = BeginningOfGame;

char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2],
    ptyname[24], *shogiDir, *programName;

char endMessage[MOVE_LEN * 4];

long blackTimeRemaining, whiteTimeRemaining, timeControl;
long timeRemaining[2][MAX_MOVES];

extern char currentMoveString[];

int updateRemotePlayer = False;

Catched catches[MAX_MOVES];

#define DIMENSION 100

Widget blackPieceMenu, whitePieceMenu, commentShell;

XSetWindowAttributes attr;

#define pawn      0
#define lance     1
#define knight    2
#define silver    3
#define gold      4
#define bishop    5
#define rook      6
#define king      7
#define no_piece  8
#define ppawn     9
#define plance   10
#define pknight  11
#define psilver  12
#define pbishop  13

#define NO_PIECES  15
#define NO_SQUARES 81
#define NO_COLS     9
#define NO_ROWS     9


char catchedIndexToChar[8] =
{
    'P', 'L', 'N', 'S', 'G', 'B', 'R', 'K'
};

ShogiSquare catchedIndexToPiece[2][8] =
{
    {
        BlackPawn, BlackLance, BlackKnight, BlackSilver, BlackGold,
        BlackBishop, BlackRook, BlackKing
    },
    {
        WhitePawn, WhiteLance, WhiteKnight, WhiteSilver, WhiteGold,
        WhiteBishop, WhiteRook, WhiteKing
    }
};


int pieceToCatchedIndex[] =
{
    pawn, lance, knight, silver, gold, bishop, rook,
    pawn, lance, knight, silver, bishop, rook, king,
    pawn, lance, knight, silver, gold, bishop, rook,
    pawn, lance, knight, silver, bishop, rook, king,
    no_piece
};



Board boards[MAX_MOVES];
Board initialPosition =
{
    { BlackLance,  BlackKnight, BlackSilver, BlackGold, BlackKing,
      BlackGold,   BlackSilver, BlackKnight, BlackLance },
    { EmptySquare, BlackBishop, EmptySquare, EmptySquare, EmptySquare,
      EmptySquare, EmptySquare, BlackRook,   EmptySquare },
    { BlackPawn,   BlackPawn,   BlackPawn,   BlackPawn, BlackPawn,
      BlackPawn,   BlackPawn,   BlackPawn,   BlackPawn },
    { EmptySquare, EmptySquare, EmptySquare, EmptySquare, EmptySquare,
      EmptySquare, EmptySquare, EmptySquare, EmptySquare } ,
    { EmptySquare, EmptySquare, EmptySquare, EmptySquare, EmptySquare,
      EmptySquare, EmptySquare, EmptySquare, EmptySquare } ,
    { EmptySquare, EmptySquare, EmptySquare, EmptySquare, EmptySquare,
      EmptySquare, EmptySquare, EmptySquare, EmptySquare } ,
    { WhitePawn,   WhitePawn,   WhitePawn,   WhitePawn, WhitePawn,
      WhitePawn,   WhitePawn,   WhitePawn,   WhitePawn },
    { EmptySquare, WhiteRook,   EmptySquare, EmptySquare, EmptySquare,
      EmptySquare, EmptySquare, WhiteBishop, EmptySquare },
    { WhiteLance,  WhiteKnight, WhiteSilver, WhiteGold, WhiteKing,
      WhiteGold,   WhiteSilver, WhiteKnight, WhiteLance }
};

String gnuButtonStrings[] =
{
    "Quit",       "Load Game",      "Machine White",  "Forward",
    "Reset",      "Load Position",  "Machine Black",  "Backward",
    "Flip View",  "Save Game",      "Force Moves",    "Pause",
    "Hint",       "Save Position",  "Two Machines",   "Edit Position",
    "Challenge",  "Select Level",   "Move NOW",
};

/* must be in same order as buttonStrings! */
XtActionProc gnuButtonProcs[] =
{
    QuitProc,       LoadGameProc,      MachineWhiteProc,  ForwardProc,
    ResetProc,      LoadPositionProc,  MachineBlackProc,  BackwardProc,
    FlipViewProc,   SaveGameProc,      ForceProc,         PauseProc,
    HintProc,       SavePositionProc,  TwoMachinesProc,   EditPositionProc,
    ChallengeProc,  SelectLevelProc,   MoveNowProc,
    NULL
};


String *buttonStrings;
XtActionProc *buttonProcs;
int buttonCount;

#define PIECE_MENU_SIZE 18

String pieceMenuStrings[PIECE_MENU_SIZE] =
{
    "----",     "Pawn",          "Lance",       "Knight", "Silver",
    "Gold",     "Bishop",        "Rook",
    "PPawn",    "PLance",        "PKnight",     "PSilver",
    "PBishop",  "PRook",         "King",
    "----",     "Empty square",  "Clear board"
};

/* must be in same order as PieceMenuStrings! */
ShogiSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] =
{
    {
        (ShogiSquare) 0,  BlackPawn,    BlackLance,    BlackKnight,
        BlackSilver,      BlackGold,    BlackBishop,   BlackRook,
        BlackPPawn,       BlackPLance,  BlackPKnight,  BlackPSilver,
        BlackPBishop,     BlackPRook,   BlackKing,
        (ShogiSquare) 0,  EmptySquare,  ClearBoard
    },
    {
        (ShogiSquare) 0,  WhitePawn,    WhiteLance,    WhiteKnight,
        WhiteSilver,      WhiteGold,    WhiteBishop,   WhiteRook,
        WhitePPawn,       WhitePLance,  WhitePKnight,  WhitePSilver,
        WhitePBishop,     WhitePRook,   WhiteKing,
        (ShogiSquare) 0,  EmptySquare,  ClearBoard
    },
};


typedef struct
{
    Pixel blackPieceColor;
    Pixel whitePieceColor;
    Pixel lightSquareColor;
    Pixel darkSquareColor;
    Pixel charPieceColor;
    Pixel zeroColor;
    Pixel oneColor;
    Boolean westernPieceSet;
    int movesPerSession;
    String initString;
    String blackString;
    String whiteString;
    String firstShogiProgram;
    String secondShogiProgram;
    Boolean noShogiProgram;
    String firstHost;
    String secondHost;
    String reverseBigSolidBitmap;
    String reverseSmallSolidBitmap;
    String normalBigSolidBitmap;
    String normalSmallSolidBitmap;
    String reversePawnBitmap;
    String reverseLanceBitmap;
    String reverseKnightBitmap;
    String reverseSilverBitmap;
    String reverseGoldBitmap;
    String reverseRookBitmap;
    String reverseBishopBitmap;
    String reversePPawnBitmap;
    String reversePLanceBitmap;
    String reversePKnightBitmap;
    String reversePSilverBitmap;
    String reversePBishopBitmap;
    String reversePRookBitmap;
    String reverseKingBitmap;
    String normalPawnBitmap;
    String normalLanceBitmap;
    String normalKnightBitmap;
    String normalSilverBitmap;
    String normalGoldBitmap;
    String normalRookBitmap;
    String normalBishopBitmap;
    String normalPPawnBitmap;
    String normalPLanceBitmap;
    String normalPKnightBitmap;
    String normalPSilverBitmap;
    String normalPBishopBitmap;
    String normalPRookBitmap;
    String normalKingBitmap;
    String remoteShell;
    float  timeDelay;
    String timeControl;
    String gameIn;

    Boolean autoSaveGames;
    String loadGameFile;
    String loadPositionFile;
    String saveGameFile;
    String savePositionFile;
    String matchMode;
    String challengeDisplay;
    Boolean monoMode;
    Boolean debugMode;
    Boolean clockMode;
    String boardSize;
    Boolean Iconic;
    String searchTime;
    int searchDepth;
    Boolean showCoords;
    String mainFont;
    String coordFont;
    Boolean ringBellAfterMoves;
    Boolean autoCallFlag;
    int borderXoffset;
    int borderYoffset;
} AppData, *AppDataPtr;


XtResource clientResources[] =
{
    {
        "blackPieceColor", "BlackPieceColor", XtRPixel, sizeof(Pixel),
        XtOffset(AppDataPtr, blackPieceColor), XtRString,
        BLACK_PIECE_COLOR
    },
    {
        "whitePieceColor", "WhitePieceColor", XtRPixel, sizeof(Pixel),
        XtOffset(AppDataPtr, whitePieceColor), XtRString,
        WHITE_PIECE_COLOR
    },
    {
        "charPieceColor", "CharPieceColor", XtRPixel, sizeof(Pixel),
        XtOffset(AppDataPtr, charPieceColor), XtRString,
        CHAR_PIECE_COLOR
    },
    {
        "oneColor", "OneColor", XtRPixel, sizeof(Pixel),
        XtOffset(AppDataPtr, oneColor), XtRString,
        ONE_COLOR
    },
    {
        "zeroColor", "ZeroColor", XtRPixel, sizeof(Pixel),
        XtOffset(AppDataPtr, zeroColor), XtRString,
        ZERO_COLOR
    },
    {
        "lightSquareColor", "LightSquareColor", XtRPixel,
        sizeof(Pixel), XtOffset(AppDataPtr, lightSquareColor),
        XtRString, LIGHT_SQUARE_COLOR
    },
    {
        "darkSquareColor", "DarkSquareColor", XtRPixel, sizeof(Pixel),
        XtOffset(AppDataPtr, darkSquareColor), XtRString,
        DARK_SQUARE_COLOR
    },
    {
        "westernPieceSet", "WesternPieceSet", XtRBoolean, sizeof(Boolean),
        XtOffset(AppDataPtr, westernPieceSet), XtRString,
        (XtPointer) False
    },
    {
        "movesPerSession", "movesPerSession", XtRInt, sizeof(int),
        XtOffset(AppDataPtr, movesPerSession), XtRImmediate,
        (XtPointer) MOVES_PER_SESSION
    },
    {
        "initString", "initString", XtRString, sizeof(String),
        XtOffset(AppDataPtr, initString), XtRString, INIT_STRING
    },
    {
        "blackString", "blackString", XtRString, sizeof(String),
        XtOffset(AppDataPtr, blackString), XtRString, BLACK_STRING
    },
    {
        "whiteString", "whiteString", XtRString, sizeof(String),
        XtOffset(AppDataPtr, whiteString), XtRString, WHITE_STRING
    },
    {
        "firstShogiProgram", "firstShogiProgram", XtRString,
        sizeof(String), XtOffset(AppDataPtr, firstShogiProgram),
        XtRString, FIRST_SHOGI_PROGRAM
    },
    {
        "secondShogiProgram", "secondShogiProgram", XtRString,
        sizeof(String), XtOffset(AppDataPtr, secondShogiProgram),
        XtRString, SECOND_SHOGI_PROGRAM
    },
    {
        "noShogiProgram", "noShogiProgram", XtRBoolean,
        sizeof(Boolean), XtOffset(AppDataPtr, noShogiProgram),
        XtRImmediate, (XtPointer) False
    },
    {
        "firstHost", "firstHost", XtRString, sizeof(String),
        XtOffset(AppDataPtr, firstHost), XtRString, FIRST_HOST
    },
    {
        "secondHost", "secondHost", XtRString, sizeof(String),
        XtOffset(AppDataPtr, secondHost), XtRString, SECOND_HOST
    },
    {
        "reversePawnBitmap", "reversePawnBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reversePawnBitmap),
        XtRString, NULL
    },
    {
        "reverseLanceBitmap", "reverseLanceBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reverseLanceBitmap),
        XtRString, NULL
    },
    {
        "reverseKnightBitmap", "reverseKnightBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reverseKnightBitmap),
        XtRString, NULL
    },
    {
        "reverseSilverBitmap", "reverseSilverBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reverseSilverBitmap),
        XtRString, NULL
    },
    {
        "reverseGoldBitmap", "reverseGoldBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reverseGoldBitmap),
        XtRString, NULL
    },
    {
        "reverseRookBitmap", "reverseRookBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reverseRookBitmap),
        XtRString, NULL
    },
    {
        "reverseBishopBitmap", "reverseBishopBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reverseBishopBitmap),
        XtRString, NULL
    },
    {
        "reversePPawnBitmap", "reversePPawnBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reversePPawnBitmap),
        XtRString, NULL
    },
    {
        "reversePLanceBitmap", "reversePLanceBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reversePLanceBitmap),
        XtRString, NULL
    },
    {
        "reversePKnightBitmap", "reversePKnightBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reversePKnightBitmap),
        XtRString, NULL
    },
    {
        "reversePSilverBitmap", "reversePSilverBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reversePSilverBitmap),
        XtRString, NULL
    },
    {
        "reversePRookBitmap", "reversePRookBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reversePRookBitmap),
        XtRString, NULL
    },
    {
        "reversePBishopBitmap", "reversePBishopBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reversePBishopBitmap),
        XtRString, NULL
    },
    {
        "reverseKingBitmap", "reverseKingBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, reverseKingBitmap),
        XtRString, NULL
    },
    {
        "normalPawnBitmap", "normalPawnBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalPawnBitmap),
        XtRString, NULL
    },
    {
        "normalLanceBitmap", "normalLanceBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalLanceBitmap),
        XtRString, NULL
    },
    {
        "normalKnightBitmap", "normalKnightBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalKnightBitmap),
        XtRString, NULL
    },
    {
        "normalSilverBitmap", "normalSilverBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalSilverBitmap),
        XtRString, NULL
    },
    {
        "normalGoldBitmap", "normalGoldBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalGoldBitmap),
        XtRString, NULL
    },
    {
        "normalBishopBitmap", "normalBishopBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalBishopBitmap),
        XtRString, NULL
    },
    {
        "normalRookBitmap", "normalRookBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalRookBitmap),
        XtRString, NULL
    },
    {
        "normalPPawnBitmap", "normalPPawnBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalPPawnBitmap),
        XtRString, NULL
    },
    {
        "normalPLanceBitmap", "normalPLanceBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalPLanceBitmap),
        XtRString, NULL
    },
    {
        "normalPKnightBitmap", "normalPKnightBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalPKnightBitmap),
        XtRString, NULL
    },
    {
        "normalPSilverBitmap", "normalPSilverBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalPSilverBitmap),
        XtRString, NULL
    },
    {
        "normalPBishopBitmap", "normalPBishopBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalPBishopBitmap),
        XtRString, NULL
    },
    {
        "normalPRookBitmap", "normalPRookBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalPRookBitmap),
        XtRString, NULL
    },
    {
        "normalKingBitmap", "normalKingBitmap", XtRString,
        sizeof(String), XtOffset(AppDataPtr, normalKingBitmap),
        XtRString, NULL
    },
    {
        "remoteShell", "remoteShell", XtRString, sizeof(String),
        XtOffset(AppDataPtr, remoteShell), XtRString, "rsh"
    },
    {
        "timeDelay", "timeDelay", XtRFloat, sizeof(float),
        XtOffset(AppDataPtr, timeDelay), XtRString,
        (XtPointer) TIME_DELAY
    },
    {
        "timeControl", "timeControl", XtRString, sizeof(String),
        XtOffset(AppDataPtr, timeControl), XtRString,
        (XtPointer) TIME_CONTROL
    },
    {
        "gameIn", "gameIn",
        XtRBoolean, sizeof(Boolean),
        XtOffset(AppDataPtr, gameIn), XtRImmediate,
        (XtPointer) False
    },
    {
        "autoSaveGames", "autoSaveGames", XtRBoolean,
        sizeof(Boolean), XtOffset(AppDataPtr, autoSaveGames),
        XtRImmediate, (XtPointer) False
    },
    {
        "loadGameFile", "loadGameFile", XtRString, sizeof(String),
        XtOffset(AppDataPtr, loadGameFile), XtRString, NULL
    },
    {
        "loadPositionFile", "loadPositionFile", XtRString,
        sizeof(String), XtOffset(AppDataPtr, loadPositionFile),
        XtRString, NULL
    },
    {
        "saveGameFile", "saveGameFile", XtRString, sizeof(String),
        XtOffset(AppDataPtr, saveGameFile), XtRString, ""
    },
    {
        "savePositionFile", "savePositionFile", XtRString,
        sizeof(String), XtOffset(AppDataPtr, savePositionFile),
        XtRString, ""
    },
    {
        "challengeDisplay", "challengeDisplay", XtRString,
        sizeof(String), XtOffset(AppDataPtr, challengeDisplay),
        XtRString, NULL
    },
    {
        "matchMode", "matchMode", XtRString, sizeof(String),
        XtOffset(AppDataPtr, matchMode), XtRString, MATCH_MODE
    },
    {
        "monoMode", "monoMode", XtRBoolean, sizeof(Boolean),
        XtOffset(AppDataPtr, monoMode), XtRImmediate,
        (XtPointer) False
    },
    {
        "debugMode", "debugMode", XtRBoolean, sizeof(Boolean),
        XtOffset(AppDataPtr, debugMode), XtRImmediate,
        (XtPointer) False
    },
    {
        "Iconic", "Iconic", XtRBoolean, sizeof(Boolean),
        XtOffset(AppDataPtr, Iconic), XtRImmediate,
        (XtPointer) False
    },
    {
        "clockMode", "clockMode", XtRBoolean, sizeof(Boolean),
        XtOffset(AppDataPtr, clockMode), XtRImmediate,
        (XtPointer) True
    },
    {
        "autoCallFlag", "autoCallFlag", XtRBoolean,
        sizeof(Boolean), XtOffset(AppDataPtr, autoCallFlag),
        XtRImmediate, (XtPointer) False
    },
    {
        "boardSize", "boardSize", XtRString, sizeof(String),
        XtOffset(AppDataPtr, boardSize), XtRString, DEFAULT_SIZE
    },
    {
        "searchTime", "searchTime", XtRString, sizeof(String),
        XtOffset(AppDataPtr, searchTime), XtRString,
        (XtPointer) NULL
    },
    {
        "searchDepth", "searchDepth", XtRInt, sizeof(int),
        XtOffset(AppDataPtr, searchDepth), XtRImmediate,
        (XtPointer) 0
    },
    {
        "showCoords", "showCoords", XtRBoolean, sizeof(Boolean),
        XtOffset(AppDataPtr, showCoords), XtRImmediate,
        (XtPointer) False
    },
    {
        "mainFont", "mainFont", XtRString, sizeof(String),
        XtOffset(AppDataPtr, mainFont), XtRString, MAIN_FONT
    },
    {
        "coordFont", "coordFont", XtRString, sizeof(String),
        XtOffset(AppDataPtr, coordFont), XtRString, COORD_FONT
    },
    {
        "ringBellAfterMoves", "ringBellAfterMoves",
        XtRBoolean, sizeof(Boolean),
        XtOffset(AppDataPtr, ringBellAfterMoves),
        XtRImmediate, (XtPointer) False
    },
    {
        "borderXoffset", "borderXoffset", XtRInt, sizeof(int),
        XtOffset(AppDataPtr, borderXoffset), XtRImmediate,
        (XtPointer) BORDER_X_OFFSET
    },
    {
        "borderYoffset", "borderYOffset", XtRInt, sizeof(int),
        XtOffset(AppDataPtr, borderYoffset), XtRImmediate,
        (XtPointer) BORDER_Y_OFFSET
    }
};


struct DisplayData
{

    AppData appData;

    Arg shellArgs[6];
    Arg boardArgs[3];
    Arg commandsArgs[7];
    Arg messageArgs[3];
    Arg timerArgs[2];
    Arg titleArgs[2];

    Pixmap reversePawnBitmap, reverseLanceBitmap,   reverseKnightBitmap,
        reverseSilverBitmap,
        reverseGoldBitmap,    reverseBishopBitmap,  reverseRookBitmap,
        reversePPawnBitmap,   reversePLanceBitmap,  reversePKnightBitmap,
        reversePSilverBitmap, reversePBishopBitmap, reversePRookBitmap,
        reverseKingBitmap,
        reverseBigSolidBitmap, reverseSmallSolidBitmap,
        normalBigSolidBitmap,  normalSmallSolidBitmap,
        normalPawnBitmap,      normalLanceBitmap,   normalKnightBitmap,
        normalSilverBitmap,    normalGoldBitmap,
        normalBishopBitmap,    normalRookBitmap,
        normalPPawnBitmap,     normalPLanceBitmap,  normalPKnightBitmap,
        normalPSilverBitmap,   normalPBishopBitmap, normalPRookBitmap,
        normalKingBitmap,
        iconPixmap;

    Display *xDisplay;
    int xScreen;
    Window xBoardWindow;

    GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
        woPieceGC, boPieceGC, bdPieceGC, blPieceGC, wbPieceGC,
        bwPieceGC, coordGC, dropPiece;

    GC charPieceGC;

    Font mainFontID, coordFontID;
    XFontStruct *mainFontStruct, *coordFontStruct;

    Widget shellWidget, formWidget, boardWidget,
        commandsWidget, messageWidget,
        blackTimerWidget, whiteTimerWidget,
        titleWidget, widgetList[6],
        promotionShell,
        filemodeShell, challengeWidget;

    XSegment gridSegments[(BOARD_SIZE + 1) * 2];

    Pixel timerForegroundPixel, timerBackgroundPixel;

    BoardSize boardSize;
    int squareSize;
    int black_pixel_is_zero;
    int flipView;
    int promotionUp;

    Boolean monoMode, showCoords, Iconic;
};



struct DisplayData localPlayer, remotePlayer;


typedef struct
{
    ShogiSquare piece;
    int to_x, to_y;
} PromotionMoveInfo;

static PromotionMoveInfo pmi;  /* making this global is gross */


Pixmap *pieceToReverse[2][28] =
{
    {
        &localPlayer.reversePawnBitmap,
        &localPlayer.reverseLanceBitmap,
        &localPlayer.reverseKnightBitmap,
        &localPlayer.reverseSilverBitmap,
        &localPlayer.reverseGoldBitmap,
        &localPlayer.reverseBishopBitmap,
        &localPlayer.reverseRookBitmap,
        &localPlayer.reversePPawnBitmap,
        &localPlayer.reversePLanceBitmap,
        &localPlayer.reversePKnightBitmap,
        &localPlayer.reversePSilverBitmap,
        &localPlayer.reversePBishopBitmap,
        &localPlayer.reversePRookBitmap,
        &localPlayer.reverseKingBitmap,
        &localPlayer.reversePawnBitmap,
        &localPlayer.reverseLanceBitmap,
        &localPlayer.reverseKnightBitmap,
        &localPlayer.reverseSilverBitmap,
        &localPlayer.reverseGoldBitmap,
        &localPlayer.reverseBishopBitmap,
        &localPlayer.reverseRookBitmap,
        &localPlayer.reversePPawnBitmap,
        &localPlayer.reversePLanceBitmap,
        &localPlayer.reversePKnightBitmap,
        &localPlayer.reversePSilverBitmap,
        &localPlayer.reversePBishopBitmap,
        &localPlayer.reversePRookBitmap,
        &localPlayer.reverseKingBitmap
    },
    {
        &remotePlayer.reversePawnBitmap,
        &remotePlayer.reverseLanceBitmap,
        &remotePlayer.reverseKnightBitmap,
        &remotePlayer.reverseSilverBitmap,
        &remotePlayer.reverseGoldBitmap,
        &remotePlayer.reverseBishopBitmap,
        &remotePlayer.reverseRookBitmap,
        &remotePlayer.reversePPawnBitmap,
        &remotePlayer.reversePLanceBitmap,
        &remotePlayer.reversePKnightBitmap,
        &remotePlayer.reversePSilverBitmap,
        &remotePlayer.reversePBishopBitmap,
        &remotePlayer.reversePRookBitmap,
        &remotePlayer.reverseKingBitmap,
        &remotePlayer.reversePawnBitmap,
        &remotePlayer.reverseLanceBitmap,
        &remotePlayer.reverseKnightBitmap,
        &remotePlayer.reverseSilverBitmap,
        &remotePlayer.reverseGoldBitmap,
        &remotePlayer.reverseBishopBitmap,
        &remotePlayer.reverseRookBitmap,
        &remotePlayer.reversePPawnBitmap,
        &remotePlayer.reversePLanceBitmap,
        &remotePlayer.reversePKnightBitmap,
        &remotePlayer.reversePSilverBitmap,
        &remotePlayer.reversePBishopBitmap,
        &remotePlayer.reversePRookBitmap,
        &remotePlayer.reverseKingBitmap
    }
};



Pixmap *pieceToNormal[2][28] =
{
    {
        &localPlayer.normalPawnBitmap,
        &localPlayer.normalLanceBitmap,
        &localPlayer.normalKnightBitmap,
        &localPlayer.normalSilverBitmap,
        &localPlayer.normalGoldBitmap,
        &localPlayer.normalBishopBitmap,
        &localPlayer.normalRookBitmap,
        &localPlayer.normalPPawnBitmap,
        &localPlayer.normalPLanceBitmap,
        &localPlayer.normalPKnightBitmap,
        &localPlayer.normalPSilverBitmap,
        &localPlayer.normalPBishopBitmap,
        &localPlayer.normalPRookBitmap,
        &localPlayer.normalKingBitmap,
        &localPlayer.normalPawnBitmap,
        &localPlayer.normalLanceBitmap,
        &localPlayer.normalKnightBitmap,
        &localPlayer.normalSilverBitmap,
        &localPlayer.normalGoldBitmap,
        &localPlayer.normalBishopBitmap,
        &localPlayer.normalRookBitmap,
        &localPlayer.normalPPawnBitmap,
        &localPlayer.normalPLanceBitmap,
        &localPlayer.normalPKnightBitmap,
        &localPlayer.normalPSilverBitmap,
        &localPlayer.normalPBishopBitmap,
        &localPlayer.normalPRookBitmap,
        &localPlayer.normalKingBitmap
    },
    {
        &remotePlayer.normalPawnBitmap,
        &remotePlayer.normalLanceBitmap,
        &remotePlayer.normalKnightBitmap,
        &remotePlayer.normalSilverBitmap,
        &remotePlayer.normalGoldBitmap,
        &remotePlayer.normalBishopBitmap,
        &remotePlayer.normalRookBitmap,
        &remotePlayer.normalPPawnBitmap,
        &remotePlayer.normalPLanceBitmap,
        &remotePlayer.normalPKnightBitmap,
        &remotePlayer.normalPSilverBitmap,
        &remotePlayer.normalPBishopBitmap,
        &remotePlayer.normalPRookBitmap,
        &remotePlayer.normalKingBitmap,
        &remotePlayer.normalPawnBitmap,
        &remotePlayer.normalLanceBitmap,
        &remotePlayer.normalKnightBitmap,
        &remotePlayer.normalSilverBitmap,
        &remotePlayer.normalGoldBitmap,
        &remotePlayer.normalBishopBitmap,
        &remotePlayer.normalRookBitmap,
        &remotePlayer.normalPPawnBitmap,
        &remotePlayer.normalPLanceBitmap,
        &remotePlayer.normalPKnightBitmap,
        &remotePlayer.normalPSilverBitmap,
        &remotePlayer.normalPBishopBitmap,
        &remotePlayer.normalPRookBitmap,
        &remotePlayer.normalKingBitmap
    }
};



Pixmap *pieceToReverseSolid[2][28] =
{
    {
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseSmallSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap,
        &localPlayer.reverseBigSolidBitmap
    },
    {
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseSmallSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap,
        &remotePlayer.reverseBigSolidBitmap
    }
};



Pixmap *pieceToNormalSolid[2][28] =
{
    {
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalSmallSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap,
        &localPlayer.normalBigSolidBitmap
    },
    {
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalSmallSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap,
        &remotePlayer.normalBigSolidBitmap
    }
};



int pieceIsPromoted[] =
{
    False, False, False, False, False, False, False,
    True,  True,  True,  True,  True,  True,  False,
    False, False, False, False, False, False, False,
    True,  True,  True,  True,  True,  True,  False,
    False
};


int piecePromotable[] =
{
    True,  True,  True,  True,  False, True,  True,
    False, False, False, False, False, False, False,
    True,  True,  True,  True,  False, True,  True,
    False, False, False, False, False, False, False,
    False
};


char pieceToChar[] =
{
    'P', 'L', 'N', 'S', 'G', 'B', 'R', 'P', 'L', 'N', 'S', 'B', 'R', 'K',
    'p', 'l', 'n', 's', 'g', 'b', 'r', 'p', 'l', 'n', 's', 'b', 'r', 'k',
    '.'
};


int pieceisWhite[] =
{
    False, False, False, False, False, False, False,
    False, False, False, False, False, False, False,
    True,  True,  True,  True,  True, True,  True,
    True,  True,  True,  True,  True, True, True,
    False
};



ShogiSquare pieceToPromoted[] =
{
    BlackPPawn,   BlackPLance, BlackPKnight, BlackPSilver, BlackGold,
    BlackPBishop, BlackPRook,
    BlackPPawn,   BlackPLance, BlackPKnight, BlackPSilver,
    BlackPBishop, BlackPRook,  BlackKing,
    WhitePPawn,   WhitePLance, WhitePKnight, WhitePSilver, WhiteGold,
    WhitePBishop, WhitePRook,
    WhitePPawn,   WhitePLance, WhitePKnight, WhitePSilver,
    WhitePBishop, WhitePRook,  WhiteKing
};



XrmOptionDescRec shellOptions[] =
{
    { "-blackPieceColor", "blackPieceColor", XrmoptionSepArg, NULL },
    { "-bpc", "blackPieceColor", XrmoptionSepArg, NULL },
    { "-whitePieceColor", "whitePieceColor", XrmoptionSepArg, NULL },
    { "-wpc", "whitePieceColor", XrmoptionSepArg, NULL },
    { "-charPieceColor", "charPieceColor", XrmoptionSepArg, NULL },
    { "-cpc", "charPieceColor", XrmoptionSepArg, NULL },
    { "-zeroColor", "zeroColor", XrmoptionSepArg, NULL },
    { "-zc", "zeroColor", XrmoptionSepArg, NULL },
    { "-oneColor", "oneColor", XrmoptionSepArg, NULL },
    { "-oc", "oneColor", XrmoptionSepArg, NULL },
    { "-lightSquareColor", "lightSquareColor", XrmoptionSepArg, NULL },
    { "-lsc", "lightSquareColor", XrmoptionSepArg, NULL },
    { "-darkSquareColor", "darkSquareColor", XrmoptionSepArg, NULL },
    { "-dsc", "darkSquareColor", XrmoptionSepArg, NULL },
    { "-westernPieceSet", "westernPieceSet", XrmoptionSepArg, NULL },
    { "-wps", "westernPieceSet", XrmoptionSepArg, NULL },
    { "-movesPerSession", "movesPerSession", XrmoptionSepArg, NULL },
    { "-mps", "movesPerSession", XrmoptionSepArg, NULL },
    { "-firstShogiProgram", "firstShogiProgram", XrmoptionSepArg, NULL },
    { "-fsp", "firstShogiProgram", XrmoptionSepArg, NULL },
    { "-secondShogiProgram", "secondShogiProgram", XrmoptionSepArg, NULL },
    { "-ssp", "secondShogiProgram", XrmoptionSepArg, NULL },
    { "-noShogiProgram", "noShogiProgram", XrmoptionSepArg, NULL },
    { "-nsp", "noShogiProgram", XrmoptionSepArg, NULL },
    { "-firstHost", "firstHost", XrmoptionSepArg, NULL },
    { "-fh", "firstHost", XrmoptionSepArg, NULL },
    { "-secondHost", "secondHost", XrmoptionSepArg, NULL },
    { "-sh", "secondHost", XrmoptionSepArg, NULL },
    { "-reversePawnBitmap", "reversePawnBitmap", XrmoptionSepArg, NULL },
    { "-rpb", "reversePawnBitmap", XrmoptionSepArg, NULL },
    { "-reverseLanceBitmap", "reverseLanceBitmap", XrmoptionSepArg, NULL },
    { "-rlb", "reverseLanceBitmap", XrmoptionSepArg, NULL },
    { "-reverseKnightBitmap", "reverseKnightBitmap", XrmoptionSepArg, NULL },
    { "-rnb", "reverseKnightBitmap", XrmoptionSepArg, NULL },
    { "-reverseSilverBitmap", "reverseSilverBitmap", XrmoptionSepArg, NULL },
    { "-rsb", "reverseSilverBitmap", XrmoptionSepArg, NULL },
    { "-reverseGoldBitmap", "reverseGoldBitmap", XrmoptionSepArg, NULL },
    { "-rgb", "reverseGoldBitmap", XrmoptionSepArg, NULL },
    { "-reverseRookBitmap", "reverseRookBitmap", XrmoptionSepArg, NULL },
    { "-rrb", "reverseRookBitmap", XrmoptionSepArg, NULL },
    { "-reverseBishopBitmap", "reverseBishopBitmap", XrmoptionSepArg, NULL },
    { "-rbb", "reverseBishopBitmap", XrmoptionSepArg, NULL },
    { "-reversePPawnBitmap", "reversePPawnBitmap",
      XrmoptionSepArg, NULL },
    { "-rppb", "reversePPawnBitmap", XrmoptionSepArg, NULL },
    { "-reversePLanceBitmap", "reversePLanceBitmap",
      XrmoptionSepArg, NULL },
    { "-rplb", "reversePLanceBitmap", XrmoptionSepArg, NULL },
    { "-reversePKnightBitmap", "reversePKnightBitmap",
      XrmoptionSepArg, NULL },
    { "-rpnb", "reversePKnightBitmap", XrmoptionSepArg, NULL },
    { "-reversePSilverBitmap", "reversePSilverBitmap",
      XrmoptionSepArg, NULL },
    { "-rpsb", "reversePSilverBitmap", XrmoptionSepArg, NULL },
    { "-reversePRookBitmap", "reversePRookBitmap",
      XrmoptionSepArg, NULL },
    { "-rprb", "reversePRookBitmap", XrmoptionSepArg, NULL },
    { "-reversePBishopBitmap", "reversePBishopBitmap",
      XrmoptionSepArg, NULL },
    { "-rpbb", "reversePBishopBitmap", XrmoptionSepArg, NULL },
    { "-reverseKingBitmap", "reverseKingBitmap", XrmoptionSepArg, NULL },
    { "-rkb", "reverseKingBitmap", XrmoptionSepArg, NULL },
    { "-outlinePawnBitmap", "outlinePawnBitmap", XrmoptionSepArg, NULL },
    { "-opb", "normalPawnBitmap", XrmoptionSepArg, NULL },
    { "-normalLanceBitmap", "normalLanceBitmap", XrmoptionSepArg, NULL },
    { "-olb", "normalLanceBitmap", XrmoptionSepArg, NULL },
    { "-normalKnightBitmap", "normalKnightBitmap", XrmoptionSepArg, NULL },
    { "-onb", "normalKnightBitmap", XrmoptionSepArg, NULL },
    { "-normalSilverBitmap", "normalSilverBitmap", XrmoptionSepArg, NULL },
    { "-osb", "normalSilverBitmap", XrmoptionSepArg, NULL },
    { "-normalGoldBitmap", "normalGoldBitmap", XrmoptionSepArg, NULL },
    { "-ogb", "normalGoldBitmap", XrmoptionSepArg, NULL },
    { "-normalRookBitmap", "normalRookBitmap", XrmoptionSepArg, NULL },
    { "-orb", "normalRookBitmap", XrmoptionSepArg, NULL },
    { "-normalBishopBitmap", "normalBishopBitmap", XrmoptionSepArg, NULL },
    { "-obb", "normalBishopBitmap", XrmoptionSepArg, NULL },
    { "-normalPPawnBitmap", "normalPPawnBitmap", XrmoptionSepArg, NULL },
    { "-oppb", "normalPPawnBitmap", XrmoptionSepArg, NULL },
    { "-normalPLanceBitmap", "normalPLanceBitmap", XrmoptionSepArg, NULL },
    { "-oplb", "normalPLanceBitmap", XrmoptionSepArg, NULL },
    { "-normalPKnightBitmap", "normalPKnightBitmap", XrmoptionSepArg, NULL },
    { "-opnb", "normalPKnightBitmap", XrmoptionSepArg, NULL },
    { "-normalPSilverBitmap", "normalPSilverBitmap", XrmoptionSepArg, NULL },
    { "-opsb", "normalPSilverBitmap", XrmoptionSepArg, NULL },
    { "-normalPRookBitmap", "normalPRookBitmap", XrmoptionSepArg, NULL },
    { "-oprb", "normalPRookBitmap", XrmoptionSepArg, NULL },
    { "-normalPBishopBitmap", "normalPBishopBitmap", XrmoptionSepArg, NULL },
    { "-opbb", "normalPBishopBitmap", XrmoptionSepArg, NULL },
    { "-normalKingBitmap", "normalKingBitmap", XrmoptionSepArg, NULL },
    { "-okb", "outlineKingBitmap", XrmoptionSepArg, NULL },
    { "-remoteShell", "remoteShell", XrmoptionSepArg, NULL },
    { "-rsh", "remoteShell", XrmoptionSepArg, NULL },
    { "-timeDelay", "timeDelay", XrmoptionSepArg, NULL },
    { "-td", "timeDelay", XrmoptionSepArg, NULL },
    { "-timeControl", "timeControl", XrmoptionSepArg, NULL },
    { "-tc", "timeControl", XrmoptionSepArg, NULL },
    { "-gameIn", "gameIn", XrmoptionSepArg, NULL },
    { "-gi", "gameIn", XrmoptionSepArg, NULL },
    { "-loadGameFile", "loadGameFile", XrmoptionSepArg, NULL },
    { "-lgf", "loadGameFile", XrmoptionSepArg, NULL },
    { "-loadPositionFile", "loadPositionFile", XrmoptionSepArg, NULL },
    { "-lpf", "loadPositionFile", XrmoptionSepArg, NULL },
    { "-saveGameFile", "saveGameFile", XrmoptionSepArg, NULL },
    { "-sgf", "saveGameFile", XrmoptionSepArg, NULL },
    { "-savePositionFile", "savePositionFile", XrmoptionSepArg, NULL },
    { "-spf", "savePositionFile", XrmoptionSepArg, NULL },
    { "-challengeDisplay", "challengeDisplay", XrmoptionSepArg, NULL },
    { "-cd", "challengeDisplay", XrmoptionSepArg, NULL },
    { "-matchMode", "matchMode", XrmoptionSepArg, NULL },
    { "-mm", "matchMode", XrmoptionSepArg, NULL },
    { "-monoMode", "monoMode", XrmoptionSepArg, NULL },
    { "-mono", "monoMode", XrmoptionSepArg, NULL },
    { "-debugMode", "debugMode", XrmoptionSepArg, NULL },
    { "-debug", "debugMode", XrmoptionSepArg, NULL },
    { "-clockMode", "clockMode", XrmoptionSepArg, NULL },
    { "-clock", "clockMode", XrmoptionSepArg, NULL },
    { "-boardSize", "boardSize", XrmoptionSepArg, NULL },
    { "-size", "boardSize", XrmoptionSepArg, NULL },
    { "-searchTime", "searchTime", XrmoptionSepArg, NULL },
    { "-st", "searchTime", XrmoptionSepArg, NULL },
    { "-searchDepth", "searchDepth", XrmoptionSepArg, NULL },
    { "-sd", "searchDepth", XrmoptionSepArg, NULL },
    { "-showCoords", "showCoords", XrmoptionSepArg, NULL },
    { "-coords", "showCoords", XrmoptionSepArg, NULL },
    { "-iconic", "Iconic", XrmoptionNoArg, "True" }
};



XtActionsRec boardActions[] =
{
    { "DrawPosition",   (XtActionProc) DrawPosition },
    { "HandleUserMove", (XtActionProc) HandleUserMove },
    { "ResetProc",      (XtActionProc) ResetProc },
    { "ResetFileProc",  (XtActionProc) ResetFileProc },
    { "LoadGameProc",   (XtActionProc) LoadGameProc },
    { "QuitProc",       (XtActionProc) QuitProc },
    { "ForwardProc",    (XtActionProc) ForwardProc },
    { "BackwardProc",   (XtActionProc) BackwardProc },
    { "PauseProc",      (XtActionProc) PauseProc },
    { "Iconify",        (XtActionProc) Iconify },
    { "FileNameAction", (XtActionProc) FileNameAction },
    { "PieceMenuPopup", (XtActionProc) PieceMenuPopup },
    { "SetBlackToPlay", (XtActionProc) SetBlackToPlay },
    { "SetWhiteToPlay", (XtActionProc) SetWhiteToPlay }
};


char translationsTable[] =
"<Expose>: DrawPosition() \n \
<Btn1Down>: HandleUserMove() \n \
<Btn1Up>: HandleUserMove() \n \
<Btn2Down>: XawPositionSimpleMenu(menuW) PieceMenuPopup(menuW) \n \
<Btn3Down>: XawPositionSimpleMenu(menuB) PieceMenuPopup(menuB) \n \
<Key>r: ResetFileProc() ResetProc() \n \
<Key>R: ResetFileProc() ResetProc() \n \
<Key>g: LoadGameProc() \n \
<Key>G: LoadGameProc() \n \
<Key>q: QuitProc() \n \
<Key>Q: QuitProc() \n \
<Message>WM_PROTOCOLS: QuitProc() \n \
<Key>f: ForwardProc() \n \
<Key>F: ForwardProc() \n \
<Key>b: BackwardProc() \n \
<Key>B: BackwardProc() \n \
<Key>p: PauseProc() \n \
<Key>P: PauseProc() \n \
<Key>i: Iconify() \n \
<Key>I: Iconify() \n \
<Key>c: Iconify() \n \
<Key>C: Iconify() \n";


char translationsTableReduced[] =
"<Expose>: DrawPosition() \n \
<Btn1Down>: HandleUserMove() \n \
<Btn1Up>: HandleUserMove() \n \
<Message>WM_PROTOCOLS: QuitProc() \n";


char blackTranslations[] = "<BtnDown>: SetBlackToPlay()\n";
char whiteTranslations[] = "<BtnDown>: SetWhiteToPlay()\n";

String xshogiResources[] =
{
    DEFAULT_FONT,
    "*Dialog*value.translations: #override "
    "\\n <Key>Return: FileNameAction()",
    NULL
};


int global_argc;       /* number of command args */
char *global_argv[10]; /* pointers to up to 10 command args */



static struct DisplayData *player;


typedef struct
{
    char mode[2];
    char name[100];
} FileModeInfo;

static FileModeInfo fmi;


/**********************************************************************
 *
 * End of globals.
 *
 **********************************************************************/


void
CreatePlayerWindow(void)
{
    int mainFontPxlSize, coordFontPxlSize;
    int min, sec, matched;
    XSetWindowAttributes window_attributes;
    char buf[MSG_SIZ];
    Arg args[10];
    Dimension timerWidth, boardWidth, commandsWidth, w, h;
    int local;
    int fromRemotePlayer = (player == &remotePlayer);

    player->monoMode = player->appData.monoMode;
    player->showCoords = player->appData.showCoords;

    /*
     * Parse timeControl resource.
     */

    if (player->appData.timeControl != NULL)
    {
        matched = sscanf(player->appData.timeControl, "%d:%d", &min, &sec);

        if (matched == 1)
        {
            timeControl = min * 60 * 1000;
        }
        else if (matched == 2)
        {
            timeControl = (min * 60 + sec) * 1000;
        }
        else
        {
            fprintf(stderr, "%s: bad timeControl option %s\n",
                    programName, player->appData.timeControl);
            Usage();
        }
    }

    /*
     * Parse searchTime resource
     */

    if (player->appData.searchTime != NULL)
    {
        matched = sscanf(player->appData.searchTime, "%d:%d", &min, &sec);

        if (matched == 1)
        {
            searchTime = min * 60;
        }
        else if (matched == 2)
        {
            searchTime = min * 60 + sec;
        }
        else
        {
            fprintf(stderr, "%s: bad searchTime option %s\n",
                    programName, player->appData.searchTime);
            Usage();
        }
    }

    if ((player->appData.searchTime != NULL)
        || (player->appData.searchDepth > 0)
        || player->appData.noShogiProgram)
    {
        player->appData.clockMode = False;
    }

    player->Iconic = False;
    player->boardSize = Small;
    player->squareSize = SMALL_SQUARE_SIZE;
    player->flipView = (player == &remotePlayer);
    player->promotionUp = False;

    /*
     * Determine boardSize.
     */

    if (strcasecmp(player->appData.boardSize, "Large") == 0)
    {
        player->boardSize = Large;
    }
    else if (strcasecmp(player->appData.boardSize, "Medium") == 0)
    {
        player->boardSize = Medium;
    }
    else if (strcasecmp(player->appData.boardSize, "Small") == 0)
    {
        player->boardSize = Small;
    }
    else
    {
        fprintf(stderr, "%s: bad boardSize option %s\n",
                programName, player->appData.boardSize);
        Usage();
    }

    if ((local = (player == &localPlayer)))
    {
        player->xDisplay = XtDisplay(player->shellWidget);
        player->xScreen = DefaultScreen(player->xDisplay);
    }

    if (((DisplayWidth(player->xDisplay, player->xScreen) < 800)
         || (DisplayHeight(player->xDisplay, player->xScreen) < 800))
        && (player->boardSize == Large))
    {
        player->boardSize = Medium;
    }

    switch (player->boardSize)
    {
    case Small:
        player->squareSize = SMALL_SQUARE_SIZE;
        mainFontPxlSize    = 11;
        coordFontPxlSize   = 10;
        break;

    case Medium:
        player->squareSize = MEDIUM_SQUARE_SIZE;
        mainFontPxlSize    = 17;
        coordFontPxlSize   = 12;
        break;

    default:
    case Large:
        player->squareSize = LARGE_SQUARE_SIZE;
        mainFontPxlSize    = 17;
        coordFontPxlSize   = 14;
        break;
    }

    /*
     * Detect if there are not enough colors are available and adapt.
     */

    if (DefaultDepth(player->xDisplay, player->xScreen) <= 2)
        player->monoMode = True;

    /*
     * Determine what fonts to use.
     */

    player->appData.mainFont
        = FindFont(player->appData.mainFont, mainFontPxlSize);
    player->mainFontID
        = XLoadFont(player->xDisplay, player->appData.mainFont);
    player->mainFontStruct
        = XQueryFont(player->xDisplay, player->mainFontID);
    player->appData.coordFont
        = FindFont(player->appData.coordFont, coordFontPxlSize);
    player->coordFontID
        = XLoadFont(player->xDisplay, player->appData.coordFont);
    player->coordFontStruct
        = XQueryFont(player->xDisplay, player->coordFontID);

    /*
     * Set default arguments.
     */

    XtSetArg(player->shellArgs[0], XtNwidth, 0);
    XtSetArg(player->shellArgs[1], XtNheight, 0);
    XtSetArg(player->shellArgs[2], XtNminWidth, 0);
    XtSetArg(player->shellArgs[3], XtNminHeight, 0);
    XtSetArg(player->shellArgs[4], XtNmaxWidth, 0);
    XtSetArg(player->shellArgs[5], XtNmaxHeight, 0);

    XtSetArg(player->boardArgs[0], XtNborderWidth, 0);
    XtSetArg(player->boardArgs[1], XtNwidth,
             LINE_GAP + (BOARD_SIZE + 4)
             * (SMALL_SQUARE_SIZE + LINE_GAP));
    XtSetArg(player->boardArgs[2], XtNheight,
             LINE_GAP + BOARD_SIZE
             * (SMALL_SQUARE_SIZE + LINE_GAP));

    XtSetArg(player->commandsArgs[0], XtNborderWidth, 0);
    XtSetArg(player->commandsArgs[1], XtNdefaultColumns, 4);
    XtSetArg(player->commandsArgs[2], XtNforceColumns, True);
    XtSetArg(player->commandsArgs[3], XtNcolumnSpacing, 12);
    XtSetArg(player->commandsArgs[4], XtNlist, (XtArgVal) buttonStrings);
    XtSetArg(player->commandsArgs[5], XtNnumberStrings, buttonCount);
    XtSetArg(player->commandsArgs[6], XtNfont, player->mainFontStruct);

    XtSetArg(player->messageArgs[0], XtNborderWidth, 0);
    XtSetArg(player->messageArgs[1], XtNjustify, (XtArgVal) XtJustifyLeft);
    XtSetArg(player->messageArgs[2], XtNlabel, (XtArgVal) "starting...");

    XtSetArg(player->timerArgs[0], XtNborderWidth, 0);
    XtSetArg(player->timerArgs[1], XtNjustify, (XtArgVal) XtJustifyLeft);

    XtSetArg(player->titleArgs[0], XtNborderWidth, 0);
    XtSetArg(player->titleArgs[1], XtNjustify, (XtArgVal) XtJustifyLeft);

    boardWidth = LINE_GAP
        + (BOARD_SIZE + 4) * (player->squareSize + LINE_GAP);

    XtSetArg(player->boardArgs[1], XtNwidth, boardWidth);
    XtSetArg(player->boardArgs[2], XtNheight,
             LINE_GAP + BOARD_SIZE * (player->squareSize + LINE_GAP));

    /*
     * widget hierarchy
     */

    player->formWidget = XtCreateManagedWidget("form",
                                               formWidgetClass,
                                               player->shellWidget, NULL, 0);

    player->widgetList[0] = player->blackTimerWidget
        = XtCreateWidget((local ? "black time:" : "rblack time:"),
                         labelWidgetClass,
                         player->formWidget, player->timerArgs,
                         XtNumber(player->timerArgs));

    XtSetArg(args[0], XtNfont, player->mainFontStruct);
    XtSetValues(player->blackTimerWidget, args, 1);

    player->widgetList[1] = player->whiteTimerWidget
        = XtCreateWidget((local ? "white time:" : "rwhite time:"),
                         labelWidgetClass,
                         player->formWidget, player->timerArgs,
                         XtNumber(player->timerArgs));

    XtSetArg(args[0], XtNfont, player->mainFontStruct);
    XtSetValues(player->whiteTimerWidget, args, 1);

    player->widgetList[2] = player->titleWidget
        = XtCreateWidget((local ? "" : "r"), labelWidgetClass,
                         player->formWidget, player->titleArgs,
                         XtNumber(player->titleArgs));

    XtSetArg(args[0], XtNfont, player->mainFontStruct);
    XtSetValues(player->titleWidget, args, 1);

    player->widgetList[3] = player->messageWidget
        = XtCreateWidget((local ? "message" : "rmessage"),
                         labelWidgetClass, player->formWidget,
                         player->messageArgs,
                         XtNumber(player->messageArgs));

    XtSetArg(args[0], XtNfont, player->mainFontStruct);
    XtSetValues(player->messageWidget, args, 1);

    player->widgetList[4] = player->commandsWidget
        = XtCreateWidget((local ? "commands" : "rcommand"),
                         listWidgetClass, player->formWidget,
                         player->commandsArgs,
                         XtNumber(player->commandsArgs));

    player->widgetList[5] = player->boardWidget
        = XtCreateWidget((local ? "board" : "rboard"),
                         widgetClass, player->formWidget,
                         player->boardArgs,
                         XtNumber(player->boardArgs));

    XtManageChildren(player->widgetList, XtNumber(player->widgetList));

    /*
     * Calculate the width of the timer labels.
     */

    XtSetArg(args[0], XtNfont, &player->mainFontStruct);
    XtGetValues(player->blackTimerWidget, args, 1);

    if (player->appData.clockMode)
    {
        /* sprintf(buf, "Black: %s ", TimeString(timeControl));
           timerWidth = XTextWidth(player->mainFontStruct,
           buf, strlen(buf)); */
        timerWidth = XTextWidth(player->mainFontStruct,
                                "Black: 8:88:88 ", 15);
    }
    else
    {
        timerWidth = XTextWidth(player->mainFontStruct, "Black  ", 7);
    }

    XtSetArg(args[0], XtNwidth, timerWidth);
    XtSetValues(player->blackTimerWidget, args, 1);
    XtSetValues(player->whiteTimerWidget, args, 1);

    XtSetArg(args[0], XtNbackground, &player->timerForegroundPixel);
    XtSetArg(args[1], XtNforeground, &player->timerBackgroundPixel);
    XtGetValues(player->blackTimerWidget, args, 2);

    /*
     * Calculate the width of the name and message labels.
     */

    XtSetArg(args[0], XtNwidth, &commandsWidth);
    XtGetValues(player->commandsWidget, args, 1);
    w = ((commandsWidth > boardWidth) ? commandsWidth : boardWidth);
    XtSetArg(args[0], XtNwidth, w - timerWidth*2 - 12);
    XtSetValues(player->titleWidget, args, 1);
    XtSetArg(args[0], XtNwidth, w - 8);
    XtSetValues(player->messageWidget, args, 1);

    /*
     * formWidget uses these constraints but they are stored
     * in the children.
     */

    XtSetArg(args[0], XtNfromHoriz, player->blackTimerWidget);
    XtSetValues(player->whiteTimerWidget, args, 1);
    XtSetArg(args[0], XtNfromHoriz, player->whiteTimerWidget);
    XtSetValues(player->titleWidget, args, 1);
    XtSetArg(args[0], XtNfromVert, player->blackTimerWidget);
    XtSetValues(player->messageWidget, args, 1);
    XtSetArg(args[0], XtNfromVert, player->messageWidget);
    XtSetValues(player->commandsWidget, args, 1);
    XtSetArg(args[0], XtNfromVert, player->commandsWidget);
    XtSetValues(player->boardWidget, args, 1);

    XtRealizeWidget(player->shellWidget);

    player->xBoardWindow = XtWindow(player->boardWidget);

    /*
     * Create an icon.
     */

    player->iconPixmap =
        XCreateBitmapFromData(player->xDisplay,
                              XtWindow(player->shellWidget),
                              icon_bits, icon_width, icon_height);

    XtSetArg(args[0], XtNiconPixmap, player->iconPixmap);
    XtSetValues(player->shellWidget, args, 1);

    /*
     * Create a cursor for the board widget.
     */

    window_attributes.cursor = XCreateFontCursor(player->xDisplay, XC_hand2);
    XChangeWindowAttributes(player->xDisplay, player->xBoardWindow,
                            CWCursor, &window_attributes);

    /*
     * Inhibit shell resizing.
     */

    player->shellArgs[0].value = (XtArgVal) &w;
    player->shellArgs[1].value = (XtArgVal) &h;
    XtGetValues(player->shellWidget, player->shellArgs, 2);
    player->shellArgs[4].value = player->shellArgs[2].value = w;
    player->shellArgs[5].value = player->shellArgs[3].value = h;
    XtSetValues(player->shellWidget, &player->shellArgs[2], 4);

    /*
     * Determine value of black pixel.
     */

    player->black_pixel_is_zero =
        (XBlackPixel(player->xDisplay, player->xScreen) == 0);

    CreateGCs();
    CreateGrid();
    CreatePieces();

    if (!fromRemotePlayer)
        CreatePieceMenus();

    XtAddCallback(player->commandsWidget, XtNcallback, SelectCommand,
                  (XtPointer)fromRemotePlayer);

    if (!fromRemotePlayer)
        XtAppAddActions(appContext, boardActions, XtNumber(boardActions));

    if (fromRemotePlayer)
    {
        XtSetArg(args[0], XtNtranslations,
                 XtParseTranslationTable(translationsTableReduced));
        /* Disable key commands because often keys are pressed
           in the board window if using another talk window. */
        XtSetValues(player->boardWidget, &args[0], 1);
        XtSetValues(localPlayer.boardWidget, &args[0], 1);
    }
    else
    {
        XtSetArg(args[0], XtNtranslations,
                 XtParseTranslationTable(translationsTable));
        XtSetValues(player->boardWidget, &args[0], 1);
        XtSetArg(args[0], XtNtranslations,
                 XtParseTranslationTable(blackTranslations));
        XtSetValues(player->blackTimerWidget, &args[0], 1);
        XtSetArg(args[0], XtNtranslations,
                 XtParseTranslationTable(whiteTranslations));
        XtSetValues(player->whiteTimerWidget, &args[0], 1);
    }

    XtAddEventHandler(player->boardWidget, ExposureMask | ButtonPressMask
                      | ButtonReleaseMask | Button1MotionMask | KeyPressMask,
                      False, (XtEventHandler)EventProc,
                      (XtPointer)(player == &remotePlayer));

    sprintf(buf, "xshogi version %s, patchlevel %s based on "
            "xboard version %s",
            version, patchlevel, XBOARD_VERSION);

    /*
     * If there is to be a machine match, set it up.
     */

    if (matchMode != MatchFalse && player != &remotePlayer)
    {
        if (player->appData.noShogiProgram)
        {
            fprintf(stderr,
                    "%s: can't have a match with no shogi programs!\n",
                    programName);
            exit(1);
        }

        DisplayMessage(buf, fromRemotePlayer);
        TwoMachinesProc(NULL, NULL, NULL, NULL);
    }
    else
    {
        Reset(True);
        DisplayMessage(buf, fromRemotePlayer);
    }
}




int
main(int argc, char **argv)
{
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    /*
     * Copy pointers to command line arguments and number of such pointers.
     * (argc, argv will be destroyed by XtAppInitialize)
     */

    for (global_argc = 0; global_argc < argc; global_argc++)
        global_argv[global_argc] = argv[global_argc];

    programName = strrchr(argv[0], '/');

    if (programName == NULL)
        programName = argv[0];
    else
        programName++;

    localPlayer.shellWidget
        = XtAppInitialize(&appContext, "XShogi", shellOptions,
                          XtNumber(shellOptions), &argc, argv,
                          xshogiResources, NULL, 0);

    if (argc > 1)
        Usage();

    if ((shogiDir = (char *)getenv("SHOGIDIR")) == NULL)
    {
        shogiDir = ".";
    }
    else
    {
        if (chdir(shogiDir) != 0)
        {
            fprintf(stderr, "%s: can't cd to SHOGIDIR\n",
                    programName);
            perror(shogiDir);
            exit(1);
        }
    }

    XtGetApplicationResources(localPlayer.shellWidget,
                              &localPlayer.appData, clientResources,
                              XtNumber(clientResources), NULL, 0);

    xshogiDebug = localPlayer.appData.debugMode;

    /*
     * Determine matchMode state -- poor man's resource converter
     */

    if (strcasecmp(localPlayer.appData.matchMode, "Init") == 0)
    {
        matchMode = MatchInit;
    }
    else if (strcasecmp(localPlayer.appData.matchMode, "Position") == 0)
    {
        matchMode = MatchPosition;
    }
    else if (strcasecmp(localPlayer.appData.matchMode, "Opening") == 0)
    {
        matchMode = MatchOpening;
    }
    else if (strcasecmp(localPlayer.appData.matchMode, "False") == 0)
    {
        matchMode = MatchFalse;
    }
    else
    {
        fprintf(stderr, "%s: bad matchMode option %s\n",
                programName, localPlayer.appData.matchMode);
        Usage();
    }

    buttonStrings = gnuButtonStrings;
    buttonProcs = gnuButtonProcs;
    buttonCount = XtNumber(gnuButtonStrings);

    player = &localPlayer;

    CreatePlayerWindow();

    XtAppMainLoop(appContext);

    return 0;
}



/*
 * Find a font that matches "pattern" that is as close as
 * possible to the targetPxlSize.  Prefer fonts that are k
 * pixels smaller to fonts that are k pixels larger.  The
 * pattern must be in the X Consortium standard format,
 * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
 * The return value should be freed with XtFree when no
 * longer needed.
 */

char *
FindFont(char *pattern, int targetPxlSize)
{
    char **fonts, *p, *best;
    int i, j, nfonts, minerr, err, pxlSize;

    fonts = XListFonts(player->xDisplay, pattern, 999999, &nfonts);

    if (nfonts < 1)
    {
        fprintf(stderr, "%s: No fonts match pattern %s\n",
                programName, pattern);
        exit(1);
    }

    best = "";
    minerr = 999999;

    for (i = 0; i < nfonts; i++)
    {
        j = 0;
        p = fonts[i];

        if (*p != '-')
            continue;

        while (j < 7)
        {
            if (*p == NULLCHAR)
                break;

            if (*p++ == '-')
                j++;
        }

        if (j < 7)
            continue;

        pxlSize = atoi(p);

        if (pxlSize == targetPxlSize)
        {
            best = fonts[i];
            break;
        }

        err = pxlSize - targetPxlSize;

        if (abs(err) < abs(minerr)
            || ((minerr > 0) && (err < 0) && (-err == minerr)))
        {
            best = fonts[i];
            minerr = err;
        }
    }

    p = (char *)XtMalloc(strlen(best) + 1);
    strcpy(p, best);
    XFreeFontNames(fonts);
    return p;
}




void
CreateGCs(void)
{
    XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
        | GCBackground | GCFunction | GCPlaneMask;
    XGCValues gc_values;

    gc_values.plane_mask = AllPlanes;
    gc_values.line_width = LINE_GAP;
    gc_values.line_style = LineSolid;
    gc_values.function = GXcopy;

    gc_values.foreground = XBlackPixel(player->xDisplay, player->xScreen);
    gc_values.background = XBlackPixel(player->xDisplay, player->xScreen);
    player->lineGC = XtGetGC(player->shellWidget, value_mask, &gc_values);

    gc_values.background = XWhitePixel(player->xDisplay, player->xScreen);
    player->coordGC = XtGetGC(player->shellWidget, value_mask, &gc_values);
    XSetFont(player->xDisplay, player->coordGC, player->coordFontID);

    if (player->monoMode)
    {
        gc_values.foreground
            = XWhitePixel(player->xDisplay, player->xScreen);
        gc_values.background
            = XBlackPixel(player->xDisplay, player->xScreen);
        player->lightSquareGC = player->darkSquareGC = player->wbPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);
        gc_values.foreground
            = XBlackPixel(player->xDisplay, player->xScreen);
        gc_values.background
            = XWhitePixel(player->xDisplay, player->xScreen);
        player->bwPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);
    }
    else
    {
        /* white piece black background */
        gc_values.foreground
            = XWhitePixel(player->xDisplay, player->xScreen);
        gc_values.background
            = XBlackPixel(player->xDisplay, player->xScreen);
        player->wbPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* black piece white background */
        gc_values.foreground
            = XBlackPixel(player->xDisplay, player->xScreen);
        gc_values.background
            = XWhitePixel(player->xDisplay, player->xScreen);
        player->bwPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* light empty square */
        gc_values.foreground
            = player->appData.lightSquareColor;
        gc_values.background
            = player->appData.darkSquareColor;
        player->lightSquareGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* dark empty square */
        gc_values.foreground
            = player->appData.darkSquareColor;
        gc_values.background
            = player->appData.lightSquareColor;
        player->darkSquareGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* black piece on dark square */
        gc_values.background
            = player->appData.blackPieceColor;
        gc_values.foreground
            = player->appData.darkSquareColor;
        player->bdPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* black piece on light square */
        gc_values.background
            = player->appData.blackPieceColor;
        gc_values.foreground
            = player->appData.lightSquareColor;
        player->blPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* white piece on dark square */
        gc_values.background
            = player->appData.whitePieceColor;
        gc_values.foreground
            = player->appData.darkSquareColor;
        player->wdPieceGC
            = XtGetGC(player->shellWidget,  value_mask, &gc_values);

        /* white piece on dark square */
        gc_values.background
            = player->appData.whitePieceColor;
        gc_values.foreground
            = player->appData.lightSquareColor;
        player->wlPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* black piece off board */
        gc_values.background
            = player->appData.blackPieceColor;
        gc_values.foreground
            = XWhitePixel(player->xDisplay, player->xScreen);
        player->boPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* white piece off board */
        gc_values.background
            = player->appData.whitePieceColor;
        gc_values.foreground
            = XWhitePixel(player->xDisplay, player->xScreen);
        player->woPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);

        /* piece symbol */
        gc_values.function = (player->black_pixel_is_zero ? GXand : GXor);

        gc_values.foreground
            = XBlackPixel(player->xDisplay, player->xScreen);
        gc_values.background
            = XWhitePixel(player->xDisplay, player->xScreen);
        player->charPieceGC
            = XtGetGC(player->shellWidget, value_mask, &gc_values);
    }
}




void
CreatePieces(void)
{
    XSynchronize(player->xDisplay, True);   /* Work-around for xlib/xt
                                               buffering bug */

    if (player->appData.westernPieceSet)
    {
        ReadBitmap(player->appData.reverseBigSolidBitmap,
                   &player->reverseBigSolidBitmap,
                   NULL,
                   bigsolidR_bits, bigsolidR_bits, bigsolidR_bits);

        ReadBitmap(player->appData.reverseSmallSolidBitmap,
                   &player->reverseSmallSolidBitmap,
                   NULL,
                   smallsolidR_bits, smallsolidR_bits, smallsolidR_bits);

        ReadBitmap(player->appData.normalBigSolidBitmap,
                   &player->normalBigSolidBitmap,
                   NULL,
                   bigsolid_bits, bigsolid_bits, bigsolid_bits);

        ReadBitmap(player->appData.normalSmallSolidBitmap,
                   &player->normalSmallSolidBitmap,
                   NULL,
                   smallsolid_bits, smallsolid_bits, smallsolid_bits);

        ReadBitmap(player->appData.reversePawnBitmap,
                   &player->reversePawnBitmap,
                   &player->reverseSmallSolidBitmap,
                   pawnRW_bits, pawnRW_bits, pawnRW_bits);

        ReadBitmap(player->appData.reverseLanceBitmap,
                   &player->reverseLanceBitmap,
                   &player->reverseSmallSolidBitmap,
                   lanceRW_bits, lanceRW_bits, lanceRW_bits);

        ReadBitmap(player->appData.reverseKnightBitmap,
                   &player->reverseKnightBitmap,
                   &player->reverseSmallSolidBitmap,
                   knightRW_bits, knightRW_bits, knightRW_bits);

        ReadBitmap(player->appData.reverseSilverBitmap,
                   &player->reverseSilverBitmap,
                   &player->reverseBigSolidBitmap,
                   silverRW_bits, silverRW_bits, silverRW_bits);

        ReadBitmap(player->appData.reverseGoldBitmap,
                   &player->reverseGoldBitmap,
                   &player->reverseBigSolidBitmap,
                   goldRW_bits, goldRW_bits, goldRW_bits);

        ReadBitmap(player->appData.reverseRookBitmap,
                   &player->reverseRookBitmap,
                   &player->reverseBigSolidBitmap,
                   rookRW_bits, rookRW_bits, rookRW_bits);

        ReadBitmap(player->appData.reverseBishopBitmap,
                   &player->reverseBishopBitmap,
                   &player->reverseBigSolidBitmap,
                   bishopRW_bits, bishopRW_bits, bishopRW_bits);

        ReadBitmap(player->appData.reversePPawnBitmap,
                   &player->reversePPawnBitmap,
                   &player->reverseSmallSolidBitmap,
                   pawnPRW_bits, pawnPRW_bits, pawnPRW_bits);

        ReadBitmap(player->appData.reversePLanceBitmap,
                   &player->reversePLanceBitmap,
                   &player->reverseSmallSolidBitmap,
                   lancePRW_bits, lancePRW_bits, lancePRW_bits);

        ReadBitmap(player->appData.reversePKnightBitmap,
                   &player->reversePKnightBitmap,
                   &player->reverseSmallSolidBitmap,
                   knightPRW_bits, knightPRW_bits, knightPRW_bits);

        ReadBitmap(player->appData.reversePSilverBitmap,
                   &player->reversePSilverBitmap,
                   &player->reverseBigSolidBitmap,
                   silverPRW_bits, silverPRW_bits, silverPRW_bits);

        ReadBitmap(player->appData.reversePRookBitmap,
                   &player->reversePRookBitmap,
                   &player->reverseBigSolidBitmap,
                   rookPRW_bits, rookPRW_bits, rookPRW_bits);

        ReadBitmap(player->appData.reversePBishopBitmap,
                   &player->reversePBishopBitmap,
                   &player->reverseBigSolidBitmap,
                   bishopPRW_bits, bishopPRW_bits, bishopPRW_bits);

        ReadBitmap(player->appData.reverseKingBitmap,
                   &player->reverseKingBitmap,
                   &player->reverseBigSolidBitmap,
                   kingRW_bits, kingRW_bits, kingRW_bits);

        ReadBitmap(player->appData.normalPawnBitmap,
                   &player->normalPawnBitmap,
                   &player->normalSmallSolidBitmap,
                   pawnW_bits, pawnW_bits, pawnW_bits);

        ReadBitmap(player->appData.normalLanceBitmap,
                   &player->normalLanceBitmap,
                   &player->normalSmallSolidBitmap,
                   lanceW_bits, lanceW_bits, lanceW_bits);

        ReadBitmap(player->appData.normalKnightBitmap,
                   &player->normalKnightBitmap,
                   &player->normalSmallSolidBitmap,
                   knightW_bits, knightW_bits, knightW_bits);

        ReadBitmap(player->appData.normalSilverBitmap,
                   &player->normalSilverBitmap,
                   &player->normalBigSolidBitmap,
                   silverW_bits, silverW_bits, silverW_bits);

        ReadBitmap(player->appData.normalGoldBitmap,
                   &player->normalGoldBitmap,
                   &player->normalBigSolidBitmap,
                   goldW_bits, goldW_bits, goldW_bits);

        ReadBitmap(player->appData.normalRookBitmap,
                   &player->normalRookBitmap,
                   &player->normalBigSolidBitmap,
                   rookW_bits, rookW_bits, rookW_bits);

        ReadBitmap(player->appData.normalBishopBitmap,
                   &player->normalBishopBitmap,
                   &player->normalBigSolidBitmap,
                   bishopW_bits, bishopW_bits, bishopW_bits);

        ReadBitmap(player->appData.normalPPawnBitmap,
                   &player->normalPPawnBitmap,
                   &player->normalSmallSolidBitmap,
                   pawnPW_bits, pawnPW_bits, pawnPW_bits);

        ReadBitmap(player->appData.normalPLanceBitmap,
                   &player->normalPLanceBitmap,
                   &player->normalSmallSolidBitmap,
                   lancePW_bits, lancePW_bits, lancePW_bits);

        ReadBitmap(player->appData.normalPKnightBitmap,
                   &player->normalPKnightBitmap,
                   &player->normalSmallSolidBitmap,
                   knightPW_bits, knightPW_bits, knightPW_bits);

        ReadBitmap(player->appData.normalPSilverBitmap,
                   &player->normalPSilverBitmap,
                   &player->normalBigSolidBitmap,
                   silverPW_bits, silverPW_bits, silverPW_bits);

        ReadBitmap(player->appData.normalPRookBitmap,
                   &player->normalPRookBitmap,
                   &player->normalBigSolidBitmap,
                   rookPW_bits, rookPW_bits, rookPW_bits);

        ReadBitmap(player->appData.normalPBishopBitmap,
                   &player->normalPBishopBitmap,
                   &player->normalBigSolidBitmap,
                   bishopPW_bits, bishopPW_bits, bishopPW_bits);

        ReadBitmap(player->appData.normalKingBitmap,
                   &player->normalKingBitmap,
                   &player->normalBigSolidBitmap,
                   kingW_bits, kingW_bits, kingW_bits);
    }
    else
    {
        ReadBitmap(player->appData.reverseBigSolidBitmap,
                   &player->reverseBigSolidBitmap,
                   NULL,
                   bigsolidR_bits, bigsolidR_m_bits, bigsolidR_l_bits);

        ReadBitmap(player->appData.reverseSmallSolidBitmap,
                   &player->reverseSmallSolidBitmap,
                   NULL,
                   smallsolidR_bits, smallsolidR_m_bits, smallsolidR_l_bits);

        ReadBitmap(player->appData.normalBigSolidBitmap,
                   &player->normalBigSolidBitmap,
                   NULL,
                   bigsolid_bits, bigsolid_m_bits, bigsolid_l_bits);

        ReadBitmap(player->appData.normalSmallSolidBitmap,
                   &player->normalSmallSolidBitmap,
                   NULL,
                   smallsolid_bits, smallsolid_m_bits, smallsolid_l_bits);

        ReadBitmap(player->appData.reversePawnBitmap,
                   &player->reversePawnBitmap,
                   &player->reverseSmallSolidBitmap,
                   pawnR_bits, pawnR_m_bits, pawnR_l_bits);

        ReadBitmap(player->appData.reverseLanceBitmap,
                   &player->reverseLanceBitmap,
                   &player->reverseSmallSolidBitmap,
                   lanceR_bits, lanceR_m_bits, lanceR_l_bits);

        ReadBitmap(player->appData.reverseKnightBitmap,
                   &player->reverseKnightBitmap,
                   &player->reverseSmallSolidBitmap,
                   knightR_bits, knightR_m_bits, knightR_l_bits);

        ReadBitmap(player->appData.reverseSilverBitmap,
                   &player->reverseSilverBitmap,
                   &player->reverseBigSolidBitmap,
                   silverR_bits, silverR_m_bits, silverR_l_bits);

        ReadBitmap(player->appData.reverseGoldBitmap,
                   &player->reverseGoldBitmap,
                   &player->reverseBigSolidBitmap,
                   goldR_bits, goldR_m_bits, goldR_l_bits);

        ReadBitmap(player->appData.reverseRookBitmap,
                   &player->reverseRookBitmap,
                   &player->reverseBigSolidBitmap,
                   rookR_bits, rookR_m_bits, rookR_l_bits);

        ReadBitmap(player->appData.reverseBishopBitmap,
                   &player->reverseBishopBitmap,
                   &player->reverseBigSolidBitmap,
                   bishopR_bits, bishopR_m_bits, bishopR_l_bits);

        ReadBitmap(player->appData.reversePPawnBitmap,
                   &player->reversePPawnBitmap,
                   &player->reverseSmallSolidBitmap,
                   pawnPR_bits, pawnPR_m_bits, pawnPR_l_bits);

        ReadBitmap(player->appData.reversePLanceBitmap,
                   &player->reversePLanceBitmap,
                   &player->reverseSmallSolidBitmap,
                   lancePR_bits, lancePR_m_bits, lancePR_l_bits);

        ReadBitmap(player->appData.reversePKnightBitmap,
                   &player->reversePKnightBitmap,
                   &player->reverseSmallSolidBitmap,
                   knightPR_bits, knightPR_m_bits, knightPR_l_bits);

        ReadBitmap(player->appData.reversePSilverBitmap,
                   &player->reversePSilverBitmap,
                   &player->reverseBigSolidBitmap,
                   silverPR_bits, silverPR_m_bits, silverPR_l_bits);

        ReadBitmap(player->appData.reversePRookBitmap,
                   &player->reversePRookBitmap,
                   &player->reverseBigSolidBitmap,
                   rookPR_bits, rookPR_m_bits, rookPR_l_bits);

        ReadBitmap(player->appData.reversePBishopBitmap,
                   &player->reversePBishopBitmap,
                   &player->reverseBigSolidBitmap,
                   bishopPR_bits, bishopPR_m_bits, bishopPR_l_bits);

        ReadBitmap(player->appData.reverseKingBitmap,
                   &player->reverseKingBitmap,
                   &player->reverseBigSolidBitmap,
                   kingR_bits, kingR_m_bits, kingR_l_bits);

        ReadBitmap(player->appData.normalPawnBitmap,
                   &player->normalPawnBitmap,
                   &player->normalSmallSolidBitmap,
                   pawn_bits, pawn_m_bits, pawn_l_bits);

        ReadBitmap(player->appData.normalLanceBitmap,
                   &player->normalLanceBitmap,
                   &player->normalSmallSolidBitmap,
                   lance_bits, lance_m_bits, lance_l_bits);

        ReadBitmap(player->appData.normalKnightBitmap,
                   &player->normalKnightBitmap,
                   &player->normalSmallSolidBitmap,
                   knight_bits, knight_m_bits, knight_l_bits);

        ReadBitmap(player->appData.normalSilverBitmap,
                   &player->normalSilverBitmap,
                   &player->normalBigSolidBitmap,
                   silver_bits, silver_m_bits, silver_l_bits);

        ReadBitmap(player->appData.normalGoldBitmap,
                   &player->normalGoldBitmap,
                   &player->normalBigSolidBitmap,
                   gold_bits, gold_m_bits, gold_l_bits);

        ReadBitmap(player->appData.normalRookBitmap,
                   &player->normalRookBitmap,
                   &player->normalBigSolidBitmap,
                   rook_bits, rook_m_bits, rook_l_bits);

        ReadBitmap(player->appData.normalBishopBitmap,
                   &player->normalBishopBitmap,
                   &player->normalBigSolidBitmap,
                   bishop_bits, bishop_m_bits, bishop_l_bits);

        ReadBitmap(player->appData.normalPPawnBitmap,
                   &player->normalPPawnBitmap,
                   &player->normalSmallSolidBitmap,
                   pawnP_bits, pawnP_m_bits, pawnP_l_bits);

        ReadBitmap(player->appData.normalPLanceBitmap,
                   &player->normalPLanceBitmap,
                   &player->normalSmallSolidBitmap,
                   lanceP_bits, lanceP_m_bits, lanceP_l_bits);

        ReadBitmap(player->appData.normalPKnightBitmap,
                   &player->normalPKnightBitmap,
                   &player->normalSmallSolidBitmap,
                   knightP_bits, knightP_m_bits, knightP_l_bits);

        ReadBitmap(player->appData.normalPSilverBitmap,
                   &player->normalPSilverBitmap,
                   &player->normalBigSolidBitmap,
                   silverP_bits, silverP_m_bits, silverP_l_bits);

        ReadBitmap(player->appData.normalPRookBitmap,
                   &player->normalPRookBitmap,
                   &player->normalBigSolidBitmap,
                   rookP_bits, rookP_m_bits, rookP_l_bits);

        ReadBitmap(player->appData.normalPBishopBitmap,
                   &player->normalPBishopBitmap,
                   &player->normalBigSolidBitmap,
                   bishopP_bits, bishopP_m_bits, bishopP_l_bits);

        ReadBitmap(player->appData.normalKingBitmap,
                   &player->normalKingBitmap,
                   &player->normalBigSolidBitmap,
                   king_bits, king_m_bits, king_l_bits);

    }

    XSynchronize(player->xDisplay, False);  /* Work-around for xlib/xt
                                               buffering bug */
}




int
ReadBitmapFile(Display *display, Drawable d, char *filename,
               unsigned int *width_return,
               unsigned int *height_return,
               Pixmap *bitmap_return,
               int *x_hot_return, int *y_hot_return)
{
    int n;

    if ((n = XReadBitmapFile(display, d, filename,
                             width_return, height_return,
                             bitmap_return, x_hot_return, y_hot_return))
        != BitmapSuccess)
    {
        return n;
    }
    else
    {
        /* transform a 1 plane pixmap to a k plane pixmap */
        return BitmapSuccess;
    }
}




/*
 * Create the X pixmap from .xbm file bitmap data.  This may
 * have to be revised considerably.
 */

void
ReadBitmap(String name, Pixmap *pm, Pixmap *qm,
           char *small_bits, char *medium_bits, char *large_bits)
{
    int x_hot, y_hot;
    u_int w, h;

    if ((name == NULL)
        || (ReadBitmapFile(player->xDisplay, player->xBoardWindow, name,
                          &w, &h, pm, &x_hot, &y_hot) != BitmapSuccess)
        || (w != player->squareSize)
        || (h != player->squareSize))
    {
        unsigned long fg, bg;
        unsigned int depth;

        depth = DisplayPlanes(player->xDisplay, player->xScreen);

        if (player->monoMode)
        {
            fg = XBlackPixel(player->xDisplay, player->xScreen);
            bg = XWhitePixel(player->xDisplay, player->xScreen);
        }
        else if (qm == NULL)
        {
            fg = player->appData.oneColor;
            bg = player->appData.zeroColor;
        }
        else
        {
            fg = (player->black_pixel_is_zero ? 0 : ~0);
            bg = (player->black_pixel_is_zero ? ~0 : 0);
        }

        switch (player->boardSize)
        {
        case Large:
            *pm = XCreatePixmapFromBitmapData(player->xDisplay,
                                              player->xBoardWindow,
                                              large_bits,
                                              player->squareSize,
                                              player->squareSize,
                                              fg, bg, depth);
            break;

        case Medium:
            *pm = XCreatePixmapFromBitmapData(player->xDisplay,
                                              player->xBoardWindow,
                                              medium_bits,
                                              player->squareSize,
                                              player->squareSize,
                                              fg, bg, depth);
            break;

        case Small:
            *pm = XCreatePixmapFromBitmapData(player->xDisplay,
                                              player->xBoardWindow,
                                              small_bits,
                                              player->squareSize,
                                              player->squareSize,
                                              fg, bg, depth);
            break;
        }
    }
}




void
CreateGrid(void)
{
    int i, offset;

    offset = 2 * (player->squareSize + LINE_GAP);

    for (i = 0; i < BOARD_SIZE + 1; i++)
    {
        player->gridSegments[i].x1 = offset;
        player->gridSegments[i + BOARD_SIZE + 1].y1 = 0;
        player->gridSegments[i].y1 = player->gridSegments[i].y2
            = LINE_GAP / 2 + (i * (player->squareSize + LINE_GAP));
        player->gridSegments[i].x2 = LINE_GAP + BOARD_SIZE *
            (player->squareSize + LINE_GAP) + offset;
        player->gridSegments[i + BOARD_SIZE + 1].x1 
            = player->gridSegments[i + BOARD_SIZE + 1].x2 = LINE_GAP / 2
            + (i * (player->squareSize + LINE_GAP)) + offset;
        player->gridSegments[i + BOARD_SIZE + 1].y2 
            = BOARD_SIZE * (player->squareSize + LINE_GAP);
    }
}




void
CreatePieceMenus(void)
{
    int i;
    Widget entry;
    Arg args[1];
    ShogiSquare selection;

    XtSetArg(args[0], XtNlabel, "Black");
    blackPieceMenu = XtCreatePopupShell("menuW", simpleMenuWidgetClass,
                                        localPlayer.boardWidget, args, 1);

    for (i = 0; i < PIECE_MENU_SIZE; i++)
    {
        String item = pieceMenuStrings[i];

        if (strcmp(item, "----") == 0)
        {
            entry = XtCreateManagedWidget(item, smeLineObjectClass,
                                          blackPieceMenu, NULL, 0);
        }
        else
        {
            entry = XtCreateManagedWidget(item, smeBSBObjectClass,
                                          blackPieceMenu, NULL, 0);
            selection = pieceMenuTranslation[0][i];
            XtAddCallback(entry, XtNcallback,
                          (XtCallbackProc) PieceMenuSelect,
                          (caddr_t)selection);

            if (selection == BlackPawn)
            {
                XtSetArg(args[0], XtNpopupOnEntry, entry);
                XtSetValues(blackPieceMenu, args, 1);
            }
        }
    }

    XtSetArg(args[0], XtNlabel, "White");
    whitePieceMenu = XtCreatePopupShell("menuB", simpleMenuWidgetClass,
                                        localPlayer.boardWidget, args, 1);

    for (i = 0; i < PIECE_MENU_SIZE; i++)
    {
        String item = pieceMenuStrings[i];

        if (strcmp(item, "----") == 0)
        {
            entry = XtCreateManagedWidget(item, smeLineObjectClass,
                                          whitePieceMenu, NULL, 0);
        }
        else
        {
            entry = XtCreateManagedWidget(item, smeBSBObjectClass,
                                          whitePieceMenu, NULL, 0);
            selection = pieceMenuTranslation[1][i];
            XtAddCallback(entry, XtNcallback,
                          (XtCallbackProc) PieceMenuSelect,
                          (caddr_t)selection);

            if (selection == WhitePawn)
            {
                XtSetArg(args[0], XtNpopupOnEntry, entry);
                XtSetValues(whitePieceMenu, args, 1);
            }
        }
    }

    XtRegisterGrabAction(PieceMenuPopup, True,
                         (unsigned)(ButtonPressMask|ButtonReleaseMask),
                         GrabModeAsync, GrabModeAsync);
}




void
PieceMenuPopup(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    if (event->type != ButtonPress) 
        return;

    if (gameMode != EditPosition) 
        return;

    if (((pmFromX = EventToXSquare(event->xbutton.x)) < 1) 
        || (pmFromX > BOARD_SIZE + 2) 
        || ((pmFromY = EventToSquare(event->xbutton.y)) < 0))
    {
        pmFromX = pmFromY = -1;
        return;
    }

    if (localPlayer.flipView)
        pmFromX = BOARD_SIZE + 3 - pmFromX;
    else
        pmFromY = BOARD_SIZE - 1 - pmFromY;

    XtPopupSpringLoaded(XtNameToWidget(localPlayer.boardWidget, params[0]));
}




static void
PieceMenuSelect(Widget w, ShogiSquare piece, caddr_t junk)
{
    if ((pmFromX < 0) || (pmFromY < 0))
        return;

    if (off_board(pmFromX))
    {
        int i, c;
        switch (piece)
        {
        case ClearBoard:
            break;

        case BlackPlay:
            break;

        case WhitePlay:
            break;

        default:
            i = pieceToCatchedIndex[piece];
            c = (piece >= WhitePawn);
            catches[0][c][i]++;
            DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
            XSync(localPlayer.xDisplay, False);
            return;
        }
    }

    pmFromX -= 2;

    switch (piece)
    {
    case ClearBoard:
        for (pmFromY = 0; pmFromY < BOARD_SIZE; pmFromY++)
            for (pmFromX = 0; pmFromX < BOARD_SIZE; pmFromX++)
                boards[0][pmFromY][pmFromX] = EmptySquare;

        ClearCatches(catches[0]);
        DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
        break;

    case BlackPlay:  /* not currently on menu */
        SetBlackToPlay();
        break;

    case WhitePlay:  /* not currently on menu */
        SetWhiteToPlay();
        break;

    default:
        boards[0][pmFromY][pmFromX] = piece;
        DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
        break;
    }

    XSync(localPlayer.xDisplay, False);
}




static void
SetBlackToPlay(void)
{
    int saveCM;

    if (gameMode != EditPosition) 
        return;

    whitePlaysFirst = False;
    saveCM = currentMove;
    currentMove = 0;  /* kludge */
    DisplayClocks(ReDisplayTimers);
    currentMove = saveCM;
}




static void
SetWhiteToPlay(void)
{
    int saveCM;

    if (gameMode != EditPosition) 
        return;

    whitePlaysFirst = True;
    saveCM = currentMove;
    currentMove = 1;  /* kludge */
    DisplayClocks(ReDisplayTimers);
    currentMove = saveCM;
}




/*
 * If the user selects on a border boundary or off the board, return failure.
 * Otherwise map the event coordinate to the square.
 */

int
EventToSquare(int x)
{
    if (x < LINE_GAP)
        return -1;

    x -= LINE_GAP;

    if ((x % (player->squareSize + LINE_GAP)) >= player->squareSize)
        return -1;

    x /= (player->squareSize + LINE_GAP);

    if (x >= BOARD_SIZE)
        return -1;

    return x;
}




int
EventToXSquare(int x)
{
    if (x < LINE_GAP)
        return -1;

    x -= LINE_GAP;

    if ((x % (player->squareSize + LINE_GAP)) >= player->squareSize)
        return -1;

    x /= (player->squareSize + LINE_GAP);

    if (x >= BOARD_SIZE + 4)
        return -1;

    return x;
}




ShogiSquare
CharToPiece(int c, int p)
{
    if (p)
    {
        switch (c)
        {
        default:
        case '.':   return EmptySquare;
        case 'P':   return BlackPPawn;
        case 'L':   return BlackPLance;
        case 'N':   return BlackPKnight;
        case 'S':   return BlackPSilver;
        case 'G':   return BlackGold;
        case 'R':   return BlackPRook;
        case 'B':   return BlackPBishop;
        case 'K':   return BlackKing;
        case 'p':   return WhitePPawn;
        case 'l':   return WhitePLance;
        case 'n':   return WhitePKnight;
        case 's':   return WhitePSilver;
        case 'g':   return WhiteGold;
        case 'r':   return WhitePRook;
        case 'b':   return WhitePBishop;
        case 'k':   return WhiteKing;
        }
    }
    else
    {
        switch (c)
        {
        default:
        case '.':   return EmptySquare;
        case 'P':   return BlackPawn;
        case 'L':   return BlackLance;
        case 'N':   return BlackKnight;
        case 'S':   return BlackSilver;
        case 'G':   return BlackGold;
        case 'R':   return BlackRook;
        case 'B':   return BlackBishop;
        case 'K':   return BlackKing;
        case 'p':   return WhitePawn;
        case 'l':   return WhiteLance;
        case 'n':   return WhiteKnight;
        case 's':   return WhiteSilver;
        case 'g':   return WhiteGold;
        case 'r':   return WhiteRook;
        case 'b':   return WhiteBishop;
        case 'k':   return WhiteKing;
        }
    }
}




/*
 * Convert coordinates to normal algebraic notation.
 * promoPiece must be NULLCHAR if not a promotion.
 */

ShogiMove
MakeAlg(int fromX, int fromY, int toX, int toY,
        char promoPiece, int currentBoardIndex, char *out)
{
    ShogiSquare piece;
    char *outp = out;

    if (fromX > 80)
    {
        piece = (fromX - 81);
        *outp++ = catchedIndexToChar[piece];
        *outp++ = '*';
        *outp++ = '9' - toX;
        *outp++ = 'i' - toY;
        *outp++ = NULLCHAR;
        return (BlackOnMove(forwardMostMove) ? BlackDrop : WhiteDrop);
    }
    else
    {
        *outp++ = '9' - fromX;
        *outp++ = 'i' - fromY;
        *outp++ = '9' - toX;
        *outp++ = 'i' - toY;
        *outp++ = promoPiece;
        *outp++ = NULLCHAR;

        if (promoPiece == NULLCHAR)
        {
            return NormalMove;
        }
        else
        {
            return (BlackOnMove(forwardMostMove)
                    ? BlackPromotion : WhitePromotion);
        }
    }
}




void
DrawSquare(int row, int column, ShogiSquare piece)
{
    int square_color, x, y, direction, font_ascent, font_descent;
    char string[2];
    XCharStruct overall;
    struct DisplayData *player;

    for (player = &localPlayer; True; player = &remotePlayer)
    {
        int offset, remote;

        remote = (player == &remotePlayer);
        offset = 2 * (player->squareSize + LINE_GAP);

        if (player->flipView)
        {
            x = LINE_GAP + ((BOARD_SIZE - 1) - column) *
                (player->squareSize + LINE_GAP) + offset;
            y = LINE_GAP + row * (player->squareSize + LINE_GAP);
        }
        else
        {
            x = LINE_GAP + column * (player->squareSize + LINE_GAP) + offset;
            y = LINE_GAP + ((BOARD_SIZE - 1) - row) *
                (player->squareSize + LINE_GAP);
        }

        square_color = (((column + row) % 2) ? LIGHT : DARK);

        if (piece == EmptySquare)
        {
            if (column < 0 || column >= BOARD_SIZE)
            {
                /* empty square off board */
                XFillRectangle(player->xDisplay, player->xBoardWindow,
                               player->wbPieceGC,
                               x, y, player->squareSize,
                               player->squareSize);
            }
            else
            {
                /* empty square on board */
                XFillRectangle(player->xDisplay, player->xBoardWindow,
                               ((square_color == LIGHT)
                                ? player->lightSquareGC
                                : player->darkSquareGC),
                               x, y, player->squareSize,
                               player->squareSize);
            }
        }
        else if (player->monoMode)
        {
            /* in mono mode */
            if (square_color == LIGHT)
            {
                XCopyArea(player->xDisplay,
                          ((((((int)piece) < ((int)WhitePawn)))
                            ^ player->flipView)
                           ? *pieceToNormal[remote][(int)piece]
                           : *pieceToReverse[remote][(int)piece]),
                          player->xBoardWindow,
                          (player->monoMode
                           ? player->wbPieceGC
                           : player->wlPieceGC),
                          0, 0,
                          player->squareSize, player->squareSize, x, y);
            }
            else
            {
                XCopyArea(player->xDisplay,
                          ((((((int)piece) < ((int)WhitePawn))) 
                            ^ player->flipView)
                           ? *pieceToNormal[remote][(int)piece]
                           : *pieceToReverse[remote][(int)piece]),
                          player->xBoardWindow,
                          (player->monoMode
                           ? player->bwPieceGC
                           : player->blPieceGC),
                          0, 0,
                          player->squareSize, player->squareSize, x, y);
            }
        }
        else
        {
            /* in color mode */
            if ((column < 0) || (column >= BOARD_SIZE))
            {
                /* off board */
                XCopyPlane(player->xDisplay,
                           ((((((int)piece) < ((int)WhitePawn)))
                             ^ player->flipView)
                            ? *pieceToNormalSolid[remote][(int)piece]
                            : *pieceToReverseSolid[remote][(int)piece]),
                           player->xBoardWindow,
                           (pieceisWhite[(int)piece]
                            ? player->woPieceGC
                            : player->boPieceGC),
                           0, 0,
                           player->squareSize, player->squareSize, x, y, 1);

                XCopyArea(player->xDisplay,
                          ((((((int)piece) < ((int)WhitePawn)))
                            ^ player->flipView)
                           ? *pieceToNormal[remote][(int)piece]
                           : *pieceToReverse[remote][(int)piece]),
                          player->xBoardWindow,
                          player->charPieceGC,
                          0, 0,
                          player->squareSize, player->squareSize, x, y);
            }
            else if (square_color == LIGHT)
            {
                /* on board, light square */
                XCopyPlane(player->xDisplay,
                           ((((((int)piece) < ((int)WhitePawn)))
                             ^ player->flipView)
                            ? *pieceToNormalSolid[remote][(int)piece]
                            : *pieceToReverseSolid[remote][(int)piece]),
                           player->xBoardWindow,
                           pieceisWhite[(int)piece]
                           ? player->wlPieceGC
                           : player->blPieceGC,
                           0, 0,
                           player->squareSize, player->squareSize, x, y, 1);

                XCopyArea(player->xDisplay,
                          ((((((int)piece) < ((int)WhitePawn)))
                            ^ player->flipView)
                           ? *pieceToNormal[remote][(int)piece]
                           : *pieceToReverse[remote][(int)piece]),
                          player->xBoardWindow,
                          player->charPieceGC,
                          0, 0,
                          player->squareSize, player->squareSize, x, y);
            }
            else
            {
                /* on board, dark square */
                XCopyPlane(player->xDisplay,
                           ((((((int)piece) < ((int)WhitePawn)))
                             ^ player->flipView)
                            ? *pieceToNormalSolid[remote][(int)piece]
                            : *pieceToReverseSolid[remote][(int)piece]),
                           player->xBoardWindow,
                           (pieceisWhite[(int)piece]
                            ? player->wdPieceGC
                            : player->bdPieceGC),
                           0, 0,
                           player->squareSize, player->squareSize, x, y, 1);

                XCopyArea(player->xDisplay,
                          ((((((int)piece) < ((int)WhitePawn)))
                            ^ player->flipView)
                           ? *pieceToNormal[remote][(int)piece]
                           : *pieceToReverse[remote][(int)piece]),
                          player->xBoardWindow,
                          player->charPieceGC,
                          0, 0,
                          player->squareSize, player->squareSize, x, y);
            }
        }
        string[1] = NULLCHAR;

        if (player->showCoords
            && (column >= 0) && (column < 9)
            && (row == (player->flipView ? 8 : 0)))
        {
            string[0] = '9' - column;
            XTextExtents(player->coordFontStruct, string, 1, &direction,
                         &font_ascent, &font_descent, &overall);

            if (player->monoMode)
            {
                XDrawImageString(player->xDisplay,
                                 player->xBoardWindow, player->coordGC,
                                 x + player->squareSize - overall.width - 2,
                                 y + player->squareSize - font_descent - 1,
                                 string, 1);
            }
            else
            {
                XDrawString(player->xDisplay, player->xBoardWindow,
                            player->coordGC,
                            x + player->squareSize - overall.width - 2,
                            y + player->squareSize - font_descent - 1,
                            string, 1);
            }
        }

        if (player->showCoords
            && (row >= 0) && (row < 9)
            && (column == (player->flipView ? 8 : 0)))
        {
            string[0] = 'i' - row;
            XTextExtents(player->coordFontStruct, string, 1, &direction,
                         &font_ascent, &font_descent, &overall);

            if (player->monoMode)
            {
                XDrawImageString(player->xDisplay,
                                 player->xBoardWindow, player->coordGC,
                                 x + 2, y + font_ascent + 1, string, 1);
            }
            else
            {
                XDrawString(player->xDisplay, player->xBoardWindow,
                            player->coordGC,
                            x + 2, y + font_ascent + 1, string, 1);
            }
        }

        if (!updateRemotePlayer || (player == &remotePlayer))
            break;
    }
}




void
EventProc(Widget widget, caddr_t client_data, XEvent *event)
{
    if (event->type == MappingNotify)
    {
        XRefreshKeyboardMapping((XMappingEvent *) event);
        return;
    }

    if (!XtIsRealized(widget))
        return;

    if ((event->type == ButtonPress) || (event->type == ButtonRelease))
    {
        if (event->xbutton.button != Button1)
            return;
    }

    switch (event->type)
    {
    case Expose:
        DrawPosition(widget, event, NULL, NULL);
        break;

    default:
        return;
    }
}




/*
 * event handler for redrawing the board
 */

void
DrawPosition(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    Arg args[1];
    int i, j;
    static Board lastBoard;
    static Catched lastCatches;
    static int lastBoardValid = 0;
    static int lastFlipView = 0, lastRemoteFlipView = 1;

    if (!player->Iconic)
    {
        XtSetArg(args[0], XtNiconic, False);
        XtSetValues(localPlayer.shellWidget, args, 1);
    }

    /*
     * It would be simpler to clear the window with XClearWindow()
     * but this causes a very distracting flicker.
     */

    if ((w == localPlayer.boardWidget)
        && (event == NULL)
        && lastBoardValid
        && (lastFlipView == localPlayer.flipView)
        && (!updateRemotePlayer
            || (lastRemoteFlipView == remotePlayer.flipView)))
    {
        for (i = 0; i < BOARD_SIZE; i++)
        {
            for (j = 0; j < BOARD_SIZE; j++)
            {
                if (boards[currentMove][i][j] != lastBoard[i][j])
                    DrawSquare(i, j, boards[currentMove][i][j]);
            }
        }

        for (i = 0; i < 2; i++)
        {
            for (j = 0; j < 8; j++)
            {
                if (catches[currentMove][i][j] != lastCatches[i][j])
                {
                    UpdateCatched(i, 0, False, True, currentMove);
                    break;
                }
            }
        }
    }
    else
    {
        XDrawSegments(localPlayer.xDisplay,
                      localPlayer.xBoardWindow, localPlayer.lineGC,
                      localPlayer.gridSegments, (BOARD_SIZE + 1) * 2);

        if (updateRemotePlayer)
        {
            XDrawSegments(remotePlayer.xDisplay,
                          remotePlayer.xBoardWindow, remotePlayer.lineGC,
                          remotePlayer.gridSegments, (BOARD_SIZE + 1) * 2);
        }

        for (i = 0; i < BOARD_SIZE; i++)
            for (j = 0; j < BOARD_SIZE; j++)
                DrawSquare(i, j, boards[currentMove][i][j]);

        UpdateCatched(0, 0, False, True, currentMove);
        UpdateCatched(1, 0, False, True, currentMove);
    }

    CopyBoard(lastBoard, boards[currentMove]);
    CopyCatches(lastCatches, catches[currentMove]);
    lastBoardValid = 1;
    lastFlipView = localPlayer.flipView;

    if (updateRemotePlayer)
        lastRemoteFlipView = remotePlayer.flipView;

    XSync(localPlayer.xDisplay, False);

    if (updateRemotePlayer)
        XSync(remotePlayer.xDisplay, False);
}




void
InitPosition(int redraw)
{
    currentMove = forwardMostMove = backwardMostMove = 0;
    CopyBoard(boards[0], initialPosition);
    ClearCatches(catches[0]);

    if (redraw)
        DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
}




void
CopyBoard(Board to, Board from)
{
    int i, j;

    for (i = 0; i < BOARD_SIZE; i++)
        for (j = 0; j < BOARD_SIZE; j++)
            to[i][j] = from[i][j];
}




void
CopyCatches(Catched to, Catched from)
{
    int i, j;

    for (i = 0; i < 2; i++)
        for (j = 0; j < 8; j++)
            to[i][j] = from[i][j];
}




void
SendCurrentBoard(FILE *fp)
{
    SendBoard(fp, boards[currentMove], catches[currentMove]);
}




void
SendBoard(FILE *fp, Board board, Catched catches)
{
    char message[MSG_SIZ];
    ShogiSquare *bp;
    int i, j;

    SendToProgram("edit\n", fp);
    SendToProgram("#\n", fp);

    for (i = BOARD_SIZE - 1; i >= 0; i--)
    {
        bp = &board[i][0];

        for (j = 0; j < BOARD_SIZE; j++, bp++)
        {
            if (((int) *bp) < (int)WhitePawn)
            {
                sprintf(message, "%c%c%c%s\n",
                        pieceToChar[(int) *bp],
                        '9' - j, 'i' - i,
                        (pieceIsPromoted[(int) *bp] ? "+" : ""));
                SendToProgram(message, fp);
            }
        }
    }

    for (i = 0; i <= 7; i++)
    {
        int n;

        for (n = catches[0][i]; n > 0; n--)
        {
            sprintf(message, "%c*\n",
                    catchedIndexToChar[i]);
            SendToProgram(message, fp);
        }
    }

    SendToProgram("c\n", fp);

    for (i = BOARD_SIZE - 1; i >= 0; i--)
    {
        bp = &board[i][0];

        for (j = 0; j < BOARD_SIZE; j++, bp++)
        {
            if ((((int) *bp) != ((int)EmptySquare))
                && (((int) *bp) >= ((int)WhitePawn)))
            {
                sprintf(message, "%c%c%c%s\n",
                        pieceToChar[((int) *bp) - ((int)WhitePawn)],
                        '9' - j, 'i' - i,
                        (pieceIsPromoted[(int) *bp] ? "+" : ""));
                SendToProgram(message, fp);
            }
        }
    }

    for (i = 0; i <= 7; i++)
    {
        int n;

        for (n = catches[1][i]; n > 0; n--)
        {
            sprintf(message, "%c*\n",
                    catchedIndexToChar[i]);
            SendToProgram(message, fp);
        }
    }

    SendToProgram(".\n", fp);
}




static int
PromotionPossible(int fromY, int toY, ShogiSquare piece)
{
    if (((int)piece) < ((int)WhitePawn))
    {
        if ((fromY < 6) && (toY < 6))
            return False;
    }
    else
    {
        if ((fromY > 2) && (toY > 2))
            return False;
    }

    return piecePromotable[(int)piece];

}




static void
ShowCount(int row, int column, int n)
{
    int offset = 2 * (player->squareSize + LINE_GAP);
    int x, y, direction, font_ascent, font_descent;
    char string[2];
    XCharStruct overall;
    struct DisplayData *player;

    DrawSquare(row, column, EmptySquare);

    if (n <= 1)
        return;

    for (player = &localPlayer; True; player = &remotePlayer)
    {
        if (player->flipView)
        {
            x = LINE_GAP + ((BOARD_SIZE - 1) - column) *
                (player->squareSize + LINE_GAP) + offset;
            y = LINE_GAP + row * (player->squareSize + LINE_GAP);
        }
        else
        {
            x = LINE_GAP + column * (player->squareSize + LINE_GAP) + offset;
            y = LINE_GAP + ((BOARD_SIZE - 1) - row) *
                (player->squareSize + LINE_GAP);
        }

        x -= player->squareSize / 2;

        string[1] = NULLCHAR;

        if (n > 9)
            string[0] = '*';
        else
            string[0] = '0' + n;

        XTextExtents(player->coordFontStruct, string, 1, &direction,
                     &font_ascent, &font_descent, &overall);

        if (player->monoMode)
        {
            XDrawImageString(player->xDisplay, player->xBoardWindow,
                             player->coordGC,
                             x + player->squareSize - overall.width - 2,
                             y + player->squareSize - font_descent - 1,
                             string, 1);
        }
        else
        {
            XDrawString(player->xDisplay, player->xBoardWindow,
                        player->coordGC,
                        x + player->squareSize - overall.width - 2,
                        y + player->squareSize - font_descent - 1,
                        string, 1);
        }

        if (!updateRemotePlayer || (player == &remotePlayer))
            break;
    }
}




void
UpdateCatched(int Color, int Figure, int Drop, int DropAll, int currentMove)
{
    int n, F;
    int x, y;

    /* Determine first row and column. */

    if (Color)
    {
        x = -1;
        y = BOARD_SIZE - 1;
    }
    else
    {
        x = BOARD_SIZE;
        y = 0;
    }

    if (DropAll)
        n = 0;
    else
        n = catches[currentMove][Color][Figure];

    /* Update the display for captured pieces
       if no piece of the dropped type is there (Drop && n==1)
       or if a piece type is removed (NOT Drop && n==0).
       In the other cases update only the count. */

    if (DropAll || (Drop && (n == 1)) || (!Drop && (n == 0)))
    {
        /* show all captured pieces */
        n = 0;

        for (F = pawn; F <= king; F++)
        {
            int c;

            if ((c = catches[currentMove][Color][F]) > 0)
            {
                n++;
                DrawSquare(y, x, catchedIndexToPiece[Color][F]);
                ShowCount(y, (Color ? (x - 1) : (x + 1)), c);

                if (Color) 
                    y--;
                else
                    y++;
            }
        }

        if (DropAll)
        {
            for (; n < 9; n++)
            {
                DrawSquare(y, x, EmptySquare);
                ShowCount(y, (Color ? (x - 1) : (x + 1)), 0);

                if (Color) 
                    y--;
                else
                    y++;
            }
        }
        else if (!Drop)
        {
            /* remove one line! */
            DrawSquare(y, x, EmptySquare);
            ShowCount(y, (Color ? (x - 1) : (x + 1)), 0);
        }
    }
    else
    {
        /* show the actual count */
        for (F = pawn; F <= Figure - 1; F++)
        {
            if (catches[currentMove][Color][F] > 0)
            {
                if (Color) 
                    y--;
                else 
                    y++;
            }
        }

        ShowCount(y, (Color ? (x - 1) : (x + 1)), n);
    }
}




#ifdef BLINK_COUNT

static int BlinkCount = 0;
static int BlinkRow, BlinkCol;
static ShogiSquare BlinkPiece;


void
BlinkSquareProc(void)
{
    if (BlinkCount > 0)
    {
        BlinkCount--;
        DrawSquare (BlinkRow, BlinkCol,
                    ((BlinkCount & 1) ? EmptySquare : BlinkPiece));

        if (BlinkCount > 0)
        {
            blinkSquareXID
                = XtAppAddTimeOut(appContext,
                                  150,
                                  (XtTimerCallbackProc)BlinkSquareProc,
                                  NULL);
        }
    }
    else
    {
        BlinkCount = 0;
    }
}




void
BlinkSquare(int row, int col, ShogiSquare piece)
{
    BlinkCount = 2 * BLINK_COUNT + 1;
    BlinkRow = row;
    BlinkCol = col;
    BlinkPiece = piece;
    BlinkSquareProc();
}


#endif /* BLINK_COUNT */




static int
PieceOfCatched(int color, int x, int y, int currentMove)
{
    int F, n;

    if (color)
    {
        if (x != 1) 
            return (no_piece);

        y = 8 - y;
    }
    else
    {
        if (x != 11) 
            return no_piece;
    }

    for (F = pawn, n = 0; F <= king; F++)
    {
        if (catches[currentMove][color][F] > 0)
        {
            if (n == y) 
                return F;

            n++;
        }
    }

    return no_piece;
}




/*
 * event handler for parsing user moves
 */

void
HandleUserMove(Widget w, XEvent *event)
{
    ShogiMove move_type;
    ShogiSquare from_piece;
    int to_x, to_y, fromRemotePlayer;

    if (updateRemotePlayer)
    {
        if (((w != localPlayer.boardWidget)
             && (w != remotePlayer.boardWidget))
            || (matchMode != MatchFalse))
        {
            return;
        }

        fromRemotePlayer = (w == remotePlayer.boardWidget);
    }
    else
    {
        if ((w != localPlayer.boardWidget) || (matchMode != MatchFalse))
            return;

        fromRemotePlayer = False;
    }

    player = (fromRemotePlayer ? &remotePlayer : &localPlayer);

    if (player->promotionUp)
    {
        XtPopdown(player->promotionShell);
        XtDestroyWidget(player->promotionShell);
        player->promotionUp = False;
        fromX = fromY = -1;
    }

    switch (gameMode)
    {
    case EndOfGame:
    case PlayFromGameFile:
    case TwoMachinesPlay:
        return;

    case MachinePlaysBlack:
        if (BlackOnMove(forwardMostMove))
        {
            DisplayMessage("It is not your turn", fromRemotePlayer);
            return;
        }

        break;

    case MachinePlaysWhite:
        if (!BlackOnMove(forwardMostMove))
        {
            DisplayMessage("It is not your turn", fromRemotePlayer);
            return;
        }

        break;

    case ForceMoves:
        forwardMostMove = currentMove;
        break;

    default:
        break;
    }

    if (currentMove != forwardMostMove)
    {
        DisplayMessage("Displayed position is not current",
                       fromRemotePlayer);
        return;
    }

    switch (event->type)
    {
    case ButtonPress:
        if ((fromX >= 0) || (fromY >= 0))
            return;

        if (((fromX = EventToXSquare(event->xbutton.x)) < 1)
            || (fromX > BOARD_SIZE + 2)
            || ((fromY = EventToSquare(event->xbutton.y)) < 0))
        {
            fromX = fromY = -1;
            return;
        }

        if (player->flipView)
            fromX = BOARD_SIZE + 3 - fromX;
        else
            fromY = BOARD_SIZE - 1 - fromY;

        break;

    case ButtonRelease:
        if ((fromX < 0) || (fromY < 0)) 
            return;

        if (((to_x = EventToXSquare(event->xbutton.x)) < 1)
            || (to_x > BOARD_SIZE + 2)
            || ((to_y = EventToSquare(event->xbutton.y)) < 0))
        {
            if (gameMode == EditPosition && !off_board(fromX))
            {
                fromX -= 2;
                boards[0][fromY][fromX] = EmptySquare;
                DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
                XSync(localPlayer.xDisplay, False);

                if (updateRemotePlayer)
                    XSync(remotePlayer.xDisplay, False);
            }

            fromX = fromY = -1;
            return;
        }

        if (player->flipView)
            to_x = BOARD_SIZE + 3 - to_x;
        else
            to_y = BOARD_SIZE - 1 - to_y;

        if ((fromX == to_x) && (fromY == to_y))
        {
            fromX = fromY = -1;
            return;
        }

        if (gameMode == EditPosition)
        {
            ShogiSquare piece;

            if (off_board(fromX))
            {
                /* Remove a catched piece */
                int i, c;
                c = ((fromX < 5) ^ player->flipView);
                i = PieceOfCatched(c, fromX, fromY, 0);

                if (i == no_piece)
                {
                    fromX = fromY = -1;
                    return;
                }
                else
                {
                    piece = catchedIndexToPiece[c][i];
                    catches[0][c][i]--;
                }
            }
            else
            {
                /* remove piece from board field */
                fromX -= 2;
                piece = boards[0][fromY][fromX];
                boards[0][fromY][fromX] = EmptySquare;
            }

            if (!off_board(to_x))
            {
                /* drop piece to board field */
                ShogiSquare catched_piece;
                to_x -= 2;
                catched_piece = boards[0][to_y][to_x];

                if (catched_piece != EmptySquare)
                {
                    /* put piece to catched pieces */
                    int i = pieceToCatchedIndex[catched_piece];
                    int c = (catched_piece < WhitePawn);
                    catches[0][c][i]++;
                }

                /* place moved piece */
                boards[0][to_y][to_x] = piece;
            }

            fromX = fromY = -1;
            DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
            XSync(localPlayer.xDisplay, False);

            if (updateRemotePlayer)
                XSync(remotePlayer.xDisplay, False);

            return;
        }

        if (off_board(fromX))
        {
            int c     = (BlackOnMove(forwardMostMove) ? 0 : 1);
            int piece = PieceOfCatched(c, fromX, fromY, currentMove);

            if (piece == no_piece)
            {
                fromX = fromY = -1;
                return;
            }
            else
            {
                if (updateRemotePlayer
                    && (BlackOnMove(forwardMostMove) == fromRemotePlayer))
                {
                    DisplayMessage("do not drop opponent pieces",
                                   fromRemotePlayer);
                    fromX = fromY = -1;
                    return;
                }

                fromX = fromY = piece + 81;
                to_x -= 2;
                move_type = (BlackOnMove(forwardMostMove)
                             ? BlackDrop : WhiteDrop);
                MakeMove(&move_type, fromX, fromY, to_x, to_y);

#ifdef BLINK_COUNT
                if (updateRemotePlayer)
                    BlinkSquare(to_y, to_x, boards[currentMove][to_y][to_x]);
#endif

                FinishUserMove(move_type, to_x, to_y);
                break;
            }
        }
        else if (off_board(to_x))
        {
            fromX = fromY = -1;
            return;
        }
        else
        {
            fromX -= 2;
            to_x -= 2;
            from_piece = boards[currentMove][fromY][fromX];

            if ((from_piece != EmptySquare)
                && updateRemotePlayer
                && ((from_piece < WhitePawn) == fromRemotePlayer))
            {
                DisplayMessage("do not move opponent pieces",
                               fromRemotePlayer);
                fromX = fromY = -1;
                return;
            }

            if (PromotionPossible(fromY, to_y, from_piece))
            {
                PromotionPopUp(from_piece, to_x, to_y, fromRemotePlayer);
                return;
            }

            move_type = NormalMove;
            MakeMove(&move_type, fromX, fromY, to_x, to_y);

#ifdef BLINK_COUNT
            if (updateRemotePlayer)
                BlinkSquare(to_y, to_x, boards[currentMove][to_y][to_x]);
#endif

            FinishUserMove(move_type, to_x, to_y);
            break;
        }
    }
}




void
FinishUserMove(ShogiMove move_type, int to_x, int to_y)
{
    char user_move[MSG_SIZ];

    /* output move for gnushogi */
    switch (move_type)
    {
    case BlackPromotion:
    case WhitePromotion:
        sprintf(user_move, "%c%c%c%c+\n",
                '9' - fromX, 'i' - fromY, '9' - to_x, 'i' - to_y);
        break;
        
    case BlackDrop:
    case WhiteDrop:
        sprintf(user_move, "%c*%c%c\n",
                catchedIndexToChar[fromX - 81], '9' - to_x, 'i' - to_y);
        break;
        
    case NormalMove:
        sprintf(user_move, "%c%c%c%c\n",
                '9' - fromX, 'i' - fromY, '9' - to_x, 'i' - to_y);
        break;
        
    default:
        fprintf(stderr, "%s: internal error; bad move_type\n",
                (char *)programName);
        break;
    }

    Attention(firstProgramPID);

    if (firstSendTime)
        SendTimeRemaining(toFirstProgFP);

    SendToProgram(user_move, toFirstProgFP);
    strcpy(moveList[currentMove - 1], user_move);

    fromX = fromY = -1;

    if (gameMode == PauseGame)
    {
        /* a user move restarts a paused game*/
        PauseProc(NULL, NULL, NULL, NULL);
    }

    switch (gameMode)
    {
    case ForceMoves:
        break;

    case BeginningOfGame:
        if (localPlayer.appData.noShogiProgram)
            lastGameMode = gameMode = ForceMoves;
        else
            lastGameMode = gameMode = MachinePlaysWhite;

        ModeHighlight();
        break;

    case MachinePlaysWhite:
    case MachinePlaysBlack:
    default:
        break;
    }
}




/* Simple parser for moves from gnushogi. */
void
ParseMachineMove(char *machine_move, ShogiMove *move_type,
                 int *from_x, int *from_y, int *to_x, int *to_y)
{
#define no_digit(c) (c < '0' || c > '9')
    {
        if (no_digit(machine_move[0]))
        {
            switch (machine_move[0])
            {
            case 'P': 
                *from_x = 81;
                break;

            case 'L': 
                *from_x = 82;
                break;

            case 'N': 
                *from_x = 83;
                break;

            case 'S': 
                *from_x = 84;
                break;

            case 'G': 
                *from_x = 85;
                break;

            case 'B': 
                *from_x = 86;
                break;

            case 'R': 
                *from_x = 87;
                break;

            case 'K': 
                *from_x = 88;
                break;

            default: 
                *from_x = -1;
            }

            *from_y = *from_x;
            *to_x   = '9' - machine_move[2];
            *to_y   = 'i' - machine_move[3];
        }
        else
        {
            *from_x = '9' - machine_move[0] ;
            *from_y = 'i' - machine_move[1];
            *to_x   = '9' - machine_move[2];
            *to_y   = 'i' - machine_move[3];

            switch (machine_move[4])
            {
            case '+':
                *move_type = (BlackOnMove(forwardMostMove) 
                    ? BlackPromotion : WhitePromotion);
                break;

            default:
                *move_type = NormalMove;
                break;
            }
        }
    }
}




void
SkipString(char **mpr)
{
    while (**mpr == ' ')
        (*mpr)++;

    while ((**mpr != ' ') && (**mpr != NULLCHAR) && (**mpr != '\n'))
        (*mpr)++;

    while (**mpr == ' ')
        (*mpr)++;
}




void
HandleMachineMove(char *message, FILE *fp)
{
    char machine_move[MSG_SIZ], buf1[MSG_SIZ], buf2[MSG_SIZ];
    int from_x, from_y, to_x, to_y;
    ShogiMove move_type;
    char *mpr;

#ifdef SYNCHTIME
    long time_remaining;
#endif

    maybeThinking = False;

    if (strncmp(message, "warning:", 8) == 0)
    {
        DisplayMessage(message, False);

        if (updateRemotePlayer)
            DisplayMessage(message, True);

        return;
    }

    /*
     * If shogi program startup fails, exit with an error message.
     * Attempts to recover here are futile.
     */

    if ((strstr(message, "unknown host") != NULL)
        || (strstr(message, "No remote directory") != NULL)
        || (strstr(message, "not found") != NULL)
        || (strstr(message, "No such file") != NULL)
        || (strstr(message, "Permission denied") != NULL))
    {
        fprintf(stderr,
                "%s: failed to start shogi program %s on %s: %s\n",
                programName,
                ((fp == fromFirstProgFP) 
                 ? localPlayer.appData.firstShogiProgram
                 : localPlayer.appData.secondShogiProgram),
                ((fp == fromFirstProgFP)
                 ? localPlayer.appData.firstHost
                 : localPlayer.appData.secondHost),
                message);
        ShutdownShogiPrograms(message);
        exit(1);
    }

    /*
     * If the move is illegal, cancel it and redraw the board.
     */

    if (strncmp(message, "Illegal move", 12) == 0)
    {
        if (fp == fromFirstProgFP && firstSendTime == 2)
        {
            /* First program doesn't have the "time" command */
            firstSendTime = 0;
            return;
        }
        else if (fp == fromSecondProgFP && secondSendTime == 2)
        {
            /* Second program doesn't have the "time" command */
            secondSendTime = 0;
            return;
        }

        if (forwardMostMove <= backwardMostMove) 
            return;

        if (gameMode == PauseGame) 
            PauseProc(NULL, NULL, NULL, NULL);

        if (gameMode == PlayFromGameFile)
        {
            /* Stop reading this game file */
            gameMode = ForceMoves;
            ModeHighlight();
        }

        currentMove = --forwardMostMove;

        if ((gameMode == PlayFromGameFile) 
            || (gameMode == ForceMoves))
            DisplayClocks(ReDisplayTimers);
        else
            DisplayClocks(SwitchTimers);

        sprintf(buf1, "Illegal move: %s", parseList[currentMove]);
        DisplayMessage(buf1, False);

        if (updateRemotePlayer)
            DisplayMessage(buf1, True);

#ifdef BLINK_COUNT
        /*
         * Disable blinking of the target square.
         */

        if (BlinkCount > 0)
        {
            /* If BlinkCount is even, the piece is currently displayed. */
            if (!(BlinkCount & 1))
                DrawSquare (BlinkRow, BlinkCol, EmptySquare);

            /* BlinkCount = 0 will force the next blink timeout
             * to do nothing. */
            BlinkCount = 0;
        }
#endif

        DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);

        XSync(localPlayer.xDisplay, False);

        if (updateRemotePlayer)
            XSync(remotePlayer.xDisplay, False);

        return;
    }

    if (strstr(message, "GNU Shogi") != NULL)
    {
        at_least_gnushogi_1_2p03 = True;
        return;
    }

    if (strncmp(message, "Hint:", 5) == 0)
    {
        char promoPiece;
        sscanf(message, "Hint: %s", machine_move);
        ParseMachineMove(machine_move, &move_type,
                         &from_x, &from_y, &to_x, &to_y);

        if (move_type == WhitePromotion || move_type == BlackPromotion)
            promoPiece = '+';
        else
            promoPiece = NULLCHAR;

        move_type = MakeAlg(from_x, from_y, to_x, to_y, promoPiece,
                            currentMove, buf1);
        sprintf(buf2, "Hint: %s", buf1);
        DisplayMessage(buf2, False);

        if (updateRemotePlayer)
            DisplayMessage(buf2, True);

        return;
    }

    if (strncmp(message, "Clocks:", 7) == 0)
    {
        sscanf(message, "Clocks: %ld %ld",
               &blackTimeRemaining, &whiteTimeRemaining);
        DisplayClocks(ReDisplayTimers);

        return;
    }

    /*
     * win, lose or draw
     */

    if (strncmp(message, "Black", 5) == 0)
    {
        ShutdownShogiPrograms("Black wins");
        return;
    }
    else if (strncmp(message, "White", 5) == 0)
    {
        ShutdownShogiPrograms("White wins");
        return;
    }
    else if (strncmp(message, "Repetition", 10) == 0)
    {
        ShutdownShogiPrograms("Repetition");
        return;
    }
    else if (strncmp(message, "opponent mates!", 15) == 0)
    {
        switch ((gameMode == PauseGame) ? pausePreviousMode : gameMode)
        {
        case MachinePlaysWhite:
            ShutdownShogiPrograms("Black wins");
            break;

        case MachinePlaysBlack:
            ShutdownShogiPrograms("White wins");
            break;

        case TwoMachinesPlay:
            ShutdownShogiPrograms((fp == fromFirstProgFP)
                                  ? "Black wins" : "White wins");
            break;

        default:
            /* can't happen */
            break;
        }

        return;
    }
    else if (strncmp(message, "computer mates!", 15) == 0)
    {
        switch ((gameMode == PauseGame) ? pausePreviousMode : gameMode)
        {
        case MachinePlaysWhite:
            ShutdownShogiPrograms("White wins");
            break;

        case MachinePlaysBlack:
            ShutdownShogiPrograms("Black wins");
            break;

        case TwoMachinesPlay:
            ShutdownShogiPrograms((fp == fromFirstProgFP)
                                  ? "White wins" : "Black wins");
            break;

        default:
            /* can't happen */
            break;
        }

        return;
    }
    else if (strncmp(message, "Draw", 4) == 0)
    {
        ShutdownShogiPrograms("Draw");
        return;
    }

    /*
     * normal machine reply move
     */
    maybeThinking = True;

    if (strstr(message, "...") != NULL)
    {
        sscanf(message, "%s %s %s", buf1, buf2, machine_move);

#ifdef SYNCHTIME
        mpr = message;
        SkipString(&mpr); /* skip move number */
        SkipString(&mpr); /* skip ... */
        SkipString(&mpr); /* skip move */

        if ((gameMode != TwoMachinesPlay) && (gameMode != ForceMoves)
            && ((*mpr == '-') || ((*mpr >= '0') && (*mpr <= '9'))))
        {
            /* synchronize with shogi program clock */
            sscanf(mpr, "%ld", &time_remaining);

            if (xshogiDebug)
            {
                printf("from '%s' synchronize %s clock %ld\n",
                       message, 
                       (BlackOnMove(forwardMostMove)
                        ? "Black's" 
                        : "White's"), 
                       time_remaining);
            }

            if (BlackOnMove(forwardMostMove))
                blackTimeRemaining = time_remaining;
            else
                whiteTimeRemaining = time_remaining;
        }
#endif

        if (machine_move[0] == NULLCHAR)
            return;
    }
    else
    {
        mpr = message;

#ifdef SYNCHTIME
        if (strstr(message, "time") == NULL)
        {
            /* remaining time will be determined from move */
            SkipString(&mpr); /* skip move number */
            SkipString(&mpr); /* skip move */
        }

        if ((gameMode != TwoMachinesPlay) && (gameMode != ForceMoves)
            && ((*mpr == '-') || ((*mpr >= '0') && (*mpr <= '9'))))
        {
            /* synchronize with shogi program clock */
            sscanf(mpr, "%ld", &time_remaining);

            if (xshogiDebug)
            {
                printf("from '%s' synchronize %s clock %ld\n",
                       message, 
                       ((!BlackOnMove(forwardMostMove))
                        ? "Black's" : "White's"), 
                       time_remaining);
            }

            if (!BlackOnMove(forwardMostMove))
                blackTimeRemaining = time_remaining;
            else
                whiteTimeRemaining = time_remaining;
        }
        else
#endif

            if (xshogiDebug)
                printf("ignore noise: '%s'\n", message);

        return; /* ignore noise */
    }

    strcpy(moveList[forwardMostMove], machine_move);

    ParseMachineMove(machine_move, &move_type, &from_x, &from_y,
                     &to_x, &to_y);

    if (gameMode != PauseGame)
        currentMove = forwardMostMove;  /* display latest move */

    MakeMove(&move_type, from_x, from_y, to_x, to_y);

#ifdef BLINK_COUNT
    if (gameMode != TwoMachinesPlay)
        BlinkSquare(to_y, to_x, boards[currentMove][to_y][to_x]);
#endif

    if ((gameMode != PauseGame) && localPlayer.appData.ringBellAfterMoves)
        putc(BELLCHAR, stderr);

    if ((gameMode == TwoMachinesPlay)
        || ((gameMode == PauseGame) 
            && (pausePreviousMode == TwoMachinesPlay)))
    {
        strcat(machine_move, "\n");

        if (BlackOnMove(forwardMostMove))
        {
            Attention(secondProgramPID);

            if (secondSendTime)
                SendTimeRemaining(toSecondProgFP);

            SendToProgram(machine_move, toSecondProgFP);

            if (firstMove)
            {
                firstMove = False;
                SendToProgram(localPlayer.appData.blackString,
                              toSecondProgFP);
            }
        }
        else
        {
            Attention(firstProgramPID);

            if (firstSendTime)
                SendTimeRemaining(toFirstProgFP);

            SendToProgram(machine_move, toFirstProgFP);

            if (firstMove)
            {
                firstMove = False;
                SendToProgram(localPlayer.appData.blackString,
                              toFirstProgFP);
            }
        }
    }
}




void
ReadGameFile(void)
{
    for (;;)
    {
        if (!ReadGameFileProc())
            return;

        if (matchMode == MatchOpening)
            continue;

        readGameXID
            = XtAppAddTimeOut(appContext,
                              (int)(1000 * localPlayer.appData.timeDelay),
                              (XtTimerCallbackProc) ReadGameFile, NULL);
        break;
    }
}



/*
 * FIXME: there is a naming inconsistency: here ReadGameFileProc() is
 * called by ReadGameFile() while in other places XXXProc() calls XXX().
 */

int
ReadGameFileProc(void)
{
    ShogiMove move_type;
    char move[MSG_SIZ], buf[MSG_SIZ];

    if (gameFileFP == NULL)
        return (int)False;

    if (gameMode == PauseGame) 
        return True;

    if (gameMode != PlayFromGameFile)
    {
        fclose(gameFileFP);
        gameFileFP = NULL;
        return (int)False;
    }

    if (commentUp)
    {
        XtPopdown(commentShell);
        XtDestroyWidget(commentShell);
        commentUp = False;
    }

    fgets(move, MSG_SIZ, gameFileFP);
    move[strlen(move) - 1] = NULLCHAR;
    sprintf(buf, "# %s game file", programName);

    if (strncmp(move, buf, strlen(buf)))
    {
        strcat(move, ": no xshogi game file");
        DisplayMessage(move, False);
        return (int)False;
    }

    DisplayName(move);
    rewind(gameFileFP);

    parseGameFile();

    move_type = (ShogiMove)0;

    lastGameMode = gameMode;
    gameMode = ForceMoves;
    ModeHighlight();
    DisplayMessage("End of game file", False);

    if (readGameXID != 0)
    {
        XtRemoveTimeOut(readGameXID);
        readGameXID = 0;
    }

    fclose(gameFileFP);
    gameFileFP = NULL;

    return (int)False;
}




/* 
 * Apply a move to the given board.  Oddity: move_type is ignored on input
 * unless the move is seen to be a pawn promotion, in which case move_type
 * tells us what to promote to.
 */

void
ApplyMove(ShogiMove *move_type, int from_x, int from_y,
          int to_x, int to_y, int currentMove)
{
    ShogiSquare piece, cpiece;
    char pieceChar;
    int  i, c;

    if (from_x > 80)
    {
        i = from_x - 81;
        c = (BlackOnMove(currentMove) ? 1 : 0);
        cpiece = catchedIndexToPiece[c][i];
        boards[currentMove][to_y][to_x] = cpiece;
        catches[currentMove][c][i]--;
    }
    else if (PromotionPossible(from_y, to_y,
                               piece = boards[currentMove][from_y][from_x]))
    {
        cpiece = boards[currentMove][to_y][to_x];

        if (cpiece != EmptySquare)
        {
            i = pieceToCatchedIndex[cpiece];
            c = (cpiece < WhitePawn);
            catches[currentMove][c][i]++;
        }

        if (*move_type == NormalMove)
        {
            boards[currentMove][to_y][to_x] = piece;
        }
        else
        {
            boards[currentMove][to_y][to_x] = piece = pieceToPromoted[piece];
            pieceChar = '+';
        }

        boards[currentMove][from_y][from_x] = EmptySquare;
    }
    else
    {
        ShogiSquare piece = boards[currentMove][to_y][to_x];

        if (piece != EmptySquare)
        {
            i = pieceToCatchedIndex[piece];
            c = (piece < WhitePawn);
            catches[currentMove][c][i]++;
        }

        *move_type = NormalMove;
        boards[currentMove][to_y][to_x] =
            boards[currentMove][from_y][from_x];
        boards[currentMove][from_y][from_x] = EmptySquare;
    }
}




/*
 * MakeMove() displays moves.  If they are illegal, GNU shogi will detect
 * this and send an Illegal move message.  XShogi will then retract the move.
 * The clockMode False case is tricky because it displays the player on move.
 */

void
MakeMove(ShogiMove *move_type, int from_x, int from_y, int to_x, int to_y)
{
    char message[MSG_SIZ], movestr[MSG_SIZ];
    char promoPiece = NULLCHAR;

    forwardMostMove++;

    CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
    CopyCatches(catches[forwardMostMove], catches[forwardMostMove - 1]);

    ApplyMove(move_type, from_x, from_y, to_x, to_y, forwardMostMove);

    endMessage[0] = NULLCHAR;

    timeRemaining[0][forwardMostMove] = blackTimeRemaining;
    timeRemaining[1][forwardMostMove] = whiteTimeRemaining;

    if ((gameMode == PauseGame) && (pausePreviousMode != PlayFromGameFile))
        return;

    currentMove = forwardMostMove;

    if (gameMode == PlayFromGameFile)
    {
        sprintf(message, "%d. %s%s", 
                ((currentMove + 1) / 2),
                (BlackOnMove(currentMove) ? "... " : ""), 
                currentMoveString);
        strcpy(parseList[currentMove - 1], currentMoveString);
    }
    else
    {
        if ((*move_type == WhitePromotion) || (*move_type == BlackPromotion))
            promoPiece = '+';
        else
            promoPiece = NULLCHAR;

        MakeAlg(from_x, from_y, to_x, to_y, promoPiece,
                currentMove - 1, movestr);
        sprintf(message, "%d. %s%s", 
                ((currentMove + 1) / 2),
                (BlackOnMove(currentMove) ? "... " : ""), 
                movestr);
        strcpy(parseList[currentMove - 1], movestr);
    }

    DisplayMessage(message, False);

    if ((gameMode == PlayFromGameFile) || (gameMode == ForceMoves) 
        || ((gameMode == PauseGame) 
            && (pausePreviousMode == PlayFromGameFile)))
    {
        DisplayClocks(ReDisplayTimers);
    }
    else
    {
        DisplayClocks(SwitchTimers);
    }

    DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);

    XSync(localPlayer.xDisplay, False);

    if (updateRemotePlayer)
    {
        DisplayMessage(message, True);
        XSync(remotePlayer.xDisplay, False);
    }
}




void
InitShogiProgram(char *host_name, char *program_name, int *pid,
                 FILE **to, FILE **from, XtIntervalId *xid, int *sendTime)
{
    char  arg_buf[10];
    char *arg1, *arg2;
    int   to_prog[2], from_prog[2];
    FILE *from_fp, *to_fp;
    int   dummy_source;
    XtInputId dummy_id;

    if (localPlayer.appData.noShogiProgram) 
        return;

    signal(SIGPIPE, CatchPipeSignal);
    pipe(to_prog);
    pipe(from_prog);

    if ((*pid = fork()) == 0)
    {
        signal(SIGPIPE, CatchPipeSignal);

        dup2(to_prog[0], 0);
        dup2(from_prog[1], 1);
        close(to_prog[0]);
        close(to_prog[1]);
        close(from_prog[0]);
        close(from_prog[1]);
        dup2(1, fileno(stderr));    /* force stderr to the pipe */

        if (localPlayer.appData.searchTime != NULL)
        {
            sprintf(arg_buf, "%d", searchTime);
            arg1 = arg_buf;
            arg2 = (char *)NULL;
        }
        else if (localPlayer.appData.searchDepth > 0)
        {
            sprintf(arg_buf, "%d", localPlayer.appData.searchDepth);
            arg1 = "1";
            arg2 = "9999";
        }
        else
        {
            sprintf(arg_buf, "%d", localPlayer.appData.movesPerSession);
            arg1 = arg_buf;
            arg2 = localPlayer.appData.timeControl;
        }

        if (strcmp(host_name, "localhost") == 0)
        {
            execlp(program_name, program_name, arg1, arg2,
                   (char *)NULL);
        }
        else
        {
            execlp(localPlayer.appData.remoteShell,
                   localPlayer.appData.remoteShell,
                   host_name, program_name, arg1, arg2,
                   (char *)NULL);
        }

        perror(program_name);
        exit(1);
    }

    close(to_prog[0]);
    close(from_prog[1]);

    *from = from_fp = fdopen(from_prog[0], "r");
    *to   = to_fp   = fdopen(to_prog[1],   "w");
    setbuf(from_fp, NULL);
    setbuf(to_fp,   NULL);

    ReceiveFromProgram(from_fp, &dummy_source, &dummy_id); /* "GNU Shogi"*/

    if (!at_least_gnushogi_1_2p03)
    {
        fprintf(stderr, "you must have at least gnushogi-1.2p03\n");
        exit(1);
    }

    if (*pid == 0)
        return;

    *xid = XtAppAddInput(appContext, fileno(from_fp),
                         (XtPointer)XtInputReadMask,
                         (XtInputCallbackProc)ReceiveFromProgram,
                         (XtPointer)from_fp);

    SendToProgram(localPlayer.appData.initString, *to);

    if (localPlayer.appData.gameIn)
        SendToProgram("gamein\n", *to);

    SendSearchDepth(*to);

    if (*sendTime == 2)
    {
        /* Does program have "time" command? */
        char buf[MSG_SIZ];

        sprintf(buf, "time %ld\n", blackTimeRemaining / 10);
        SendToProgram(buf, to_fp);
        ReceiveFromProgram(from_fp, &dummy_source, &dummy_id);

        if (*sendTime == 2)
        {
            *sendTime = 1;  /* yes! */
            sprintf(buf, "otime %ld\n", whiteTimeRemaining / 10);
            SendToProgram(buf, to_fp);
            ReceiveFromProgram(from_fp, &dummy_source, &dummy_id);
        }
    }
}




void
ShutdownShogiPrograms(char *why)
{
    lastGameMode = gameMode;
    gameMode = EndOfGame;
    ModeHighlight();
    CopyBoard(boards[currentMove + 1], boards[currentMove]);
    CopyCatches(catches[currentMove + 1], catches[currentMove]);
    strncpy(parseList[currentMove], why, MOVE_LEN);
    parseList[currentMove][MOVE_LEN - 1] = NULLCHAR;
    currentMove++;
    DisplayMessage(why, False);

    if (readGameXID != 0)
        XtRemoveTimeOut(readGameXID);

    readGameXID = 0;

    if (firstProgramPID != 0)
    {
        fclose(fromFirstProgFP);
        fclose(toFirstProgFP);
        fromFirstProgFP = toFirstProgFP = NULL;

        if (kill(firstProgramPID, SIGTERM) == 0)
            WAIT0;
    }

    firstProgramPID = 0;

    if (firstProgramXID != 0)
        XtRemoveInput(firstProgramXID);

    firstProgramXID = 0;

    if (secondProgramPID != 0)
    {
        fclose(fromSecondProgFP);
        fclose(toSecondProgFP);
        fromSecondProgFP = toSecondProgFP = NULL;

        if (kill(secondProgramPID, SIGTERM) == 0)
            WAIT0;
    }

    secondProgramPID = 0;

    if (secondProgramXID != 0)
        XtRemoveInput(secondProgramXID);

    secondProgramXID = 0;

    DisplayClocks(StopTimers);

    if (matchMode != MatchFalse)
    {
        if (localPlayer.appData.saveGameFile[0] != NULLCHAR)
            SaveGame(localPlayer.appData.saveGameFile);

        exit(0);
    }
}




void
CommentPopUp(char *label)
{
    Arg args[2];
    Position x, y;
    Dimension bw_width, pw_width;

    if (commentUp)
    {
        XtPopdown(commentShell);
        XtDestroyWidget(commentShell);
        commentUp = False;
    }

    DisplayMessage("Comment", False);

    XtSetArg(args[0], XtNwidth, &bw_width);
    XtGetValues(localPlayer.formWidget, args, 1);

    XtSetArg(args[0], XtNresizable, True);
    XtSetArg(args[1], XtNwidth, bw_width - 8);

    commentShell = XtCreatePopupShell("Comment",
                                      transientShellWidgetClass,
                                      localPlayer.commandsWidget, args, 2);

    XtSetArg(args[0], XtNlabel, label);

    (void)XtCreateManagedWidget("commentLabel", labelWidgetClass,
                                commentShell, args, 1);

    XtRealizeWidget(commentShell);

    XtSetArg(args[0], XtNwidth, &pw_width);
    XtGetValues(commentShell, args, 1);

    XtTranslateCoords(localPlayer.shellWidget,
                      (bw_width - pw_width) / 2, -50, &x, &y);

    XtSetArg(args[0], XtNx, x);
    XtSetArg(args[1], XtNy, y);
    XtSetValues(commentShell, args, 2);

    XtPopup(commentShell, XtGrabNone);
    commentUp = True;
}




void
FileNamePopUp(char *label, Boolean (*proc) (char *))
{
    Arg args[2];
    Widget popup, dialog;
    Position x, y;
    Dimension bw_width, pw_width;

    fileProc = proc;

    XtSetArg(args[0], XtNwidth, &bw_width);
    XtGetValues(localPlayer.boardWidget, args, 1);

    XtSetArg(args[0], XtNresizable, True);
    XtSetArg(args[1], XtNwidth, DIALOG_SIZE);

    popup = XtCreatePopupShell("File Name Prompt",
                               transientShellWidgetClass,
                               localPlayer.commandsWidget, args, 2);

    XtSetArg(args[0], XtNlabel, label);
    XtSetArg(args[1], XtNvalue, "");

    dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
                                   popup, args, 2);

    XawDialogAddButton(dialog, "ok", FileNameCallback, (XtPointer) dialog);
    XawDialogAddButton(dialog, "cancel", FileNameCallback,
                       (XtPointer) dialog);

    XtRealizeWidget(popup);

    XtSetArg(args[0], XtNwidth, &pw_width);
    XtGetValues(popup, args, 1);

    XtTranslateCoords(localPlayer.boardWidget,
                      (bw_width - pw_width) / 2, 10, &x, &y);

    XtSetArg(args[0], XtNx, x);
    XtSetArg(args[1], XtNy, y);
    XtSetValues(popup, args, 2);

    XtPopup(popup, XtGrabExclusive);
    filenameUp = True;

    XtSetKeyboardFocus(localPlayer.shellWidget, popup);
}




void
FileNameCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
    String name;
    Arg args[1];

    XtSetArg(args[0], XtNlabel, &name);
    XtGetValues(w, args, 1);

    if (strcmp(name, "cancel") == 0)
    {
        XtPopdown(w = XtParent(XtParent(w)));
        XtDestroyWidget(w);
        filenameUp = False;
        ModeHighlight();
        return;
    }

    FileNameAction(w, NULL, NULL, NULL);
}




void
FileNameAction(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    char buf[MSG_SIZ];
    String name;

    name = XawDialogGetValueString(w = XtParent(w));

    if ((name != NULL) && (*name != NULLCHAR))
    {
        strcpy(buf, name);
        XtPopdown(w = XtParent(w));
        XtDestroyWidget(w);
        filenameUp = False;
        (*fileProc)(buf);  /* I can't see a way not
                              to use a global here */
        ModeHighlight();
        return;
    }

    XtPopdown(w = XtParent(w));
    XtDestroyWidget(w);
    filenameUp = False;
    ModeHighlight();
}




void
PromotionPopUp(ShogiSquare piece, int to_x, int to_y, int fromRemotePlayer)
{
    Arg args[2];
    Widget dialog;
    Position x, y;
    Dimension bw_width, bw_height, pw_width, pw_height;

    player = (fromRemotePlayer ? &remotePlayer : &localPlayer);

    pmi.piece = piece;
    pmi.to_x = to_x;
    pmi.to_y = to_y;

    XtSetArg(args[0], XtNwidth, &bw_width);
    XtSetArg(args[1], XtNheight, &bw_height);
    XtGetValues(player->boardWidget, args, 2);

    XtSetArg(args[0], XtNresizable, True);

    player->promotionShell
        = XtCreatePopupShell("Promotion",
                             transientShellWidgetClass,
                             player->commandsWidget, args, 1);

    XtSetArg(args[0], XtNlabel, "Promote piece?");
    dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
                                   player->promotionShell, args, 1);

    XawDialogAddButton(dialog, "Yes", PromotionCallback,
                       (XtPointer) dialog);
    XawDialogAddButton(dialog, "No", PromotionCallback,
                       (XtPointer) dialog);
    XawDialogAddButton(dialog, "cancel", PromotionCallback,
                       (XtPointer) dialog);

    XtRealizeWidget(player->promotionShell);

    XtSetArg(args[0], XtNwidth, &pw_width);
    XtSetArg(args[1], XtNheight, &pw_height);
    XtGetValues(player->promotionShell, args, 2);

    XtTranslateCoords(player->boardWidget, 
                      ((bw_width - pw_width) / 2),
                      (LINE_GAP 
                       + player->squareSize / 3 
                       + (((piece == BlackPawn) ^ (player->flipView)) 
                          ? 0 
                          : (6 * (player->squareSize + LINE_GAP)))),
                      &x, &y);

    XtSetArg(args[0], XtNx, x);
    XtSetArg(args[1], XtNy, y);
    XtSetValues(player->promotionShell, args, 2);

    XtPopup(player->promotionShell, XtGrabNone);

    player->promotionUp = True;
}




void
PromotionCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
    String name;
    Arg args[1];
    ShogiMove move_type;
    struct DisplayData *player;

    XtSetArg(args[0], XtNlabel, &name);
    XtGetValues(w, args, 1);

    w = XtParent(XtParent(w));
    player = ((w == remotePlayer.promotionShell)
              ? &remotePlayer : &localPlayer);
    XtPopdown(w);
    XtDestroyWidget(w);
    player->promotionUp = False;

    if (fromX == -1) 
        return;

    if (strcmp(name, "Yes") == 0)
    {
        if ((int)pmi.piece < (int)WhitePawn)
            move_type = BlackPromotion;
        else
            move_type = WhitePromotion;
    }
    else if (strcmp(name, "No") == 0)
    {
        move_type = NormalMove;
    }
    else /* strcmp(name, "cancel") == 0 */
    {
        fromX = fromY = -1;
        return;
    }

    MakeMove(&move_type, fromX, fromY, pmi.to_x, pmi.to_y);

#ifdef BLINK_COUNT
    if (updateRemotePlayer)
    {
        BlinkSquare(pmi.to_y, pmi.to_x,
                    boards[currentMove][pmi.to_y][pmi.to_x]);
    }
#endif

    FinishUserMove(move_type, pmi.to_x, pmi.to_y);
}




void
FileModePopUp(char *name)
{
    Arg args[2];
    Widget dialog;
    Position x, y;
    Dimension bw_width, bw_height, pw_width, pw_height;

    struct DisplayData *player = &localPlayer;

    strcpy(fmi.name, name);

    XtSetArg(args[0], XtNwidth, &bw_width);
    XtSetArg(args[1], XtNheight, &bw_height);
    XtGetValues(player->boardWidget, args, 2);

    XtSetArg(args[0], XtNresizable, True);
    player->filemodeShell
        = XtCreatePopupShell("FileMode",
                             transientShellWidgetClass,
                             player->commandsWidget, args, 1);

    XtSetArg(args[0], XtNlabel, "Append to existing file?");
    dialog = XtCreateManagedWidget("filemode", dialogWidgetClass,
                                   player->filemodeShell, args, 1);

    XawDialogAddButton(dialog, "Yes", FileModeCallback,
                       (XtPointer) dialog);
    XawDialogAddButton(dialog, "No", FileModeCallback,
                       (XtPointer) dialog);
    XawDialogAddButton(dialog, "cancel", FileModeCallback,
                       (XtPointer) dialog);

    XtRealizeWidget(player->filemodeShell);

    XtSetArg(args[0], XtNwidth, &pw_width);
    XtSetArg(args[1], XtNheight, &pw_height);
    XtGetValues(player->filemodeShell, args, 2);

    XtTranslateCoords(player->boardWidget, (bw_width - pw_width) / 2,
                      LINE_GAP + player->squareSize/3 +
                      (6*(player->squareSize + LINE_GAP)),
                      &x, &y);

    XtSetArg(args[0], XtNx, x);
    XtSetArg(args[1], XtNy, y);
    XtSetValues(player->filemodeShell, args, 2);

    XtPopup(player->filemodeShell, XtGrabNone);

    filemodeUp = True;
}




void
FileModeCallback(Widget w, XtPointer client_data, XtPointer call_data)
{
    String name;
    Arg args[1];

    XtSetArg(args[0], XtNlabel, &name);
    XtGetValues(w, args, 1);

    XtPopdown(w = XtParent(XtParent(w)));
    XtDestroyWidget(w);

    if (strcmp(name, "Yes") == 0)
    {
        strcpy(fmi.mode, "a");
    }
    else if (strcmp(name, "No") == 0)
    {
        strcpy(fmi.mode, "w");
    }
    else /* strcmp(name, "cancel") == 0 */
    {
        filemodeUp = False;
        return;
    }

    XtPopdown(localPlayer.filemodeShell);
    XtDestroyWidget(localPlayer.filemodeShell);

    SaveGame(fmi.name);

    filemodeUp = False;
}




void
SelectCommand(Widget w, XtPointer client_data, XtPointer call_data)
{
    Cardinal fromRemotePlayer = (Cardinal)client_data;

    XawListReturnStruct *list_return = XawListShowCurrent(w);

    player = fromRemotePlayer ? &remotePlayer : &localPlayer;

    fromX = fromY = -1;

    if (player->promotionUp)
    {
        XtPopdown(player->promotionShell);
        XtDestroyWidget(player->promotionShell);
        player->promotionUp = False;
    }

    (*buttonProcs[list_return->list_index])
        (w, NULL, NULL, &fromRemotePlayer);

    if (!filenameUp) 
        ModeHighlight();
}




void
HighlightProcButton(XtActionProc proc)
{
    int i = 0;

    if (proc == NULL)
    {
        XawListUnhighlight(localPlayer.commandsWidget);

        if (updateRemotePlayer)
            XawListUnhighlight(remotePlayer.commandsWidget);

        return;
    }

    for (;;)
    {
        if (buttonProcs[i] == NULL)
        {
            XawListUnhighlight(localPlayer.commandsWidget);

            if (updateRemotePlayer)
                XawListUnhighlight(remotePlayer.commandsWidget);

            return;
        }

        if (buttonProcs[i] == proc)
        {
            XawListHighlight(localPlayer.commandsWidget, i);

            if (updateRemotePlayer)
                XawListHighlight(remotePlayer.commandsWidget, i);

            return;
        }

        i++;
    }
}




void
ModeHighlight(void)
{
    switch (gameMode)
    {
    case BeginningOfGame:
        if (localPlayer.appData.noShogiProgram)
            HighlightProcButton(ForceProc);
        else
            HighlightProcButton(MachineBlackProc);

        break;

    case MachinePlaysBlack:
        HighlightProcButton(MachineBlackProc);
        break;

    case MachinePlaysWhite:
        HighlightProcButton(MachineWhiteProc);
        break;

    case TwoMachinesPlay:
        HighlightProcButton(TwoMachinesProc);
        break;

    case ForceMoves:
        HighlightProcButton(ForceProc);

        break;

    case PlayFromGameFile:
        HighlightProcButton(LoadGameProc);
        break;

    case PauseGame:
        HighlightProcButton(PauseProc);
        break;

    case EditPosition:
        HighlightProcButton(EditPositionProc);
        break;

    case EndOfGame:
    default:
        HighlightProcButton(NULL);
        break;
    }
}




/*
 * Button procedures
 */

void
QuitRemotePlayerProc(void)
{
    /* This should be modified... */
    XCloseDisplay(remotePlayer.xDisplay);
    /* XtDestroyWidget(remotePlayer.shellWidget); */
    updateRemotePlayer = False;
    DisplayMessage("Remote player has pressed Quit", False);
    fromX = fromY = -1;
}



void
QuitProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    if (updateRemotePlayer)
        QuitRemotePlayerProc();

    ShutdownShogiPrograms("Quitting");
    exit(0);
}



void
LoadGameProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int fromRemotePlayer = *nprms;

    if (fromRemotePlayer)
    {
        DisplayMessage("only opponent may load game", fromRemotePlayer);
        return;
    }

    if (gameMode != BeginningOfGame)
    {
        DisplayMessage("Press Reset first", False);
        return;
    }

    if (localPlayer.appData.loadGameFile == NULL)
        FileNamePopUp("Game file name?", LoadGame);
    else
        (void) LoadGame(localPlayer.appData.loadGameFile);
}




Boolean
LoadGame(char *name)
{
    char buf[MSG_SIZ];

    if (gameMode != BeginningOfGame)
    {
        DisplayMessage("Press Reset first", False);
        return (int)False;
    }

    if (localPlayer.appData.loadGameFile != name)
    {
        if (localPlayer.appData.loadGameFile)
            XtFree(localPlayer.appData.loadGameFile);

        localPlayer.appData.loadGameFile = XtMalloc(strlen(name) + 1);
        strcpy(localPlayer.appData.loadGameFile, name);
    }

    if ((gameFileFP = fopen(name, "r")) == NULL)
    {
        sprintf(buf, "Can't open %s", name);
        DisplayMessage(buf, False);
        XtFree(localPlayer.appData.loadGameFile);
        localPlayer.appData.loadGameFile = NULL;
        return (int)False;
    }

    lastGameMode = gameMode = PlayFromGameFile;
    ModeHighlight();
    InitPosition(True);
    DisplayClocks(StopTimers);

    if (firstProgramXID == 0)
    {
        InitShogiProgram(localPlayer.appData.firstHost,
                         localPlayer.appData.firstShogiProgram,
                         &firstProgramPID, &toFirstProgFP,
                         &fromFirstProgFP, &firstProgramXID,
                         &firstSendTime);
    }

    SendToProgram(localPlayer.appData.initString, toFirstProgFP);
    SendSearchDepth(toFirstProgFP);
    SendToProgram("force\n", toFirstProgFP);

    currentMove = forwardMostMove = backwardMostMove = 0;

    ReadGameFile();

    return True;
}




/* 
 * Restart the shogi program and feed it all the moves made so far.
 * Used when the user wants to back up from end of game, when gnushogi
 * has already exited.  Assumes gameMode == EndOfGame. 
 */

void
ResurrectShogiProgram(void)
{
    char buf[MSG_SIZ];
    int i;

    if (currentMove > 0)
        currentMove--;  /* delete "Black wins" or the like */

    InitShogiProgram(localPlayer.appData.firstHost,
                     localPlayer.appData.firstShogiProgram,
                     &firstProgramPID, &toFirstProgFP, &fromFirstProgFP,
                     &firstProgramXID, &firstSendTime);

    SendToProgram(localPlayer.appData.initString, toFirstProgFP);
    SendSearchDepth(toFirstProgFP);
    SendToProgram("force\n", toFirstProgFP);
    gameMode = lastGameMode = ForceMoves;
    ModeHighlight();

    i = (whitePlaysFirst ? 1 : 0);

    if (startedFromSetupPosition)
        SendBoard(toFirstProgFP, boards[i], catches[i]);

    for (; i < currentMove; i++)
    {
        strcpy(buf, moveList[i]);
        SendToProgram(buf, toFirstProgFP);
    }

    if (!firstSendTime)
    {
        /* can't tell gnushogi what its clock should read,
           so we bow to its notion. */
        DisplayClocks(ResetTimers);
        timeRemaining[0][currentMove] = blackTimeRemaining;
        timeRemaining[1][currentMove] = whiteTimeRemaining;
    }
}




void
MachineWhiteProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int fromRemotePlayer = *nprms;

    if (updateRemotePlayer)
    {
        DisplayMessage("no machine moves in challenge mode",
                       fromRemotePlayer);

        return;
    }

    if (gameMode == PauseGame)
        PauseProc(w, event, prms, nprms);

    if (gameMode == PlayFromGameFile)
        ForceProc(w, event, prms, nprms);

    if (gameMode == EditPosition)
        EditPositionDone();

    if ((gameMode == EndOfGame)
        || (gameMode == PlayFromGameFile)
        || (gameMode == TwoMachinesPlay)
        || localPlayer.appData.noShogiProgram
        || (gameMode == MachinePlaysWhite))
    {
        return;
    }

    if (BlackOnMove((gameMode == ForceMoves)
                    ? currentMove 
                    : forwardMostMove))
    {
        DisplayMessage("It is not White's turn", False);
        return;
    }

    if (gameMode == ForceMoves) 
        forwardMostMove = currentMove;

    lastGameMode = gameMode = MachinePlaysWhite;
    ModeHighlight();
    SendToProgram(localPlayer.appData.whiteString, toFirstProgFP);
    DisplayClocks(StartTimers);
}




void
MachineBlackProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int fromRemotePlayer = *nprms;

    if (updateRemotePlayer)
    {
        DisplayMessage("no machine moves in challenge mode",
                       fromRemotePlayer);
        return;
    }

    if (gameMode == PauseGame)
        PauseProc(w, event, prms, nprms);

    if (gameMode == PlayFromGameFile)
        ForceProc(w, event, prms, nprms);

    if (gameMode == EditPosition)
        EditPositionDone();

    if ((gameMode == EndOfGame)
        || (gameMode == PlayFromGameFile)
        || (gameMode == TwoMachinesPlay)
        || localPlayer.appData.noShogiProgram
        || (gameMode == MachinePlaysBlack))
    {
        return;
    }

    if (!BlackOnMove((gameMode == ForceMoves)
                     ? currentMove 
                     : forwardMostMove))
    {
        DisplayMessage("It is not Black's turn", False);
        return;
    }

    if (gameMode == ForceMoves) 
        forwardMostMove = currentMove;

    lastGameMode = gameMode = MachinePlaysBlack;
    ModeHighlight();
    SendToProgram(localPlayer.appData.blackString, toFirstProgFP);
    DisplayClocks(StartTimers);
}




void
ForwardProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    char buf[MSG_SIZ];
    int target;
    unsigned int state;

    int fromRemotePlayer = *nprms;

    if (updateRemotePlayer)
    {
        DisplayMessage("Forward button disabled", fromRemotePlayer);
        return;
    }

    if ((gameMode == EndOfGame) || (gameMode == EditPosition))
        return;

    if (gameMode == PlayFromGameFile)
        PauseProc(w, event, prms, nprms);

    if (currentMove >= forwardMostMove)
        return;

    if (event == NULL)
    {
        /* Kludge */
        Window root, child;
        int root_x, root_y;
        int win_x, win_y;
        XQueryPointer(localPlayer.xDisplay, localPlayer.xBoardWindow,
                      &root, &child, &root_x, &root_y,
                      &win_x, &win_y, &state);
    }
    else
    {
        state = event->xkey.state;
    }

    if (state & ShiftMask)
        target = forwardMostMove;
    else
        target = currentMove + 1;

    if (gameMode == ForceMoves)
    {
        while (currentMove < target)
        {
            strcpy(buf, moveList[currentMove++]);
            SendToProgram(buf, toFirstProgFP);
        }
    }
    else
    {
        currentMove = target;
    }

    if (gameMode == ForceMoves)
    {
        blackTimeRemaining = timeRemaining[0][currentMove];
        whiteTimeRemaining = timeRemaining[1][currentMove];
    }

    DisplayClocks(ReDisplayTimers);
    DisplayMove(currentMove - 1);
    DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
}




void
ResetFileProc(void)
{
    char *buf = "";

    if (updateRemotePlayer)
        return;

    if (localPlayer.appData.loadGameFile)
        XtFree(localPlayer.appData.loadGameFile);

    if (localPlayer.appData.loadPositionFile)
        XtFree(localPlayer.appData.loadPositionFile);

    localPlayer.appData.loadGameFile
        = localPlayer.appData.loadPositionFile = NULL;
    DisplayName(buf);

    if (gameFileFP != NULL)
    {
        fclose(gameFileFP);
        gameFileFP = NULL;
    }
}




void
ResetChallenge(void)
{
    char *buf = "";

    if (localPlayer.appData.challengeDisplay)
        XtFree(localPlayer.appData.challengeDisplay);

    localPlayer.appData.challengeDisplay = NULL;
    DisplayName(buf);
}




void
ResetProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int fromRemotePlayer = *nprms;

    if (fromRemotePlayer)
    {
        DisplayMessage("only your opponent may reset the game",
                       fromRemotePlayer);
        return;
    }

    Reset(True);
}




void
Reset(int redraw)  /* Boolean */
{
    ResetFileProc();
    ResetChallenge();

    localPlayer.flipView = False;
    remotePlayer.flipView = True;
    startedFromSetupPosition = whitePlaysFirst = False;
    matchMode = MatchFalse;
    firstMove = True;
    blackFlag = whiteFlag = False;
    maybeThinking = False;

    endMessage[0] = NULLCHAR;

    ShutdownShogiPrograms("");
    lastGameMode = gameMode = BeginningOfGame;
    ModeHighlight();
    InitPosition(redraw);
    DisplayClocks(ResetTimers);
    timeRemaining[0][0] = blackTimeRemaining;
    timeRemaining[1][0] = whiteTimeRemaining;
    InitShogiProgram(localPlayer.appData.firstHost,
                     localPlayer.appData.firstShogiProgram,
                     &firstProgramPID, &toFirstProgFP,
                     &fromFirstProgFP, &firstProgramXID,
                     &firstSendTime);

    if (commentUp)
    {
        XtPopdown(commentShell);
        XtDestroyWidget(commentShell);
        commentUp = False;
    }

    if (localPlayer.promotionUp)
    {
        XtPopdown(localPlayer.promotionShell);
        XtDestroyWidget(localPlayer.promotionShell);
        localPlayer.promotionUp = False;
    }

    if (updateRemotePlayer && remotePlayer.promotionUp)
    {
        XtPopdown(remotePlayer.promotionShell);
        XtDestroyWidget(remotePlayer.promotionShell);
        remotePlayer.promotionUp = False;
    }
}




void
ClearCatches(int (*catches)[8])
{
    int c, p;

    for (c = 0; c <= 1; c++)
        for (p = 0; p <= 7; p++)
            catches[c][p] = 0;
}




Boolean
Challenge(char *name)
{
    char buf[MSG_SIZ];
    int argc;
    char **argv;
    XrmDatabase database;

    if (gameMode != BeginningOfGame)
    {
        DisplayMessage("Press Reset first", False);
        return (int)False;
    }

    if (localPlayer.appData.challengeDisplay != name)
    {
        if (localPlayer.appData.challengeDisplay)
            XtFree(localPlayer.appData.challengeDisplay);

        localPlayer.appData.challengeDisplay = XtMalloc(strlen(name) + 1);
        strcpy(localPlayer.appData.challengeDisplay, name);
    }

    sprintf(buf, "trying to connect to %s.....", name);
    DisplayMessage(buf, False);

    argc = global_argc;
    argv = global_argv;

    if ((remotePlayer.xDisplay
         = XtOpenDisplay(appContext, name, "XShogi",
                         "XShogi", 0, 0, &argc, argv)) == NULL)
    {
        sprintf(buf, "Can't open display %s", name);
        DisplayMessage(buf, False);
        XtFree(localPlayer.appData.challengeDisplay);
        localPlayer.appData.challengeDisplay = NULL;
        return (int)False;
    }

    DisplayMessage("connected! creating remote window...", False);

    remotePlayer.xScreen = DefaultScreen(remotePlayer.xDisplay);

    remotePlayer.shellWidget
        = XtAppCreateShell(NULL, "XShogi",
                           applicationShellWidgetClass,
                           remotePlayer.xDisplay, NULL, 0);

    database = XtDatabase(remotePlayer.xDisplay);

    XrmParseCommand(&database,
                    shellOptions, XtNumber(shellOptions),
                    "XShogi", &argc, argv);

    XtGetApplicationResources(remotePlayer.shellWidget,
                              &remotePlayer.appData, clientResources,
                              XtNumber(clientResources), NULL, 0);

    player = &remotePlayer;

    CreatePlayerWindow();

    updateRemotePlayer = True;

    DisplayName("REMOTE");
    DrawPosition(remotePlayer.boardWidget, NULL, NULL, NULL);
    DisplayClocks(ReDisplayTimers);

    DisplayMessage("ready to play", False);
    DisplayMessage("ready to play", True);

    return True;
}




void
ChallengeProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int fromRemotePlayer = *nprms;

    if (updateRemotePlayer)
    {
        DisplayMessage("you are already in challenge mode",
                       fromRemotePlayer);
        return;
    }

    if (gameMode != BeginningOfGame)
    {
        DisplayMessage("Press Reset first", False);
        return;
    }

    if (localPlayer.appData.challengeDisplay == NULL)
        FileNamePopUp("Challenge display?", Challenge);
    else
        (void) Challenge(localPlayer.appData.challengeDisplay);
}




Boolean
SelectLevel(char *command)
{
    char buf[MSG_SIZ];

    sprintf(buf, "level %s\n", command);
    SendToProgram(buf, toFirstProgFP);

    return True;
}




void
SelectLevelProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    if ((BlackOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack))
        || (!BlackOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite)))
    {
        DisplayMessage("Wait until your turn", False);
    }
    else
    {
        FileNamePopUp("#moves #minutes", SelectLevel);
    }
}




void
MoveNowProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    if ((!BlackOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack))
        || (BlackOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite)))
    {
        DisplayMessage("Wait until machines turn", False);
    }
    else
    {
        Attention(firstProgramPID);
    }
}




void
LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int fromRemotePlayer = *nprms;

    if (fromRemotePlayer)
    {
        DisplayMessage("only opponent may load position", fromRemotePlayer);
        return;
    }

    if (gameMode != BeginningOfGame)
    {
        DisplayMessage("Press Reset first", False);
        return;
    }

    FileNamePopUp("Position file name?", LoadPosition);
}




Boolean
LoadPosition(char *name)
{
    char *p, line[MSG_SIZ], buf[MSG_SIZ];
    Board initial_position;
    Catched initial_catches;
    FILE *fp;
    int i, j;

    if (gameMode != BeginningOfGame)
    {
        DisplayMessage("Press Reset first", False);
        return False;
    }

    if (localPlayer.appData.loadPositionFile != name)
    {
        if (localPlayer.appData.loadPositionFile)
            XtFree(localPlayer.appData.loadPositionFile);

        localPlayer.appData.loadPositionFile = XtMalloc(strlen(name) + 1);
        strcpy(localPlayer.appData.loadPositionFile, name);
    }

    if ((fp = fopen(name, "r")) == NULL)
    {
        sprintf(buf, "Can't open %s", name);
        DisplayMessage(buf, False);
        XtFree(localPlayer.appData.loadPositionFile);
        localPlayer.appData.loadPositionFile = NULL;
        return False;
    }

    lastGameMode = gameMode = ForceMoves;
    ModeHighlight();
    startedFromSetupPosition = True;

    if (firstProgramXID == 0)
    {
        InitShogiProgram(localPlayer.appData.firstHost,
                         localPlayer.appData.firstShogiProgram,
                         &firstProgramPID, &toFirstProgFP,
                         &fromFirstProgFP, &firstProgramXID,
                         &firstSendTime);
    }

    /*
     * Check and skip header information in position file.
     */

    fgets(line, MSG_SIZ, fp);
    line[strlen(line) - 1] = NULLCHAR;
    sprintf(buf, "# %s position file", programName);

    if (strncmp(line, buf, strlen(buf)))
    {
        strcat(line, ": no xshogi position file");
        DisplayMessage(line, False);
        return False;
    }

    DisplayName(line);
    fgets(line, MSG_SIZ, fp); /* skip opponents */

    for (i = BOARD_SIZE - 1; i >= 0; i--)
    {
        fgets(line, MSG_SIZ, fp);

        for (p = line, j = 0; j < BOARD_SIZE; p++)
        {
            int promoted = False;  /* CHECKME: is this valid? */

            if (*p == '+')
                promoted = True;

            if (*p == ' ')
                promoted = False;

            p++;
            initial_position[i][j++] = CharToPiece(*p, promoted);
        }
    }

    {
        int color;

        for (color = 0; color <= 1; color++)
        {
            fscanf(fp, "%i%i%i%i%i%i%i%i\n",
                   &initial_catches[color][pawn],
                   &initial_catches[color][lance],
                   &initial_catches[color][knight],
                   &initial_catches[color][silver],
                   &initial_catches[color][gold],
                   &initial_catches[color][bishop],
                   &initial_catches[color][rook],
                   &initial_catches[color][king]);
        }
    }

    whitePlaysFirst = False;

    if (!feof(fp))
    {
        fgets(line, MSG_SIZ, fp);

        if (strncmp(line, "white", strlen("white")) == 0)
            whitePlaysFirst = True;
    }

    fclose(fp);

    if (whitePlaysFirst)
    {
        CopyBoard(boards[0], initial_position);
        CopyCatches(catches[0], initial_catches);
        strcpy(moveList[0], " ...\n");
        strcpy(parseList[0], " ...\n");
        currentMove = forwardMostMove = backwardMostMove = 1;
        CopyBoard(boards[1], initial_position);
        CopyCatches(catches[1], initial_catches);
        SendToProgram("white\n", toFirstProgFP);
        SendToProgram("force\n", toFirstProgFP);
        SendCurrentBoard(toFirstProgFP);
        DisplayMessage("White to play", False);
    }
    else
    {
        currentMove = forwardMostMove = backwardMostMove = 0;
        CopyBoard(boards[0], initial_position);
        CopyCatches(catches[0], initial_catches);
        SendCurrentBoard(toFirstProgFP);
        SendToProgram("force\n", toFirstProgFP);
        DisplayMessage("Black to play", False);
    }

    DisplayClocks(ResetTimers);
    timeRemaining[0][1] = blackTimeRemaining;
    timeRemaining[1][1] = whiteTimeRemaining;

    DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
    return True;
}




void
EditPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int fromRemotePlayer = *nprms;

    if (updateRemotePlayer)
    {
        DisplayMessage("Edit button disabled", fromRemotePlayer);
        return;
    }

    if (gameMode == EditPosition) 
        return;

    ForceProc(w, event, prms, nprms);

    if (gameMode != ForceMoves) 
        return;

    DisplayName("<-- Press to set side to play next");
    DisplayMessage("Mouse: 1=drag, 2=black, 3=white", False);

    lastGameMode = gameMode = EditPosition;
    ModeHighlight();

    if (currentMove > 0)
        CopyBoard(boards[0], boards[currentMove]);

    whitePlaysFirst = !BlackOnMove(forwardMostMove);
    currentMove = forwardMostMove = backwardMostMove = 0;
}




void
EditPositionDone(void)
{
    startedFromSetupPosition = True;
    SendToProgram(localPlayer.appData.initString, toFirstProgFP);
    SendSearchDepth(toFirstProgFP);

    if (whitePlaysFirst)
    {
        strcpy(moveList[0], " ...\n");
        strcpy(parseList[0], " ...\n");
        currentMove = forwardMostMove = backwardMostMove = 1;
        CopyBoard(boards[1], boards[0]);
        CopyCatches(catches[1], catches[0]);
        SendToProgram("force\n", toFirstProgFP);
        SendCurrentBoard(toFirstProgFP);
        DisplayName(" ");
        DisplayMessage("White to play", False);
    }
    else
    {
        currentMove = forwardMostMove = backwardMostMove = 0;
        SendCurrentBoard(toFirstProgFP);
        SendToProgram("force\n", toFirstProgFP);
        DisplayName(" ");
        DisplayMessage("Black to play", False);
    }

    lastGameMode = gameMode = ForceMoves;
}



/*
 * FUNCTION
 *     BackwardProc
 *
 * DESCRIPTION
 *     This function executes when undoing a move.
 *     FIXME: this function is totally hosed!!!
 *
 * ARGUMENTS
 *     Widget w
 *     XEvent *event
 *     String *prms
 *     Cardinal *nprms 
 *
 * RETURN VALUE
 *     void
 *
 */

void
BackwardProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int target;
    unsigned int state;

    int fromRemotePlayer = *nprms;

    if (updateRemotePlayer)
    {
        DisplayMessage("Backward button disabled", fromRemotePlayer);
        return;
    }

    /* 
     * Why do we need this here?
     */

    ForceProc(w, event, prms, nprms);

    if ((currentMove <= backwardMostMove) || (gameMode == EditPosition))
        return;

    if (gameMode == EndOfGame)
        ResurrectShogiProgram();

    if (gameMode == PlayFromGameFile)
        PauseProc(w, event, prms, nprms);

    if (event == NULL)
    {
        /* Kludge */
        Window root, child;
        int root_x, root_y;
        int win_x, win_y;

        XQueryPointer(localPlayer.xDisplay, localPlayer.xBoardWindow,
                      &root, &child, &root_x, &root_y,
                      &win_x, &win_y, &state);
    }
    else
    {
        state = event->xkey.state;
    }

    if (state & ShiftMask)
    {
        target = backwardMostMove;
    }
    else
    {
        target = currentMove - 1;
    }

    if (gameMode == ForceMoves)
    {
        Attention(firstProgramPID);

        while (currentMove > target)
        {
            SendToProgram("undo\n", toFirstProgFP);
            currentMove--;
        }
    }
    else
    {
        currentMove = target;
    }

    if (gameMode == ForceMoves)
    {
        whiteTimeRemaining = timeRemaining[0][currentMove];
        blackTimeRemaining = timeRemaining[1][currentMove];
    }

    DisplayClocks(ReDisplayTimers);
    DisplayMove(currentMove - 1);
    DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
}




void
FlipViewProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    struct DisplayData *player = (*nprms ? &remotePlayer : &localPlayer);

    player->flipView = !player->flipView;
    DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
}




void
SaveGameProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    char def[MSG_SIZ];

    int fromRemotePlayer = *nprms;

    if (fromRemotePlayer)
    {
        DisplayMessage("only opponent may save game", fromRemotePlayer);
        return;
    }

    def[0] = NULLCHAR;

    FileNamePopUp("Filename for saved game?", SaveGame);
}




Boolean
SaveGame(char *name)
{
    char buf[MSG_SIZ];
    int i, len, move = 0;
    time_t tm;

    if (!filemodeUp) /* if called via FileModeCallback avoid recursion */
    {
        if ((gameFileFP = fopen(name, "r")) == NULL)
        {
            strcpy(fmi.mode, "w");
        }
        else
        {
            fclose(gameFileFP);
            FileModePopUp(name);
            return False; /* CHECKME: what should the return value be? */
        }
    }

    if ((gameFileFP = fopen(name, fmi.mode)) == NULL)
    {
        sprintf(buf, "Can't open %s (mode %s)", name, fmi.mode);
        DisplayMessage(buf, False);
        return False;
    }

    tm = time((time_t *) NULL);

    fprintf(gameFileFP, "# %s game file -- %s", programName, ctime(&tm));
    PrintOpponents(gameFileFP);

    for (i = 0; i < currentMove;)
    {
        if ((i % 5) == 0)
            fprintf(gameFileFP, "\n");

        fprintf(gameFileFP, "%d. %s ", ++move, parseList[i++]);

        if (i >= currentMove)
        {
            fprintf(gameFileFP, "\n");
            break;
        }

        if ((len = strlen(parseList[i])) == 0)
            break;

        fprintf(gameFileFP, "%s ", parseList[i++]);
    }

    fprintf(gameFileFP, "\n");

    fclose(gameFileFP);
    gameFileFP = NULL;

    return True;
}




void
SwitchProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    if (localPlayer.appData.noShogiProgram)
        return;

    switch (gameMode)
    {
    default:
        return;

    case MachinePlaysBlack:
        if (BlackOnMove(forwardMostMove))
        {
            DisplayMessage("Wait until your turn", False);
            return;
        }

        lastGameMode = gameMode = MachinePlaysWhite;
        ModeHighlight();
        break;

    case BeginningOfGame:

    case MachinePlaysWhite:
        if (!BlackOnMove(forwardMostMove))
        {
            DisplayMessage("Wait until your turn", False);
            return;
        }

        if (forwardMostMove == 0)
        {
            MachineBlackProc(w, event, prms, nprms);
            return;
        }

        lastGameMode = gameMode = MachinePlaysBlack;
        ModeHighlight();
        break;
    }

    Attention(firstProgramPID);
    SendToProgram("switch\n", toFirstProgFP);
}




void
ForceProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int i;

    switch (gameMode)
    {
    case MachinePlaysBlack:
        if (BlackOnMove(forwardMostMove))
        {
            DisplayMessage("Wait until your turn", False);
            return;
        }

        Attention(firstProgramPID);
        SendToProgram("force\n", toFirstProgFP);
        break;

    case MachinePlaysWhite:
        if (!BlackOnMove(forwardMostMove))
        {
            DisplayMessage("Wait until your turn", False);
            return;
        }

        Attention(firstProgramPID);
        SendToProgram("force\n", toFirstProgFP);
        break;

    case BeginningOfGame:
        SendToProgram("force\n", toFirstProgFP);
        break;

    case PlayFromGameFile:
        if (readGameXID != 0)
        {
            XtRemoveTimeOut(readGameXID);
            readGameXID = 0;
        }

        if (gameFileFP != NULL)
        {
            fclose(gameFileFP);
            gameFileFP = NULL;
        }

        break;

    case EndOfGame:
        ResurrectShogiProgram();
        break;

    case EditPosition:
        EditPositionDone();
        break;

    case TwoMachinesPlay:
        ShutdownShogiPrograms("");
        ResurrectShogiProgram();
        return;

    default:
        return;
    }

    if ((gameMode == MachinePlaysWhite)
        || (gameMode == MachinePlaysBlack)
        || (gameMode == TwoMachinesPlay)
        || (gameMode == PlayFromGameFile))
    {
        i = forwardMostMove;

        while (i > currentMove)
        {
            SendToProgram("undo\n", toFirstProgFP);
            i--;
        }

        blackTimeRemaining = timeRemaining[0][currentMove];
        whiteTimeRemaining = timeRemaining[1][currentMove];

        if (whiteFlag || blackFlag)
        {
            whiteFlag = blackFlag = 0;
        }

        DisplayTitle("");
    }

    lastGameMode = gameMode = ForceMoves;
    ModeHighlight();
    DisplayClocks(StopTimers);
}



void
HintProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int fromRemotePlayer = *nprms;

    if (updateRemotePlayer)
    {
        DisplayMessage("no hints in challenge mode", fromRemotePlayer);
        return;
    }

    if (localPlayer.appData.noShogiProgram) 
        return;

    switch (gameMode)
    {
    case MachinePlaysBlack:
        if (BlackOnMove(forwardMostMove))
        {
            DisplayMessage("Wait until your turn", False);
            return;
        }

        break;

    case BeginningOfGame:
    case MachinePlaysWhite:
        if (!BlackOnMove(forwardMostMove))
        {
            DisplayMessage("Wait until your turn", False);
            return;
        }

        break;

    default:
        DisplayMessage("No hint available", False);
        return;
    }

    Attention(firstProgramPID);
    SendToProgram("hint\n", toFirstProgFP);
}




void
PrintPosition(FILE *fp, int move)
{
    int i, j, color;

    for (i = BOARD_SIZE - 1; i >= 0; i--)
    {
        for (j = 0; j < BOARD_SIZE; j++)
        {
            if (pieceIsPromoted[(int)boards[currentMove][i][j]])
                fprintf(fp, "%c", '+');
            else
                fprintf(fp, "%c", ' ');

            fprintf(fp, "%c",
                    pieceToChar[(int)boards[currentMove][i][j]]);

            if (j == BOARD_SIZE - 1)
                fputc('\n', fp);
        }
    }

    for (color = 0; color <= 1; color++)
    {
        fprintf(fp, "%i %i %i %i %i %i %i %i\n",
                catches[currentMove][color][pawn],
                catches[currentMove][color][lance],
                catches[currentMove][color][knight],
                catches[currentMove][color][silver],
                catches[currentMove][color][gold],
                catches[currentMove][color][bishop],
                catches[currentMove][color][rook],
                catches[currentMove][color][king]);
    }

    if ((gameMode == EditPosition)
        ? !whitePlaysFirst
        : BlackOnMove(forwardMostMove))
    {
        fprintf(fp, "black to play\n");
    }
    else
    {
        fprintf(fp, "white to play\n");
    }
}




void
PrintOpponents(FILE *fp)
{
    char host_name[MSG_SIZ];

#ifdef HAVE_GETHOSTNAME
    gethostname(host_name, MSG_SIZ);
#else
    strncpy(host_name, "hostname not available", MSG_SIZ);
#endif

    switch (lastGameMode)
    {
    case MachinePlaysWhite:
        fprintf(fp, "# %s@%s vs. %s@%s\n",
                localPlayer.appData.firstShogiProgram,
                localPlayer.appData.firstHost,
                getpwuid(getuid())->pw_name,
                host_name);
        break;

    case MachinePlaysBlack:
        fprintf(fp, "# %s@%s vs. %s@%s\n",
                getpwuid(getuid())->pw_name,
                host_name,
                localPlayer.appData.firstShogiProgram,
                localPlayer.appData.firstHost);
        break;

    case TwoMachinesPlay:
        fprintf(fp, "# %s@%s vs. %s@%s\n",
                localPlayer.appData.secondShogiProgram,
                localPlayer.appData.secondHost,
                localPlayer.appData.firstShogiProgram,
                localPlayer.appData.firstHost);
        break;

    default:
        fprintf(fp, "#\n");
        break;
    }
}




void
SavePositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    char def[MSG_SIZ];

    int fromRemotePlayer = *nprms;

    if (fromRemotePlayer)
    {
        DisplayMessage("only opponent may save game", fromRemotePlayer);
        return;
    }

    def[0] = NULLCHAR;

    FileNamePopUp("Filename for saved position?", SavePosition);
}




Boolean
SavePosition(char *name)
{
    char buf[MSG_SIZ];
    FILE *fp;
    time_t tm;

    if ((fp = fopen(name, "w")) == NULL)
    {
        sprintf(buf, "Can't open %s", name);
        DisplayMessage(buf, False);
        return False;
    }

    tm = time((time_t *) NULL);

    fprintf(fp, "# %s position file -- %s", programName, ctime(&tm));
    PrintOpponents(fp);
    PrintPosition(fp, currentMove);
    fclose(fp);

    return True;
}




void
TwoMachinesProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    int i;
    MatchMode matchKind;

    int fromRemotePlayer = *nprms;

    if (updateRemotePlayer)
    {
        DisplayMessage("no machine moves in challenge mode",
                       fromRemotePlayer);
        return;
    }

    if (gameMode == PauseGame) 
        PauseProc(w, event, prms, nprms);

    if (gameMode == PlayFromGameFile) 
        ForceProc(w, event, prms, nprms);

    if ((gameMode == EndOfGame) 
        || (gameMode == TwoMachinesPlay)
        || localPlayer.appData.noShogiProgram)
    {
        return;
    }

    if (matchMode == MatchFalse)
    {
        switch (gameMode)
        {
        case PauseGame:
        case PlayFromGameFile:
            return;

        case MachinePlaysBlack:
        case MachinePlaysWhite:
            ForceProc(w, event, prms, nprms);

            if (gameMode != ForceMoves) 
                return;

            matchKind = MatchOpening;
            break;

        case ForceMoves:
            matchKind = MatchOpening;
            break;

        case EditPosition:
            EditPositionDone();
            matchKind = MatchPosition;
            break;

        case BeginningOfGame:
        default:
            matchKind = MatchInit;
            break;
        }
    }
    else
    {
        matchKind = matchMode;
    }

    forwardMostMove = currentMove;

    localPlayer.flipView = False;
    remotePlayer.flipView = True;
    firstMove = False;
    DisplayClocks(ResetTimers);
    DisplayClocks(StartTimers);

    switch (matchKind)
    {
    case MatchOpening:
        if (firstProgramXID == 0)
        {
            if (localPlayer.appData.loadGameFile == NULL)
            {
                DisplayMessage("Select game file first", False);
                return;
            }

            InitShogiProgram(localPlayer.appData.firstHost,
                             localPlayer.appData.firstShogiProgram,
                             &firstProgramPID, &toFirstProgFP,
                             &fromFirstProgFP, &firstProgramXID,
                             &firstSendTime);

            if (!LoadGame(localPlayer.appData.loadGameFile))
            {
                ShutdownShogiPrograms("Bad game file");
                return;
            }

            DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
        }

        InitShogiProgram(localPlayer.appData.secondHost,
                         localPlayer.appData.secondShogiProgram,
                         &secondProgramPID, &toSecondProgFP,
                         &fromSecondProgFP, &secondProgramXID,
                         &secondSendTime);

        if (startedFromSetupPosition)
        {
            if (whitePlaysFirst)
            {
                i = 1;
                SendToProgram("force\n", toSecondProgFP);
                SendBoard(toSecondProgFP, boards[i], catches[i]);
            }
            else
            {
                i = 0;
                SendBoard(toSecondProgFP, boards[i], catches[i]);
                SendToProgram("force\n", toSecondProgFP);
            }
        }
        else
        {
            i = 0;
            SendToProgram("force\n", toSecondProgFP);
        }

        for (i = backwardMostMove; i < forwardMostMove; i++)
            SendToProgram(moveList[i], toSecondProgFP);

        lastGameMode = gameMode = TwoMachinesPlay;
        ModeHighlight();
        firstMove = True;

        if (BlackOnMove(forwardMostMove))
            SendToProgram(localPlayer.appData.blackString, toSecondProgFP);
        else
            SendToProgram(localPlayer.appData.whiteString, toFirstProgFP);

        break;

    case MatchPosition:
        if (firstProgramXID == 0)
        {
            if (localPlayer.appData.loadPositionFile == NULL)
            {
                DisplayMessage("Select position file first", False);
                return;
            }

            InitShogiProgram(localPlayer.appData.firstHost,
                             localPlayer.appData.firstShogiProgram,
                             &firstProgramPID, &toFirstProgFP,
                             &fromFirstProgFP, &firstProgramXID,
                             &firstSendTime);

            if (!LoadPosition(localPlayer.appData.loadPositionFile))
                return;
        }

        InitShogiProgram(localPlayer.appData.secondHost,
                         localPlayer.appData.secondShogiProgram,
                         &secondProgramPID, &toSecondProgFP,
                         &fromSecondProgFP, &secondProgramXID,
                         &secondSendTime);

        if (whitePlaysFirst)
            SendToProgram("force\n", toSecondProgFP);

        SendCurrentBoard(toSecondProgFP);
        lastGameMode = gameMode = TwoMachinesPlay;
        ModeHighlight();
        firstMove = True;

        if (BlackOnMove(forwardMostMove))
            SendToProgram(localPlayer.appData.blackString, toSecondProgFP);
        else
            SendToProgram(localPlayer.appData.whiteString, toFirstProgFP);

        break;

    case MatchInit:
        InitPosition(True);

        if (firstProgramXID == 0)
        {
            InitShogiProgram(localPlayer.appData.firstHost,
                             localPlayer.appData.firstShogiProgram,
                             &firstProgramPID, &toFirstProgFP,
                             &fromFirstProgFP, &firstProgramXID,
                             &firstSendTime);
        }

        InitShogiProgram(localPlayer.appData.secondHost,
                         localPlayer.appData.secondShogiProgram,
                         &secondProgramPID, &toSecondProgFP,
                         &fromSecondProgFP, &secondProgramXID,
                         &secondSendTime);

        lastGameMode = gameMode = TwoMachinesPlay;
        ModeHighlight();
        SendToProgram(localPlayer.appData.blackString, toSecondProgFP);

    default:
        break;
    }

    if (!firstSendTime || !secondSendTime)
    {
        DisplayClocks(ResetTimers);
        timeRemaining[0][forwardMostMove] = blackTimeRemaining;
        timeRemaining[1][forwardMostMove] = whiteTimeRemaining;
    }

    DisplayClocks(StartTimers);
}




void
PauseProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    static GameMode previous_mode = PauseGame;

    switch (gameMode)
    {
    case ForceMoves:
    case EndOfGame:
    case EditPosition:
    default:
        return;

    case PauseGame:
        gameMode = previous_mode;
        ModeHighlight();
        previous_mode = PauseGame;
        DisplayClocks(StartTimers);
        DisplayMessage("", False);

        if (updateRemotePlayer)
            DisplayMessage("", True);
        break;

    case PlayFromGameFile:
        if (readGameXID == 0)
        {
            readGameXID =
                XtAppAddTimeOut(appContext,
                                (int)(1000 * localPlayer.appData.timeDelay),
                                (XtTimerCallbackProc) ReadGameFile, NULL);
        }
        else
        {
            XtRemoveTimeOut(readGameXID);
            readGameXID = 0;
        }

        DisplayMessage("Pausing", False);

        if (updateRemotePlayer)
            DisplayMessage("Pausing", True);

        break;

    case BeginningOfGame:
    case MachinePlaysBlack:
    case MachinePlaysWhite:
    case TwoMachinesPlay:
        if (forwardMostMove == 0)   /* Don't pause if no one has moved. */
            return;

        if (((gameMode == MachinePlaysWhite)
             && !BlackOnMove(forwardMostMove)) 
            || ((gameMode == MachinePlaysBlack) &&
                BlackOnMove(forwardMostMove)))
        {
            DisplayClocks(StopTimers);
        }

        previous_mode = gameMode;
        gameMode = PauseGame;
        ModeHighlight();
        DisplayClocks(StopTimers);
        DisplayMessage("Pausing", False);

        if (updateRemotePlayer)
            DisplayMessage("Pausing", True);

        break;
    }
}




void
Iconify(Widget w, XEvent *event, String *prms, Cardinal *nprms)
{
    Arg args[1];

    fromX = fromY = -1;

    XtSetArg(args[0], XtNiconic, True);
    XtSetValues(localPlayer.shellWidget, args, 1);
}




void
SendToProgram(char *message, FILE *fp)
{
    if (fp == NULL) 
        return;

    lastMsgFP = fp;

    if (xshogiDebug)
    {
        fprintf(stderr, "Sending to %s: %s\n",
                ((fp == toFirstProgFP) ? "first" : "second"), message);
    }

    if (message[strlen(message) - 1] != '\n')
        fprintf(fp, "\n%s\n", message);
    else
        fputs(message, fp);

    fflush(fp);
}




void
ReceiveFromProgram(FILE *fp, int *source, XtInputId *id)
{
    char message[MSG_SIZ], *end_str, *number, *name;
    extern const char *const sys_errlist[];

    if (fgets(message, MSG_SIZ, fp) == NULL)
    {
        if (fp == fromFirstProgFP)
        {
            number = "first";
            name = localPlayer.appData.firstShogiProgram;
        }
        else if (fp == fromSecondProgFP)
        {
            number = "second";
            name = localPlayer.appData.secondShogiProgram;
        }
        else
        {
            return;
        }

        if (ferror(fp) == 0)
        {
            sprintf(message, "%s shogi program (%s) exited unexpectedly",
                    number, name);
            fprintf(stderr, "%s: %s\n", programName, message);
        }
        else
        {
            sprintf(message,
                    "error reading from %s shogi program (%s): %s",
                    number, name, sys_errlist[ferror(fp)]);
            fprintf(stderr, "%s: %s\n", programName, message);
        }

        return;
    }

    if ((end_str = (char *)strchr(message, '\r')) != NULL)
        *end_str = NULLCHAR;

    if ((end_str = (char *)strchr(message, '\n')) != NULL)
        *end_str = NULLCHAR;

    if (xshogiDebug || localPlayer.appData.debugMode)
    {
        fprintf(stderr, "Received from %s: %s\n",
                ((fp == fromFirstProgFP) ? "first" : "second"), message);
    }

    HandleMachineMove(message, fp);
}




void
SendSearchDepth(FILE *fp)
{
    char message[MSG_SIZ];

    if (localPlayer.appData.searchDepth <= 0) 
        return;

    sprintf(message, "depth\n%d\nhelp\n", localPlayer.appData.searchDepth);
    /* Note kludge: "help" command forces gnushogi to print
     * out something that ends with a newline. */
    SendToProgram(message, fp);
}




void
DisplayMessage(char *message, int toRemotePlayer)
{
    Arg arg;

    XtSetArg(arg, XtNlabel, message);

    if (!toRemotePlayer)
        XtSetValues(localPlayer.messageWidget, &arg, 1);

    if (updateRemotePlayer && toRemotePlayer)
        XtSetValues(remotePlayer.messageWidget, &arg, 1);
}




void
DisplayName(char *name)
{
    Arg arg;

    XtSetArg(arg, XtNlabel, name);
    XtSetValues(localPlayer.titleWidget, &arg, 1);

    if (updateRemotePlayer)
        XtSetValues(remotePlayer.titleWidget, &arg, 1);
}




void SendTimeRemaining(FILE *fp)
{
    char message[MSG_SIZ];
    long comtime, opptime;

    if (BlackOnMove(forwardMostMove) == (fp == toFirstProgFP))
    {
        comtime = blackTimeRemaining;
        opptime = whiteTimeRemaining;
    }
    else
    {
        comtime = whiteTimeRemaining;
        opptime = blackTimeRemaining;
    }

    if (comtime <= 0) 
        comtime = 1000;

    if (opptime <= 0) 
        opptime = 1000;

    sprintf(message, "time %ld\n",  comtime / 10);
    SendToProgram(message, fp);
    sprintf(message, "otime %ld\n", opptime / 10);
    SendToProgram(message, fp);
}




void DisplayMove(int moveNumber)
{
    char message[MSG_SIZ];

    if (moveNumber < 0)
    {
        if (moveNumber == forwardMostMove - 1)
            DisplayMessage(endMessage, False);
        else
            DisplayMessage("", False);
    }
    else
    {
        sprintf(message, "%d. %s%s  %s", 
                (moveNumber / 2 + 1),
                (BlackOnMove(moveNumber) ? "" : "... "),
                parseList[moveNumber],
                (moveNumber == (forwardMostMove - 1)) ? endMessage : "");
        DisplayMessage(message, False);
    }
}




void DisplayTitle(char *title)
{
    Arg arg;

    XtSetArg(arg, XtNlabel, title);
    XtSetValues(localPlayer.titleWidget, &arg, 1);
}




/* CHECKME: does this work?
 * This routine sends a SIGINT (^C interrupt) to gnushogi to awaken it
 * if it might be busy thinking on our time.  This normally isn't needed,
 * but is useful on systems where the FIONREAD ioctl doesn't work since 
 * on those systems the gnushogi feature that lets you interrupt its thinking 
 * just by typing a command does not work.
 *
 * In the future, similar code could be used to stop gnushogi and make
 * it move immediately when it is thinking about its own move; this could
 * be useful if we want to make Backward or ForceMoves work while gnushogi
 * is thinking.
 */

void
Attention(int pid)
{
#if !defined(FIONREAD)
    if (localPlayer.appData.noShogiProgram || (pid == 0))
        return;

    switch (gameMode)
    {
    case MachinePlaysBlack:
    case MachinePlaysWhite:
    case TwoMachinesPlay:
        if ((forwardMostMove > backwardMostMove + 1) && maybeThinking)
        {
            if (xshogiDebug || localPlayer.appData.debugMode)
            {
                fprintf(stderr, "Sending SIGINT to %s\n",
                        ((pid == firstProgramPID) ? "first" : "second"));
            }

            (void)kill(pid, SIGINT); /* stop it thinking */
        }
        break;

    default:
        break;  /* CHECKME: is this OK? */
    }
#endif /* !defined(FIONREAD) */
}




void
CheckFlags(void)
{
    if (blackTimeRemaining <= 0)
    {
        if (!blackFlag)
        {
            blackFlag = True;

            if (whiteFlag)
                DisplayName("  Both flags have fallen");
            else
                DisplayName("  Black's flag has fallen");
        }
    }

    if (whiteTimeRemaining <= 0)
    {
        if (!whiteFlag)
        {
            whiteFlag = True;

            if (blackFlag)
                DisplayName("  Both flags have fallen");
            else
                DisplayName("  White's flag has fallen");
        }
    }
}




void
CheckTimeControl(void)
{
    if (!localPlayer.appData.clockMode)
        return;

    if (forwardMostMove == 0) 
        return;

    /*
     * Add time to clocks when time control is achieved.
     */

    if ((forwardMostMove % (localPlayer.appData.movesPerSession * 2)) == 0)
    {
        blackTimeRemaining += timeControl;
        whiteTimeRemaining += timeControl;
    }
}




void
DisplayLabels(void)
{
    DisplayTimerLabel(localPlayer.blackTimerWidget, "Black",
                      blackTimeRemaining);
    DisplayTimerLabel(localPlayer.whiteTimerWidget, "White",
                      whiteTimeRemaining);

    if (updateRemotePlayer)
    {
        DisplayTimerLabel(remotePlayer.blackTimerWidget, "Black",
                          blackTimeRemaining);
        DisplayTimerLabel(remotePlayer.whiteTimerWidget, "White",
                          whiteTimeRemaining);
    }
}




#ifdef HAVE_GETTIMEOFDAY
static struct timeval tickStartTV;
static int tickLength;

int
PartialTickLength(void)
{
    struct timeval tv;
    struct timezone tz;
    int ptl;

    gettimeofday(&tv, &tz);
    ptl = ((tv.tv_sec - tickStartTV.tv_sec) * 1000000 +
           (tv.tv_usec - tickStartTV.tv_usec) + 500) / 1000;

    if (ptl > tickLength) 
        ptl = tickLength;

    return ptl;
}
#else /* !HAVE_GETTIMEOFDAY */
#define tickLength 1000
#endif /* HAVE_GETTIMEOFDAY */




/*
 * DisplayClocks manages the game clocks.
 *
 * In tournament play, white starts the clock and then black makes a move.
 * We give the human user a slight advantage if he is playing black---the
 * clocks don't run until he makes his first move, so it takes zero time.
 * Also, DisplayClocks doesn't account for network lag so it could get out
 * of sync with GNU Shogi's clock -- but then, referees are always right.  
 */

void
DisplayClocks(int clock_mode)
{
#ifdef HAVE_GETTIMEOFDAY
    struct timezone tz;
#endif /* HAVE_GETTIMEOFDAY */

    long timeRemaining;

    switch (clock_mode)
    {
    case ResetTimers:
        /* Stop clocks and reset to a fresh time control */
        if (timerXID != 0)
        {
            XtRemoveTimeOut(timerXID);
            timerXID = 0;
        }

        blackTimeRemaining = timeControl;
        whiteTimeRemaining = timeControl;

        if (blackFlag || whiteFlag)
        {
            DisplayName("");
            blackFlag = whiteFlag = False;
        }

        DisplayLabels();
        break;

    case DecrementTimers:
        /* Decrement running clock to next 1-second boundary */
        if (gameMode == PauseGame) 
            return;

        timerXID = 0;

        if (!localPlayer.appData.clockMode) 
            return;

        if (BlackOnMove(forwardMostMove))
        {
            timeRemaining = (blackTimeRemaining -= tickLength);
        }
        else
        {
            timeRemaining = (whiteTimeRemaining -= tickLength);
        }

        DisplayLabels();
        CheckFlags();

#ifdef HAVE_GETTIMEOFDAY
        tickLength = (((timeRemaining <= 1000) && (timeRemaining > 0))
                      ? 100 : 1000);
        gettimeofday(&tickStartTV, &tz);
#endif /* HAVE_GETTIMEOFDAY */

        timerXID =
            XtAppAddTimeOut(appContext, tickLength,
                            (XtTimerCallbackProc) DisplayClocks,
                            (XtPointer) DecrementTimers);
        break;

    case SwitchTimers:
        /* A player has just moved, so stop the previously running
           clock and start the other one. */

        if (timerXID != 0)
        {
            XtRemoveTimeOut(timerXID);
            timerXID = 0;

#ifdef HAVE_GETTIMEOFDAY
            if (localPlayer.appData.clockMode)
            {
                if (BlackOnMove(forwardMostMove))
                    whiteTimeRemaining -= PartialTickLength();
                else
                    blackTimeRemaining -= PartialTickLength();
                CheckFlags();
            }
#endif /* HAVE_GETTIMEOFDAY */
        }

        CheckTimeControl();
        DisplayLabels();

        if (!localPlayer.appData.clockMode) 
            return;

        if ((gameMode == PauseGame)
            && ((pausePreviousMode == MachinePlaysBlack) 
                || (pausePreviousMode == MachinePlaysWhite)))
        {
            return;
        }

        timeRemaining = (BlackOnMove(forwardMostMove) 
                         ? blackTimeRemaining : whiteTimeRemaining);

#ifdef HAVE_GETTIMEOFDAY
        tickLength = (((timeRemaining <= 1000) && (timeRemaining > 0))
                      ? (((timeRemaining - 1) % 100) + 1) 
                      : (((timeRemaining - 1) % 1000) + 1));

        if (tickLength <= 0) 
            tickLength += 1000;

        gettimeofday(&tickStartTV, &tz);

#endif /* HAVE_GETTIMEOFDAY */
        timerXID =
            XtAppAddTimeOut(appContext, tickLength,
                            (XtTimerCallbackProc) DisplayClocks,
                            (XtPointer) DecrementTimers);
        break;

    case ReDisplayTimers:
        /* Display current clock values */
        DisplayLabels();
        break;

    case StopTimers:
        /* Stop both clocks */
        if (timerXID == 0)
            return;

        XtRemoveTimeOut(timerXID);
        timerXID = 0;

        if (!localPlayer.appData.clockMode) 
            return;

#ifdef HAVE_GETTIMEOFDAY
        if (BlackOnMove(forwardMostMove))
            blackTimeRemaining -= PartialTickLength();
        else
            whiteTimeRemaining -= PartialTickLength();
        CheckFlags();
        DisplayLabels();
#endif /* HAVE_GETTIMEOFDAY */
        break;

    case StartTimers:
        /* Start clock of player on move, if not already running. */
        if (timerXID != 0)
            return;

        DisplayLabels();

        if (!localPlayer.appData.clockMode) 
            return;

        timeRemaining = (BlackOnMove(forwardMostMove) 
                         ? blackTimeRemaining : whiteTimeRemaining);

        if (timeRemaining == 0) 
            return;

#ifdef HAVE_GETTIMEOFDAY
        tickLength = (((timeRemaining <= 1000) && (timeRemaining > 0)) 
                      ? (((timeRemaining - 1) % 100) + 1)
                      : (((timeRemaining - 1) % 1000) + 1));

        if (tickLength <= 0) 
            tickLength += 1000;

        gettimeofday(&tickStartTV, &tz);
#endif /* HAVE_GETTIMEOFDAY */

        timerXID =
            XtAppAddTimeOut(appContext, tickLength,
                            (XtTimerCallbackProc) DisplayClocks,
                            (XtPointer)DecrementTimers);
        break;
    }
}




void
DisplayTimerLabel(Widget w, char *color, long int timer)
{
    char buf[MSG_SIZ];
    Arg args[3];
    struct DisplayData *player;

    player = (((w == localPlayer.blackTimerWidget)
               || (w == localPlayer.whiteTimerWidget))
              ? &localPlayer : &remotePlayer);

    if (localPlayer.appData.clockMode)
    {
        sprintf(buf, "%s: %s", color, TimeString(timer));
        XtSetArg(args[0], XtNlabel, buf);
    }
    else
    {
        XtSetArg(args[0], XtNlabel, color);
    }

    if (((color[0] == 'W') && BlackOnMove(forwardMostMove))
        || ((color[0] == 'B') && !BlackOnMove(forwardMostMove)))
    {
        XtSetArg(args[1], XtNbackground, player->timerForegroundPixel);
        XtSetArg(args[2], XtNforeground, player->timerBackgroundPixel);
    }
    else
    {
        XtSetArg(args[1], XtNbackground, player->timerBackgroundPixel);
        XtSetArg(args[2], XtNforeground, player->timerForegroundPixel);
    }

    XtSetValues(w, args, 3);
}




char *
TimeString(long tm)
{
    int second, minute, hour, day;
    char *sign = "";
    static char buf[32];

    if ((tm > 0) && (tm <= 900))
    {
        /* convert milliseconds to tenths, rounding up */
        sprintf(buf, " 0.%1ld ", (tm + 99) / 100);
        return buf;
    }

    /* convert milliseconds to seconds, rounding up */
    tm = (tm + 999) / 1000;

    if (tm < 0)
    {
        sign = "-";
        tm = -tm;
    }

    if (tm >= (60 * 60 * 24))
    {
        day = (int)(tm / (60 * 60 * 24));
        tm -= day * 60 * 60 * 24;
    }
    else
    {
        day = 0;
    }

    if (tm >= (60 * 60))
    {
        hour = (int)(tm / (60 * 60));
        tm -= hour * 60 * 60;
    }
    else
    {
        hour = 0;
    }

    if (tm >= 60)
    {
        minute = (int)(tm / 60);
        tm -= minute * 60;
    }
    else
    {
        minute = 0;
    }

    second = tm % 60;

    if (day > 0)
    {
        sprintf(buf, " %s%d:%02d:%02d:%02d ",
                sign, day, hour, minute, second);
    }
    else if (hour > 0)
    {
        sprintf(buf, " %s%d:%02d:%02d ",
                sign, hour, minute, second);
    }
    else
    {
        sprintf(buf, " %s%2d:%02d ",
                sign, minute, second);
    }

    return buf;
}




void
Usage(void)
{
    fprintf(stderr, "Usage: %s\n", programName);
    fprintf(stderr, "\tstandard Xt options\n");
    fprintf(stderr, "\t-iconic\n");
    fprintf(stderr, "\t-tc or -timeControl minutes[:seconds]\n");
    fprintf(stderr, "\t-gi or -gameIn (True | False)\n");
    fprintf(stderr, "\t-mps or -movesPerSession moves\n");
    fprintf(stderr, "\t-st or -searchTime minutes[:seconds]\n");
    fprintf(stderr, "\t-sd or -searchDepth number\n");
    fprintf(stderr, "\t-clock or -clockMode (True | False)\n");
    fprintf(stderr, "\t-td or -timeDelay seconds\n");

    fprintf(stderr, "\t-nsp or -noShogiProgram (True | False)\n");
    fprintf(stderr, "\t-fsp or -firstShogiProgram program_name\n");
    fprintf(stderr, "\t-ssp or -secondShogiProgram program_name\n");
    fprintf(stderr, "\t-fh or -firstHost host_name\n");
    fprintf(stderr, "\t-sh or -secondHost host_name\n");
    fprintf(stderr, "\t-rsh or -remoteShell shell_name\n");
    fprintf(stderr,
            "\t-mm or -matchMode (False | Init | Position | Opening)\n");
    fprintf(stderr, "\t-lgf or -loadGameFile file_name\n");
    fprintf(stderr, "\t-lpf or -loadPositionFile file_name\n");
    fprintf(stderr, "\t-sgf or -saveGameFile file_name\n");
    fprintf(stderr, "\t-spf or -savePositionFile file_name\n");
    fprintf(stderr, "\t-size or -boardSize (Large | Medium | Small)\n");
    fprintf(stderr, "\t-coords or -showCoords (True | False)\n");
    fprintf(stderr, "\t-mono or -monoMode (True | False)\n");
    fprintf(stderr, "\t-bpc or -blackPieceColor color\n");
    fprintf(stderr, "\t-wpc or -whitePieceColor color\n");
    fprintf(stderr, "\t-lsc or -lightSquareColor color\n");
    fprintf(stderr, "\t-dsc or -darkSquareColor color\n");
    fprintf(stderr, "\t-wps or -westernPieceSet (True | False)\n");
    fprintf(stderr, "\t-debug or -debugMode (True | False)\n");
    exit(2);
}



void
CatchPipeSignal(int dummy)
{
    char message[MSG_SIZ];
    
    sprintf(message,
            "%s shogi program (%s) exited unexpectedly",
            ((lastMsgFP == toFirstProgFP) ? "first" : "second"),
            ((lastMsgFP == toFirstProgFP)
             ? localPlayer.appData.firstShogiProgram
             : localPlayer.appData.secondShogiProgram));
    fprintf(stderr, "%s: %s\n", programName, message);
    ShutdownShogiPrograms(message);
    return;
}

