--- /dev/null
+/*
+ * 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;
+}
+