X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=xshogi%2Fxshogi.c;fp=xshogi%2Fxshogi.c;h=33fa7ab587823ab4023d590fc3fef5d5ce6b401a;hb=8ae7e7d1b257ef36d8a9fd1cd88807954ef10764;hp=0000000000000000000000000000000000000000;hpb=e597a4014df46fbc2b8cacc74f8f176c194872a0;p=gnushogi.git diff --git a/xshogi/xshogi.c b/xshogi/xshogi.c new file mode 100644 index 0000000..33fa7ab --- /dev/null +++ b/xshogi/xshogi.c @@ -0,0 +1,7745 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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[] = +": DrawPosition() \n \ +: HandleUserMove() \n \ +: HandleUserMove() \n \ +: XawPositionSimpleMenu(menuW) PieceMenuPopup(menuW) \n \ +: XawPositionSimpleMenu(menuB) PieceMenuPopup(menuB) \n \ +r: ResetFileProc() ResetProc() \n \ +R: ResetFileProc() ResetProc() \n \ +g: LoadGameProc() \n \ +G: LoadGameProc() \n \ +q: QuitProc() \n \ +Q: QuitProc() \n \ +WM_PROTOCOLS: QuitProc() \n \ +f: ForwardProc() \n \ +F: ForwardProc() \n \ +b: BackwardProc() \n \ +B: BackwardProc() \n \ +p: PauseProc() \n \ +P: PauseProc() \n \ +i: Iconify() \n \ +I: Iconify() \n \ +c: Iconify() \n \ +C: Iconify() \n"; + + +char translationsTableReduced[] = +": DrawPosition() \n \ +: HandleUserMove() \n \ +: HandleUserMove() \n \ +WM_PROTOCOLS: QuitProc() \n"; + + +char blackTranslations[] = ": SetBlackToPlay()\n"; +char whiteTranslations[] = ": SetWhiteToPlay()\n"; + +String xshogiResources[] = +{ + DEFAULT_FONT, + "*Dialog*value.translations: #override " + "\\n 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; +} +