f88f39a669aebbea8f6b8558ca003ff4862c2b8f
[gnushogi.git] / xshogi / xshogi.c
1 /*
2  * FILE: xshogi.c
3  *
4  *     Implementation of the X interface for GNU shogi (xshogi).
5  *
6  * ------------------------------------------------------------------------
7  * xshogi is based on XBoard -- an Xt/Athena user interface for GNU Chess.
8  *
9  * Original authors:                                Dan Sears, Chris Sears
10  * Enhancements (Version 2.0 and following):        Tim Mann
11  * Modifications to XShogi (Version 1.0):           Matthias Mutz
12  * Enhancements to XShogi (Version 1.1):            Matthias Mutz
13  * Modified implementation of ISS mode for XShogi:  Matthias Mutz
14  * Current maintainer:                              Michael C. Vanier
15  *
16  * XShogi borrows some of its piece bitmaps from CRANES Shogi.
17  *
18  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
19  * Enhancements Copyright 1992 Free Software Foundation, Inc.
20  * Enhancements for XShogi Copyright 1993, 1994, 1995 Matthias Mutz
21  * Copyright (c) 1999 Michael Vanier and the Free Software Foundation
22  *
23  * The following terms apply to Digital Equipment Corporation's copyright
24  * interest in XBoard:
25  * ------------------------------------------------------------------------
26  * All Rights Reserved
27  *
28  * Permission to use, copy, modify, and distribute this software and its
29  * documentation for any purpose and without fee is hereby granted,
30  * provided that the above copyright notice appear in all copies and that
31  * both that copyright notice and this permission notice appear in
32  * supporting documentation, and that the name of Digital not be
33  * used in advertising or publicity pertaining to distribution of the
34  * software without specific, written prior permission.
35  *
36  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
37  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
38  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
39  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
40  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
41  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
42  * SOFTWARE.
43  * ------------------------------------------------------------------------
44  *
45  * This file is part of GNU shogi.
46  *
47  * GNU shogi is free software; you can redistribute it and/or modify
48  * it under the terms of the GNU General Public License as published by
49  * the Free Software Foundation.
50  *
51  * GNU shogi is distributed in the hope that it will be useful,
52  * but WITHOUT ANY WARRANTY; without even the implied warranty of
53  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
54  * GNU General Public License for more details.
55  *
56  * You should have received a copy of the GNU General Public License
57  * along with GNU shogi; see the file COPYING.  If not, write to
58  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
59  *
60  * ------------------------------------------------------------------------
61  *
62  */
63
64 #include "config.h"
65
66 #ifdef X_DISPLAY_MISSING
67 #error You cannot compile xshogi if X windows is unavailable!
68 #endif
69
70 #include "sysdeps.h"
71
72 #define XBOARD_VERSION "2.0/2.1"
73
74 #include <stdio.h>
75 #include <ctype.h>
76 #include <signal.h>
77 #include <sys/ioctl.h>
78
79 #include <time.h>
80 #include <pwd.h>
81
82 #include <X11/Intrinsic.h>
83 #include <X11/StringDefs.h>
84 #include <X11/Shell.h>
85 #include <X11/Xaw/Dialog.h>
86 #include <X11/Xaw/Form.h>
87 #include <X11/Xaw/List.h>
88 #include <X11/Xaw/Label.h>
89 #include <X11/Xaw/SimpleMenu.h>
90 #include <X11/Xaw/SmeBSB.h>
91 #include <X11/Xaw/SmeLine.h>
92 #include <X11/cursorfont.h>
93
94 #include "xshogi.h"
95
96 #define BUF_SIZE 1024
97 #define BOARD    1
98 #define MOVES    2
99
100 #include "bitmaps.h"  /* Piece bitmaps. */
101 #include "xshogifn.h" /* Forward declarations. */
102
103 #define off_board(x) ((x < 2) || (x > BOARD_SIZE + 1))
104
105
106 /**********************************************************************
107  *
108  * Global variables, structs etc.
109  *
110  **********************************************************************/
111
112 /*
113  * NOTE: XShogi depends on Xt R4 or higher
114  */
115
116 int xtVersion = XtSpecificationRelease;
117
118 XtIntervalId firstProgramXID = 0, secondProgramXID = 0,
119     readGameXID = 0, timerXID = 0, blinkSquareXID = 0;
120
121 XtAppContext appContext;
122
123 Boolean (*fileProc) (char *name);
124
125 FILE *fromFirstProgFP, *toFirstProgFP, *fromSecondProgFP,
126     *toSecondProgFP, *gameFileFP, *lastMsgFP;
127
128 int currentMove = 0, forwardMostMove = 0, backwardMostMove = 0,
129     firstProgramPID = 0,
130     secondProgramPID = 0, fromX = -1,
131     fromY = -1, firstMove = True, flipView = False,
132     xshogiDebug = True, commentUp = False, filenameUp = False,
133     whitePlaysFirst = False, startedFromSetupPosition = False,
134     searchTime = 0, pmFromX = -1, pmFromY = -1,
135     blackFlag = False, whiteFlag = False, maybeThinking = False,
136     filemodeUp = False;
137
138 int at_least_gnushogi_1_2p03 = False;
139
140 int firstSendTime = 2, secondSendTime = 2; /* 0 = don't, 1 = do,
141                                               2 = test first */
142
143 MatchMode matchMode         = MatchFalse;
144 GameMode  gameMode          = BeginningOfGame;
145 GameMode  lastGameMode      = BeginningOfGame;
146 GameMode  pausePreviousMode = BeginningOfGame;
147
148 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2],
149     ptyname[24], *shogiDir, *programName;
150
151 char endMessage[MOVE_LEN * 4];
152
153 long blackTimeRemaining, whiteTimeRemaining, timeControl;
154 long timeRemaining[2][MAX_MOVES];
155
156 extern char currentMoveString[];
157
158 int updateRemotePlayer = False;
159
160 Catched catches[MAX_MOVES];
161
162 #define DIMENSION 100
163
164 Widget blackPieceMenu, whitePieceMenu, commentShell;
165
166 XSetWindowAttributes attr;
167
168 #define pawn      0
169 #define lance     1
170 #define knight    2
171 #define silver    3
172 #define gold      4
173 #define bishop    5
174 #define rook      6
175 #define king      7
176 #define no_piece  8
177 #define ppawn     9
178 #define plance   10
179 #define pknight  11
180 #define psilver  12
181 #define pbishop  13
182
183 #define NO_PIECES  15
184 #define NO_SQUARES 81
185 #define NO_COLS     9
186 #define NO_ROWS     9
187
188
189 char catchedIndexToChar[8] =
190 {
191     'P', 'L', 'N', 'S', 'G', 'B', 'R', 'K'
192 };
193
194 ShogiSquare catchedIndexToPiece[2][8] =
195 {
196     {
197         BlackPawn, BlackLance, BlackKnight, BlackSilver, BlackGold,
198         BlackBishop, BlackRook, BlackKing
199     },
200     {
201         WhitePawn, WhiteLance, WhiteKnight, WhiteSilver, WhiteGold,
202         WhiteBishop, WhiteRook, WhiteKing
203     }
204 };
205
206
207 int pieceToCatchedIndex[] =
208 {
209     pawn, lance, knight, silver, gold, bishop, rook,
210     pawn, lance, knight, silver, bishop, rook, king,
211     pawn, lance, knight, silver, gold, bishop, rook,
212     pawn, lance, knight, silver, bishop, rook, king,
213     no_piece
214 };
215
216
217
218 Board boards[MAX_MOVES];
219 Board initialPosition =
220 {
221     { BlackLance,  BlackKnight, BlackSilver, BlackGold, BlackKing,
222       BlackGold,   BlackSilver, BlackKnight, BlackLance },
223     { EmptySquare, BlackBishop, EmptySquare, EmptySquare, EmptySquare,
224       EmptySquare, EmptySquare, BlackRook,   EmptySquare },
225     { BlackPawn,   BlackPawn,   BlackPawn,   BlackPawn, BlackPawn,
226       BlackPawn,   BlackPawn,   BlackPawn,   BlackPawn },
227     { EmptySquare, EmptySquare, EmptySquare, EmptySquare, EmptySquare,
228       EmptySquare, EmptySquare, EmptySquare, EmptySquare } ,
229     { EmptySquare, EmptySquare, EmptySquare, EmptySquare, EmptySquare,
230       EmptySquare, EmptySquare, EmptySquare, EmptySquare } ,
231     { EmptySquare, EmptySquare, EmptySquare, EmptySquare, EmptySquare,
232       EmptySquare, EmptySquare, EmptySquare, EmptySquare } ,
233     { WhitePawn,   WhitePawn,   WhitePawn,   WhitePawn, WhitePawn,
234       WhitePawn,   WhitePawn,   WhitePawn,   WhitePawn },
235     { EmptySquare, WhiteRook,   EmptySquare, EmptySquare, EmptySquare,
236       EmptySquare, EmptySquare, WhiteBishop, EmptySquare },
237     { WhiteLance,  WhiteKnight, WhiteSilver, WhiteGold, WhiteKing,
238       WhiteGold,   WhiteSilver, WhiteKnight, WhiteLance }
239 };
240
241 String gnuButtonStrings[] =
242 {
243     "Quit",       "Load Game",      "Machine White",  "Forward",
244     "Reset",      "Load Position",  "Machine Black",  "Backward",
245     "Flip View",  "Save Game",      "Force Moves",    "Pause",
246     "Hint",       "Save Position",  "Two Machines",   "Edit Position",
247     "Challenge",  "Select Level",   "Move NOW",
248 };
249
250 /* must be in same order as buttonStrings! */
251 XtActionProc gnuButtonProcs[] =
252 {
253     QuitProc,       LoadGameProc,      MachineWhiteProc,  ForwardProc,
254     ResetProc,      LoadPositionProc,  MachineBlackProc,  BackwardProc,
255     FlipViewProc,   SaveGameProc,      ForceProc,         PauseProc,
256     HintProc,       SavePositionProc,  TwoMachinesProc,   EditPositionProc,
257     ChallengeProc,  SelectLevelProc,   MoveNowProc,
258     NULL
259 };
260
261
262 String *buttonStrings;
263 XtActionProc *buttonProcs;
264 int buttonCount;
265
266 #define PIECE_MENU_SIZE 18
267
268 String pieceMenuStrings[PIECE_MENU_SIZE] =
269 {
270     "----",     "Pawn",          "Lance",       "Knight", "Silver",
271     "Gold",     "Bishop",        "Rook",
272     "PPawn",    "PLance",        "PKnight",     "PSilver",
273     "PBishop",  "PRook",         "King",
274     "----",     "Empty square",  "Clear board"
275 };
276
277 /* must be in same order as PieceMenuStrings! */
278 ShogiSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] =
279 {
280     {
281         (ShogiSquare) 0,  BlackPawn,    BlackLance,    BlackKnight,
282         BlackSilver,      BlackGold,    BlackBishop,   BlackRook,
283         BlackPPawn,       BlackPLance,  BlackPKnight,  BlackPSilver,
284         BlackPBishop,     BlackPRook,   BlackKing,
285         (ShogiSquare) 0,  EmptySquare,  ClearBoard
286     },
287     {
288         (ShogiSquare) 0,  WhitePawn,    WhiteLance,    WhiteKnight,
289         WhiteSilver,      WhiteGold,    WhiteBishop,   WhiteRook,
290         WhitePPawn,       WhitePLance,  WhitePKnight,  WhitePSilver,
291         WhitePBishop,     WhitePRook,   WhiteKing,
292         (ShogiSquare) 0,  EmptySquare,  ClearBoard
293     },
294 };
295
296
297 typedef struct
298 {
299     Pixel pieceColor;
300     Pixel squareColor;
301     Pixel charPieceColor;
302     Pixel zeroColor;
303     Pixel oneColor;
304     Boolean westernPieceSet;
305     int movesPerSession;
306     String initString;
307     String blackString;
308     String whiteString;
309     String firstShogiProgram;
310     String secondShogiProgram;
311     Boolean noShogiProgram;
312     String firstHost;
313     String secondHost;
314     String reverseBigSolidBitmap;
315     String reverseSmallSolidBitmap;
316     String normalBigSolidBitmap;
317     String normalSmallSolidBitmap;
318     String reversePawnBitmap;
319     String reverseLanceBitmap;
320     String reverseKnightBitmap;
321     String reverseSilverBitmap;
322     String reverseGoldBitmap;
323     String reverseRookBitmap;
324     String reverseBishopBitmap;
325     String reversePPawnBitmap;
326     String reversePLanceBitmap;
327     String reversePKnightBitmap;
328     String reversePSilverBitmap;
329     String reversePBishopBitmap;
330     String reversePRookBitmap;
331     String reverseKingBitmap;
332     String normalPawnBitmap;
333     String normalLanceBitmap;
334     String normalKnightBitmap;
335     String normalSilverBitmap;
336     String normalGoldBitmap;
337     String normalRookBitmap;
338     String normalBishopBitmap;
339     String normalPPawnBitmap;
340     String normalPLanceBitmap;
341     String normalPKnightBitmap;
342     String normalPSilverBitmap;
343     String normalPBishopBitmap;
344     String normalPRookBitmap;
345     String normalKingBitmap;
346     String remoteShell;
347     float  timeDelay;
348     String timeControl;
349     String gameIn;
350
351     Boolean autoSaveGames;
352     String loadGameFile;
353     String loadPositionFile;
354     String saveGameFile;
355     String savePositionFile;
356     String matchMode;
357     String challengeDisplay;
358     Boolean monoMode;
359     Boolean debugMode;
360     Boolean clockMode;
361     String boardSize;
362     Boolean Iconic;
363     String searchTime;
364     int searchDepth;
365     Boolean showCoords;
366     String mainFont;
367     String coordFont;
368     Boolean ringBellAfterMoves;
369     Boolean autoCallFlag;
370     int borderXoffset;
371     int borderYoffset;
372 } AppData, *AppDataPtr;
373
374
375 XtResource clientResources[] =
376 {
377     {
378         "pieceColor", "pieceColor", XtRPixel, sizeof(Pixel),
379         XtOffset(AppDataPtr, pieceColor), XtRString,
380         PIECE_COLOR
381     },
382     {
383         "charPieceColor", "CharPieceColor", XtRPixel, sizeof(Pixel),
384         XtOffset(AppDataPtr, charPieceColor), XtRString,
385         CHAR_PIECE_COLOR
386     },
387     {
388         "oneColor", "OneColor", XtRPixel, sizeof(Pixel),
389         XtOffset(AppDataPtr, oneColor), XtRString,
390         ONE_COLOR
391     },
392     {
393         "zeroColor", "ZeroColor", XtRPixel, sizeof(Pixel),
394         XtOffset(AppDataPtr, zeroColor), XtRString,
395         ZERO_COLOR
396     },
397     {
398         "squareColor", "squareColor", XtRPixel,
399         sizeof(Pixel), XtOffset(AppDataPtr, squareColor),
400         XtRString, SQUARE_COLOR
401     },
402     {
403         "westernPieceSet", "WesternPieceSet", XtRBoolean, sizeof(Boolean),
404         XtOffset(AppDataPtr, westernPieceSet), XtRString,
405         (XtPointer) False
406     },
407     {
408         "movesPerSession", "movesPerSession", XtRInt, sizeof(int),
409         XtOffset(AppDataPtr, movesPerSession), XtRImmediate,
410         (XtPointer) MOVES_PER_SESSION
411     },
412     {
413         "initString", "initString", XtRString, sizeof(String),
414         XtOffset(AppDataPtr, initString), XtRString, INIT_STRING
415     },
416     {
417         "blackString", "blackString", XtRString, sizeof(String),
418         XtOffset(AppDataPtr, blackString), XtRString, BLACK_STRING
419     },
420     {
421         "whiteString", "whiteString", XtRString, sizeof(String),
422         XtOffset(AppDataPtr, whiteString), XtRString, WHITE_STRING
423     },
424     {
425         "firstShogiProgram", "firstShogiProgram", XtRString,
426         sizeof(String), XtOffset(AppDataPtr, firstShogiProgram),
427         XtRString, FIRST_SHOGI_PROGRAM
428     },
429     {
430         "secondShogiProgram", "secondShogiProgram", XtRString,
431         sizeof(String), XtOffset(AppDataPtr, secondShogiProgram),
432         XtRString, SECOND_SHOGI_PROGRAM
433     },
434     {
435         "noShogiProgram", "noShogiProgram", XtRBoolean,
436         sizeof(Boolean), XtOffset(AppDataPtr, noShogiProgram),
437         XtRImmediate, (XtPointer) False
438     },
439     {
440         "firstHost", "firstHost", XtRString, sizeof(String),
441         XtOffset(AppDataPtr, firstHost), XtRString, FIRST_HOST
442     },
443     {
444         "secondHost", "secondHost", XtRString, sizeof(String),
445         XtOffset(AppDataPtr, secondHost), XtRString, SECOND_HOST
446     },
447     {
448         "reversePawnBitmap", "reversePawnBitmap", XtRString,
449         sizeof(String), XtOffset(AppDataPtr, reversePawnBitmap),
450         XtRString, NULL
451     },
452     {
453         "reverseLanceBitmap", "reverseLanceBitmap", XtRString,
454         sizeof(String), XtOffset(AppDataPtr, reverseLanceBitmap),
455         XtRString, NULL
456     },
457     {
458         "reverseKnightBitmap", "reverseKnightBitmap", XtRString,
459         sizeof(String), XtOffset(AppDataPtr, reverseKnightBitmap),
460         XtRString, NULL
461     },
462     {
463         "reverseSilverBitmap", "reverseSilverBitmap", XtRString,
464         sizeof(String), XtOffset(AppDataPtr, reverseSilverBitmap),
465         XtRString, NULL
466     },
467     {
468         "reverseGoldBitmap", "reverseGoldBitmap", XtRString,
469         sizeof(String), XtOffset(AppDataPtr, reverseGoldBitmap),
470         XtRString, NULL
471     },
472     {
473         "reverseRookBitmap", "reverseRookBitmap", XtRString,
474         sizeof(String), XtOffset(AppDataPtr, reverseRookBitmap),
475         XtRString, NULL
476     },
477     {
478         "reverseBishopBitmap", "reverseBishopBitmap", XtRString,
479         sizeof(String), XtOffset(AppDataPtr, reverseBishopBitmap),
480         XtRString, NULL
481     },
482     {
483         "reversePPawnBitmap", "reversePPawnBitmap", XtRString,
484         sizeof(String), XtOffset(AppDataPtr, reversePPawnBitmap),
485         XtRString, NULL
486     },
487     {
488         "reversePLanceBitmap", "reversePLanceBitmap", XtRString,
489         sizeof(String), XtOffset(AppDataPtr, reversePLanceBitmap),
490         XtRString, NULL
491     },
492     {
493         "reversePKnightBitmap", "reversePKnightBitmap", XtRString,
494         sizeof(String), XtOffset(AppDataPtr, reversePKnightBitmap),
495         XtRString, NULL
496     },
497     {
498         "reversePSilverBitmap", "reversePSilverBitmap", XtRString,
499         sizeof(String), XtOffset(AppDataPtr, reversePSilverBitmap),
500         XtRString, NULL
501     },
502     {
503         "reversePRookBitmap", "reversePRookBitmap", XtRString,
504         sizeof(String), XtOffset(AppDataPtr, reversePRookBitmap),
505         XtRString, NULL
506     },
507     {
508         "reversePBishopBitmap", "reversePBishopBitmap", XtRString,
509         sizeof(String), XtOffset(AppDataPtr, reversePBishopBitmap),
510         XtRString, NULL
511     },
512     {
513         "reverseKingBitmap", "reverseKingBitmap", XtRString,
514         sizeof(String), XtOffset(AppDataPtr, reverseKingBitmap),
515         XtRString, NULL
516     },
517     {
518         "normalPawnBitmap", "normalPawnBitmap", XtRString,
519         sizeof(String), XtOffset(AppDataPtr, normalPawnBitmap),
520         XtRString, NULL
521     },
522     {
523         "normalLanceBitmap", "normalLanceBitmap", XtRString,
524         sizeof(String), XtOffset(AppDataPtr, normalLanceBitmap),
525         XtRString, NULL
526     },
527     {
528         "normalKnightBitmap", "normalKnightBitmap", XtRString,
529         sizeof(String), XtOffset(AppDataPtr, normalKnightBitmap),
530         XtRString, NULL
531     },
532     {
533         "normalSilverBitmap", "normalSilverBitmap", XtRString,
534         sizeof(String), XtOffset(AppDataPtr, normalSilverBitmap),
535         XtRString, NULL
536     },
537     {
538         "normalGoldBitmap", "normalGoldBitmap", XtRString,
539         sizeof(String), XtOffset(AppDataPtr, normalGoldBitmap),
540         XtRString, NULL
541     },
542     {
543         "normalBishopBitmap", "normalBishopBitmap", XtRString,
544         sizeof(String), XtOffset(AppDataPtr, normalBishopBitmap),
545         XtRString, NULL
546     },
547     {
548         "normalRookBitmap", "normalRookBitmap", XtRString,
549         sizeof(String), XtOffset(AppDataPtr, normalRookBitmap),
550         XtRString, NULL
551     },
552     {
553         "normalPPawnBitmap", "normalPPawnBitmap", XtRString,
554         sizeof(String), XtOffset(AppDataPtr, normalPPawnBitmap),
555         XtRString, NULL
556     },
557     {
558         "normalPLanceBitmap", "normalPLanceBitmap", XtRString,
559         sizeof(String), XtOffset(AppDataPtr, normalPLanceBitmap),
560         XtRString, NULL
561     },
562     {
563         "normalPKnightBitmap", "normalPKnightBitmap", XtRString,
564         sizeof(String), XtOffset(AppDataPtr, normalPKnightBitmap),
565         XtRString, NULL
566     },
567     {
568         "normalPSilverBitmap", "normalPSilverBitmap", XtRString,
569         sizeof(String), XtOffset(AppDataPtr, normalPSilverBitmap),
570         XtRString, NULL
571     },
572     {
573         "normalPBishopBitmap", "normalPBishopBitmap", XtRString,
574         sizeof(String), XtOffset(AppDataPtr, normalPBishopBitmap),
575         XtRString, NULL
576     },
577     {
578         "normalPRookBitmap", "normalPRookBitmap", XtRString,
579         sizeof(String), XtOffset(AppDataPtr, normalPRookBitmap),
580         XtRString, NULL
581     },
582     {
583         "normalKingBitmap", "normalKingBitmap", XtRString,
584         sizeof(String), XtOffset(AppDataPtr, normalKingBitmap),
585         XtRString, NULL
586     },
587     {
588         "remoteShell", "remoteShell", XtRString, sizeof(String),
589         XtOffset(AppDataPtr, remoteShell), XtRString, "rsh"
590     },
591     {
592         "timeDelay", "timeDelay", XtRFloat, sizeof(float),
593         XtOffset(AppDataPtr, timeDelay), XtRString,
594         (XtPointer) TIME_DELAY
595     },
596     {
597         "timeControl", "timeControl", XtRString, sizeof(String),
598         XtOffset(AppDataPtr, timeControl), XtRString,
599         (XtPointer) TIME_CONTROL
600     },
601     {
602         "gameIn", "gameIn",
603         XtRBoolean, sizeof(Boolean),
604         XtOffset(AppDataPtr, gameIn), XtRImmediate,
605         (XtPointer) False
606     },
607     {
608         "autoSaveGames", "autoSaveGames", XtRBoolean,
609         sizeof(Boolean), XtOffset(AppDataPtr, autoSaveGames),
610         XtRImmediate, (XtPointer) False
611     },
612     {
613         "loadGameFile", "loadGameFile", XtRString, sizeof(String),
614         XtOffset(AppDataPtr, loadGameFile), XtRString, NULL
615     },
616     {
617         "loadPositionFile", "loadPositionFile", XtRString,
618         sizeof(String), XtOffset(AppDataPtr, loadPositionFile),
619         XtRString, NULL
620     },
621     {
622         "saveGameFile", "saveGameFile", XtRString, sizeof(String),
623         XtOffset(AppDataPtr, saveGameFile), XtRString, ""
624     },
625     {
626         "savePositionFile", "savePositionFile", XtRString,
627         sizeof(String), XtOffset(AppDataPtr, savePositionFile),
628         XtRString, ""
629     },
630     {
631         "challengeDisplay", "challengeDisplay", XtRString,
632         sizeof(String), XtOffset(AppDataPtr, challengeDisplay),
633         XtRString, NULL
634     },
635     {
636         "matchMode", "matchMode", XtRString, sizeof(String),
637         XtOffset(AppDataPtr, matchMode), XtRString, MATCH_MODE
638     },
639     {
640         "monoMode", "monoMode", XtRBoolean, sizeof(Boolean),
641         XtOffset(AppDataPtr, monoMode), XtRImmediate,
642         (XtPointer) False
643     },
644     {
645         "debugMode", "debugMode", XtRBoolean, sizeof(Boolean),
646         XtOffset(AppDataPtr, debugMode), XtRImmediate,
647         (XtPointer) False
648     },
649     {
650         "Iconic", "Iconic", XtRBoolean, sizeof(Boolean),
651         XtOffset(AppDataPtr, Iconic), XtRImmediate,
652         (XtPointer) False
653     },
654     {
655         "clockMode", "clockMode", XtRBoolean, sizeof(Boolean),
656         XtOffset(AppDataPtr, clockMode), XtRImmediate,
657         (XtPointer) True
658     },
659     {
660         "autoCallFlag", "autoCallFlag", XtRBoolean,
661         sizeof(Boolean), XtOffset(AppDataPtr, autoCallFlag),
662         XtRImmediate, (XtPointer) False
663     },
664     {
665         "boardSize", "boardSize", XtRString, sizeof(String),
666         XtOffset(AppDataPtr, boardSize), XtRString, DEFAULT_SIZE
667     },
668     {
669         "searchTime", "searchTime", XtRString, sizeof(String),
670         XtOffset(AppDataPtr, searchTime), XtRString,
671         (XtPointer) NULL
672     },
673     {
674         "searchDepth", "searchDepth", XtRInt, sizeof(int),
675         XtOffset(AppDataPtr, searchDepth), XtRImmediate,
676         (XtPointer) 0
677     },
678     {
679         "showCoords", "showCoords", XtRBoolean, sizeof(Boolean),
680         XtOffset(AppDataPtr, showCoords), XtRImmediate,
681         (XtPointer) False
682     },
683     {
684         "mainFont", "mainFont", XtRString, sizeof(String),
685         XtOffset(AppDataPtr, mainFont), XtRString, MAIN_FONT
686     },
687     {
688         "coordFont", "coordFont", XtRString, sizeof(String),
689         XtOffset(AppDataPtr, coordFont), XtRString, COORD_FONT
690     },
691     {
692         "ringBellAfterMoves", "ringBellAfterMoves",
693         XtRBoolean, sizeof(Boolean),
694         XtOffset(AppDataPtr, ringBellAfterMoves),
695         XtRImmediate, (XtPointer) False
696     },
697     {
698         "borderXoffset", "borderXoffset", XtRInt, sizeof(int),
699         XtOffset(AppDataPtr, borderXoffset), XtRImmediate,
700         (XtPointer) BORDER_X_OFFSET
701     },
702     {
703         "borderYoffset", "borderYOffset", XtRInt, sizeof(int),
704         XtOffset(AppDataPtr, borderYoffset), XtRImmediate,
705         (XtPointer) BORDER_Y_OFFSET
706     }
707 };
708
709
710 struct DisplayData
711 {
712     AppData appData;
713
714     Arg shellArgs[6];
715     Arg boardArgs[3];
716     Arg commandsArgs[7];
717     Arg messageArgs[3];
718     Arg timerArgs[2];
719     Arg titleArgs[2];
720
721     Pixmap reversePawnBitmap, reverseLanceBitmap,   reverseKnightBitmap,
722         reverseSilverBitmap,
723         reverseGoldBitmap,    reverseBishopBitmap,  reverseRookBitmap,
724         reversePPawnBitmap,   reversePLanceBitmap,  reversePKnightBitmap,
725         reversePSilverBitmap, reversePBishopBitmap, reversePRookBitmap,
726         reverseKingBitmap,
727         reverseBigSolidBitmap, reverseSmallSolidBitmap,
728         normalBigSolidBitmap,  normalSmallSolidBitmap,
729         normalPawnBitmap,      normalLanceBitmap,   normalKnightBitmap,
730         normalSilverBitmap,    normalGoldBitmap,
731         normalBishopBitmap,    normalRookBitmap,
732         normalPPawnBitmap,     normalPLanceBitmap,  normalPKnightBitmap,
733         normalPSilverBitmap,   normalPBishopBitmap, normalPRookBitmap,
734         normalKingBitmap,
735         iconPixmap;
736
737     Display *xDisplay;
738     int xScreen;
739     Window xBoardWindow;
740
741     GC squareGC, lineGC, pieceGC, oPieceGC, charPieceGC,
742         squareOffBoardGC, coordGC, dropPiece;
743
744     Font mainFontID, coordFontID;
745     XFontStruct *mainFontStruct, *coordFontStruct;
746
747     Widget shellWidget, formWidget, boardWidget,
748         commandsWidget, messageWidget,
749         blackTimerWidget, whiteTimerWidget,
750         titleWidget, widgetList[6],
751         promotionShell,
752         filemodeShell, challengeWidget;
753
754     XSegment gridSegments[(BOARD_SIZE + 1) * 2];
755
756     Pixel timerForegroundPixel, timerBackgroundPixel;
757
758     BoardSize boardSize;
759     int squareSize;
760     int black_pixel_is_zero;
761     int flipView;
762     int promotionUp;
763
764     Boolean monoMode, showCoords, Iconic;
765 };
766
767
768
769 struct DisplayData localPlayer, remotePlayer;
770
771
772 typedef struct
773 {
774     ShogiSquare piece;
775     int to_x, to_y;
776 } PromotionMoveInfo;
777
778 static PromotionMoveInfo pmi;  /* making this global is gross */
779
780
781 Pixmap *pieceToReverse[2][28] =
782 {
783     {
784         &localPlayer.reversePawnBitmap,
785         &localPlayer.reverseLanceBitmap,
786         &localPlayer.reverseKnightBitmap,
787         &localPlayer.reverseSilverBitmap,
788         &localPlayer.reverseGoldBitmap,
789         &localPlayer.reverseBishopBitmap,
790         &localPlayer.reverseRookBitmap,
791         &localPlayer.reversePPawnBitmap,
792         &localPlayer.reversePLanceBitmap,
793         &localPlayer.reversePKnightBitmap,
794         &localPlayer.reversePSilverBitmap,
795         &localPlayer.reversePBishopBitmap,
796         &localPlayer.reversePRookBitmap,
797         &localPlayer.reverseKingBitmap,
798         &localPlayer.reversePawnBitmap,
799         &localPlayer.reverseLanceBitmap,
800         &localPlayer.reverseKnightBitmap,
801         &localPlayer.reverseSilverBitmap,
802         &localPlayer.reverseGoldBitmap,
803         &localPlayer.reverseBishopBitmap,
804         &localPlayer.reverseRookBitmap,
805         &localPlayer.reversePPawnBitmap,
806         &localPlayer.reversePLanceBitmap,
807         &localPlayer.reversePKnightBitmap,
808         &localPlayer.reversePSilverBitmap,
809         &localPlayer.reversePBishopBitmap,
810         &localPlayer.reversePRookBitmap,
811         &localPlayer.reverseKingBitmap
812     },
813     {
814         &remotePlayer.reversePawnBitmap,
815         &remotePlayer.reverseLanceBitmap,
816         &remotePlayer.reverseKnightBitmap,
817         &remotePlayer.reverseSilverBitmap,
818         &remotePlayer.reverseGoldBitmap,
819         &remotePlayer.reverseBishopBitmap,
820         &remotePlayer.reverseRookBitmap,
821         &remotePlayer.reversePPawnBitmap,
822         &remotePlayer.reversePLanceBitmap,
823         &remotePlayer.reversePKnightBitmap,
824         &remotePlayer.reversePSilverBitmap,
825         &remotePlayer.reversePBishopBitmap,
826         &remotePlayer.reversePRookBitmap,
827         &remotePlayer.reverseKingBitmap,
828         &remotePlayer.reversePawnBitmap,
829         &remotePlayer.reverseLanceBitmap,
830         &remotePlayer.reverseKnightBitmap,
831         &remotePlayer.reverseSilverBitmap,
832         &remotePlayer.reverseGoldBitmap,
833         &remotePlayer.reverseBishopBitmap,
834         &remotePlayer.reverseRookBitmap,
835         &remotePlayer.reversePPawnBitmap,
836         &remotePlayer.reversePLanceBitmap,
837         &remotePlayer.reversePKnightBitmap,
838         &remotePlayer.reversePSilverBitmap,
839         &remotePlayer.reversePBishopBitmap,
840         &remotePlayer.reversePRookBitmap,
841         &remotePlayer.reverseKingBitmap
842     }
843 };
844
845
846
847 Pixmap *pieceToNormal[2][28] =
848 {
849     {
850         &localPlayer.normalPawnBitmap,
851         &localPlayer.normalLanceBitmap,
852         &localPlayer.normalKnightBitmap,
853         &localPlayer.normalSilverBitmap,
854         &localPlayer.normalGoldBitmap,
855         &localPlayer.normalBishopBitmap,
856         &localPlayer.normalRookBitmap,
857         &localPlayer.normalPPawnBitmap,
858         &localPlayer.normalPLanceBitmap,
859         &localPlayer.normalPKnightBitmap,
860         &localPlayer.normalPSilverBitmap,
861         &localPlayer.normalPBishopBitmap,
862         &localPlayer.normalPRookBitmap,
863         &localPlayer.normalKingBitmap,
864         &localPlayer.normalPawnBitmap,
865         &localPlayer.normalLanceBitmap,
866         &localPlayer.normalKnightBitmap,
867         &localPlayer.normalSilverBitmap,
868         &localPlayer.normalGoldBitmap,
869         &localPlayer.normalBishopBitmap,
870         &localPlayer.normalRookBitmap,
871         &localPlayer.normalPPawnBitmap,
872         &localPlayer.normalPLanceBitmap,
873         &localPlayer.normalPKnightBitmap,
874         &localPlayer.normalPSilverBitmap,
875         &localPlayer.normalPBishopBitmap,
876         &localPlayer.normalPRookBitmap,
877         &localPlayer.normalKingBitmap
878     },
879     {
880         &remotePlayer.normalPawnBitmap,
881         &remotePlayer.normalLanceBitmap,
882         &remotePlayer.normalKnightBitmap,
883         &remotePlayer.normalSilverBitmap,
884         &remotePlayer.normalGoldBitmap,
885         &remotePlayer.normalBishopBitmap,
886         &remotePlayer.normalRookBitmap,
887         &remotePlayer.normalPPawnBitmap,
888         &remotePlayer.normalPLanceBitmap,
889         &remotePlayer.normalPKnightBitmap,
890         &remotePlayer.normalPSilverBitmap,
891         &remotePlayer.normalPBishopBitmap,
892         &remotePlayer.normalPRookBitmap,
893         &remotePlayer.normalKingBitmap,
894         &remotePlayer.normalPawnBitmap,
895         &remotePlayer.normalLanceBitmap,
896         &remotePlayer.normalKnightBitmap,
897         &remotePlayer.normalSilverBitmap,
898         &remotePlayer.normalGoldBitmap,
899         &remotePlayer.normalBishopBitmap,
900         &remotePlayer.normalRookBitmap,
901         &remotePlayer.normalPPawnBitmap,
902         &remotePlayer.normalPLanceBitmap,
903         &remotePlayer.normalPKnightBitmap,
904         &remotePlayer.normalPSilverBitmap,
905         &remotePlayer.normalPBishopBitmap,
906         &remotePlayer.normalPRookBitmap,
907         &remotePlayer.normalKingBitmap
908     }
909 };
910
911
912
913 Pixmap *pieceToReverseSolid[2][28] =
914 {
915     {
916         &localPlayer.reverseSmallSolidBitmap,
917         &localPlayer.reverseSmallSolidBitmap,
918         &localPlayer.reverseSmallSolidBitmap,
919         &localPlayer.reverseBigSolidBitmap,
920         &localPlayer.reverseBigSolidBitmap,
921         &localPlayer.reverseBigSolidBitmap,
922         &localPlayer.reverseBigSolidBitmap,
923         &localPlayer.reverseSmallSolidBitmap,
924         &localPlayer.reverseSmallSolidBitmap,
925         &localPlayer.reverseSmallSolidBitmap,
926         &localPlayer.reverseBigSolidBitmap,
927         &localPlayer.reverseBigSolidBitmap,
928         &localPlayer.reverseBigSolidBitmap,
929         &localPlayer.reverseBigSolidBitmap,
930         &localPlayer.reverseSmallSolidBitmap,
931         &localPlayer.reverseSmallSolidBitmap,
932         &localPlayer.reverseSmallSolidBitmap,
933         &localPlayer.reverseBigSolidBitmap,
934         &localPlayer.reverseBigSolidBitmap,
935         &localPlayer.reverseBigSolidBitmap,
936         &localPlayer.reverseBigSolidBitmap,
937         &localPlayer.reverseSmallSolidBitmap,
938         &localPlayer.reverseSmallSolidBitmap,
939         &localPlayer.reverseSmallSolidBitmap,
940         &localPlayer.reverseBigSolidBitmap,
941         &localPlayer.reverseBigSolidBitmap,
942         &localPlayer.reverseBigSolidBitmap,
943         &localPlayer.reverseBigSolidBitmap
944     },
945     {
946         &remotePlayer.reverseSmallSolidBitmap,
947         &remotePlayer.reverseSmallSolidBitmap,
948         &remotePlayer.reverseSmallSolidBitmap,
949         &remotePlayer.reverseBigSolidBitmap,
950         &remotePlayer.reverseBigSolidBitmap,
951         &remotePlayer.reverseBigSolidBitmap,
952         &remotePlayer.reverseBigSolidBitmap,
953         &remotePlayer.reverseSmallSolidBitmap,
954         &remotePlayer.reverseSmallSolidBitmap,
955         &remotePlayer.reverseSmallSolidBitmap,
956         &remotePlayer.reverseBigSolidBitmap,
957         &remotePlayer.reverseBigSolidBitmap,
958         &remotePlayer.reverseBigSolidBitmap,
959         &remotePlayer.reverseBigSolidBitmap,
960         &remotePlayer.reverseSmallSolidBitmap,
961         &remotePlayer.reverseSmallSolidBitmap,
962         &remotePlayer.reverseSmallSolidBitmap,
963         &remotePlayer.reverseBigSolidBitmap,
964         &remotePlayer.reverseBigSolidBitmap,
965         &remotePlayer.reverseBigSolidBitmap,
966         &remotePlayer.reverseBigSolidBitmap,
967         &remotePlayer.reverseSmallSolidBitmap,
968         &remotePlayer.reverseSmallSolidBitmap,
969         &remotePlayer.reverseSmallSolidBitmap,
970         &remotePlayer.reverseBigSolidBitmap,
971         &remotePlayer.reverseBigSolidBitmap,
972         &remotePlayer.reverseBigSolidBitmap,
973         &remotePlayer.reverseBigSolidBitmap
974     }
975 };
976
977
978
979 Pixmap *pieceToNormalSolid[2][28] =
980 {
981     {
982         &localPlayer.normalSmallSolidBitmap,
983         &localPlayer.normalSmallSolidBitmap,
984         &localPlayer.normalSmallSolidBitmap,
985         &localPlayer.normalBigSolidBitmap,
986         &localPlayer.normalBigSolidBitmap,
987         &localPlayer.normalBigSolidBitmap,
988         &localPlayer.normalBigSolidBitmap,
989         &localPlayer.normalSmallSolidBitmap,
990         &localPlayer.normalSmallSolidBitmap,
991         &localPlayer.normalSmallSolidBitmap,
992         &localPlayer.normalBigSolidBitmap,
993         &localPlayer.normalBigSolidBitmap,
994         &localPlayer.normalBigSolidBitmap,
995         &localPlayer.normalBigSolidBitmap,
996         &localPlayer.normalSmallSolidBitmap,
997         &localPlayer.normalSmallSolidBitmap,
998         &localPlayer.normalSmallSolidBitmap,
999         &localPlayer.normalBigSolidBitmap,
1000         &localPlayer.normalBigSolidBitmap,
1001         &localPlayer.normalBigSolidBitmap,
1002         &localPlayer.normalBigSolidBitmap,
1003         &localPlayer.normalSmallSolidBitmap,
1004         &localPlayer.normalSmallSolidBitmap,
1005         &localPlayer.normalSmallSolidBitmap,
1006         &localPlayer.normalBigSolidBitmap,
1007         &localPlayer.normalBigSolidBitmap,
1008         &localPlayer.normalBigSolidBitmap,
1009         &localPlayer.normalBigSolidBitmap
1010     },
1011     {
1012         &remotePlayer.normalSmallSolidBitmap,
1013         &remotePlayer.normalSmallSolidBitmap,
1014         &remotePlayer.normalSmallSolidBitmap,
1015         &remotePlayer.normalBigSolidBitmap,
1016         &remotePlayer.normalBigSolidBitmap,
1017         &remotePlayer.normalBigSolidBitmap,
1018         &remotePlayer.normalBigSolidBitmap,
1019         &remotePlayer.normalSmallSolidBitmap,
1020         &remotePlayer.normalSmallSolidBitmap,
1021         &remotePlayer.normalSmallSolidBitmap,
1022         &remotePlayer.normalBigSolidBitmap,
1023         &remotePlayer.normalBigSolidBitmap,
1024         &remotePlayer.normalBigSolidBitmap,
1025         &remotePlayer.normalBigSolidBitmap,
1026         &remotePlayer.normalSmallSolidBitmap,
1027         &remotePlayer.normalSmallSolidBitmap,
1028         &remotePlayer.normalSmallSolidBitmap,
1029         &remotePlayer.normalBigSolidBitmap,
1030         &remotePlayer.normalBigSolidBitmap,
1031         &remotePlayer.normalBigSolidBitmap,
1032         &remotePlayer.normalBigSolidBitmap,
1033         &remotePlayer.normalSmallSolidBitmap,
1034         &remotePlayer.normalSmallSolidBitmap,
1035         &remotePlayer.normalSmallSolidBitmap,
1036         &remotePlayer.normalBigSolidBitmap,
1037         &remotePlayer.normalBigSolidBitmap,
1038         &remotePlayer.normalBigSolidBitmap,
1039         &remotePlayer.normalBigSolidBitmap
1040     }
1041 };
1042
1043
1044
1045 int pieceIsPromoted[] =
1046 {
1047     False, False, False, False, False, False, False,
1048     True,  True,  True,  True,  True,  True,  False,
1049     False, False, False, False, False, False, False,
1050     True,  True,  True,  True,  True,  True,  False,
1051     False
1052 };
1053
1054
1055 int piecePromotable[] =
1056 {
1057     True,  True,  True,  True,  False, True,  True,
1058     False, False, False, False, False, False, False,
1059     True,  True,  True,  True,  False, True,  True,
1060     False, False, False, False, False, False, False,
1061     False
1062 };
1063
1064
1065 char pieceToChar[] =
1066 {
1067     'P', 'L', 'N', 'S', 'G', 'B', 'R', 'P', 'L', 'N', 'S', 'B', 'R', 'K',
1068     'p', 'l', 'n', 's', 'g', 'b', 'r', 'p', 'l', 'n', 's', 'b', 'r', 'k',
1069     '.'
1070 };
1071
1072
1073 ShogiSquare pieceToPromoted[] =
1074 {
1075     BlackPPawn,   BlackPLance, BlackPKnight, BlackPSilver, BlackGold,
1076     BlackPBishop, BlackPRook,
1077     BlackPPawn,   BlackPLance, BlackPKnight, BlackPSilver,
1078     BlackPBishop, BlackPRook,  BlackKing,
1079     WhitePPawn,   WhitePLance, WhitePKnight, WhitePSilver, WhiteGold,
1080     WhitePBishop, WhitePRook,
1081     WhitePPawn,   WhitePLance, WhitePKnight, WhitePSilver,
1082     WhitePBishop, WhitePRook,  WhiteKing
1083 };
1084
1085
1086
1087 XrmOptionDescRec shellOptions[] =
1088 {
1089     { "-pieceColor", "pieceColor", XrmoptionSepArg, NULL },
1090     { "-pc", "pieceColor", XrmoptionSepArg, NULL },
1091     { "-charPieceColor", "charPieceColor", XrmoptionSepArg, NULL },
1092     { "-cpc", "charPieceColor", XrmoptionSepArg, NULL },
1093     { "-zeroColor", "zeroColor", XrmoptionSepArg, NULL },
1094     { "-zc", "zeroColor", XrmoptionSepArg, NULL },
1095     { "-oneColor", "oneColor", XrmoptionSepArg, NULL },
1096     { "-oc", "oneColor", XrmoptionSepArg, NULL },
1097     { "-squareColor", "squareColor", XrmoptionSepArg, NULL },
1098     { "-sc", "squareColor", XrmoptionSepArg, NULL },
1099     { "-westernPieceSet", "westernPieceSet", XrmoptionSepArg, NULL },
1100     { "-wps", "westernPieceSet", XrmoptionSepArg, NULL },
1101     { "-movesPerSession", "movesPerSession", XrmoptionSepArg, NULL },
1102     { "-mps", "movesPerSession", XrmoptionSepArg, NULL },
1103     { "-firstShogiProgram", "firstShogiProgram", XrmoptionSepArg, NULL },
1104     { "-fsp", "firstShogiProgram", XrmoptionSepArg, NULL },
1105     { "-secondShogiProgram", "secondShogiProgram", XrmoptionSepArg, NULL },
1106     { "-ssp", "secondShogiProgram", XrmoptionSepArg, NULL },
1107     { "-noShogiProgram", "noShogiProgram", XrmoptionSepArg, NULL },
1108     { "-nsp", "noShogiProgram", XrmoptionSepArg, NULL },
1109     { "-firstHost", "firstHost", XrmoptionSepArg, NULL },
1110     { "-fh", "firstHost", XrmoptionSepArg, NULL },
1111     { "-secondHost", "secondHost", XrmoptionSepArg, NULL },
1112     { "-sh", "secondHost", XrmoptionSepArg, NULL },
1113     { "-reversePawnBitmap", "reversePawnBitmap", XrmoptionSepArg, NULL },
1114     { "-rpb", "reversePawnBitmap", XrmoptionSepArg, NULL },
1115     { "-reverseLanceBitmap", "reverseLanceBitmap", XrmoptionSepArg, NULL },
1116     { "-rlb", "reverseLanceBitmap", XrmoptionSepArg, NULL },
1117     { "-reverseKnightBitmap", "reverseKnightBitmap", XrmoptionSepArg, NULL },
1118     { "-rnb", "reverseKnightBitmap", XrmoptionSepArg, NULL },
1119     { "-reverseSilverBitmap", "reverseSilverBitmap", XrmoptionSepArg, NULL },
1120     { "-rsb", "reverseSilverBitmap", XrmoptionSepArg, NULL },
1121     { "-reverseGoldBitmap", "reverseGoldBitmap", XrmoptionSepArg, NULL },
1122     { "-rgb", "reverseGoldBitmap", XrmoptionSepArg, NULL },
1123     { "-reverseRookBitmap", "reverseRookBitmap", XrmoptionSepArg, NULL },
1124     { "-rrb", "reverseRookBitmap", XrmoptionSepArg, NULL },
1125     { "-reverseBishopBitmap", "reverseBishopBitmap", XrmoptionSepArg, NULL },
1126     { "-rbb", "reverseBishopBitmap", XrmoptionSepArg, NULL },
1127     { "-reversePPawnBitmap", "reversePPawnBitmap",
1128       XrmoptionSepArg, NULL },
1129     { "-rppb", "reversePPawnBitmap", XrmoptionSepArg, NULL },
1130     { "-reversePLanceBitmap", "reversePLanceBitmap",
1131       XrmoptionSepArg, NULL },
1132     { "-rplb", "reversePLanceBitmap", XrmoptionSepArg, NULL },
1133     { "-reversePKnightBitmap", "reversePKnightBitmap",
1134       XrmoptionSepArg, NULL },
1135     { "-rpnb", "reversePKnightBitmap", XrmoptionSepArg, NULL },
1136     { "-reversePSilverBitmap", "reversePSilverBitmap",
1137       XrmoptionSepArg, NULL },
1138     { "-rpsb", "reversePSilverBitmap", XrmoptionSepArg, NULL },
1139     { "-reversePRookBitmap", "reversePRookBitmap",
1140       XrmoptionSepArg, NULL },
1141     { "-rprb", "reversePRookBitmap", XrmoptionSepArg, NULL },
1142     { "-reversePBishopBitmap", "reversePBishopBitmap",
1143       XrmoptionSepArg, NULL },
1144     { "-rpbb", "reversePBishopBitmap", XrmoptionSepArg, NULL },
1145     { "-reverseKingBitmap", "reverseKingBitmap", XrmoptionSepArg, NULL },
1146     { "-rkb", "reverseKingBitmap", XrmoptionSepArg, NULL },
1147     { "-outlinePawnBitmap", "outlinePawnBitmap", XrmoptionSepArg, NULL },
1148     { "-opb", "normalPawnBitmap", XrmoptionSepArg, NULL },
1149     { "-normalLanceBitmap", "normalLanceBitmap", XrmoptionSepArg, NULL },
1150     { "-olb", "normalLanceBitmap", XrmoptionSepArg, NULL },
1151     { "-normalKnightBitmap", "normalKnightBitmap", XrmoptionSepArg, NULL },
1152     { "-onb", "normalKnightBitmap", XrmoptionSepArg, NULL },
1153     { "-normalSilverBitmap", "normalSilverBitmap", XrmoptionSepArg, NULL },
1154     { "-osb", "normalSilverBitmap", XrmoptionSepArg, NULL },
1155     { "-normalGoldBitmap", "normalGoldBitmap", XrmoptionSepArg, NULL },
1156     { "-ogb", "normalGoldBitmap", XrmoptionSepArg, NULL },
1157     { "-normalRookBitmap", "normalRookBitmap", XrmoptionSepArg, NULL },
1158     { "-orb", "normalRookBitmap", XrmoptionSepArg, NULL },
1159     { "-normalBishopBitmap", "normalBishopBitmap", XrmoptionSepArg, NULL },
1160     { "-obb", "normalBishopBitmap", XrmoptionSepArg, NULL },
1161     { "-normalPPawnBitmap", "normalPPawnBitmap", XrmoptionSepArg, NULL },
1162     { "-oppb", "normalPPawnBitmap", XrmoptionSepArg, NULL },
1163     { "-normalPLanceBitmap", "normalPLanceBitmap", XrmoptionSepArg, NULL },
1164     { "-oplb", "normalPLanceBitmap", XrmoptionSepArg, NULL },
1165     { "-normalPKnightBitmap", "normalPKnightBitmap", XrmoptionSepArg, NULL },
1166     { "-opnb", "normalPKnightBitmap", XrmoptionSepArg, NULL },
1167     { "-normalPSilverBitmap", "normalPSilverBitmap", XrmoptionSepArg, NULL },
1168     { "-opsb", "normalPSilverBitmap", XrmoptionSepArg, NULL },
1169     { "-normalPRookBitmap", "normalPRookBitmap", XrmoptionSepArg, NULL },
1170     { "-oprb", "normalPRookBitmap", XrmoptionSepArg, NULL },
1171     { "-normalPBishopBitmap", "normalPBishopBitmap", XrmoptionSepArg, NULL },
1172     { "-opbb", "normalPBishopBitmap", XrmoptionSepArg, NULL },
1173     { "-normalKingBitmap", "normalKingBitmap", XrmoptionSepArg, NULL },
1174     { "-okb", "outlineKingBitmap", XrmoptionSepArg, NULL },
1175     { "-remoteShell", "remoteShell", XrmoptionSepArg, NULL },
1176     { "-rsh", "remoteShell", XrmoptionSepArg, NULL },
1177     { "-timeDelay", "timeDelay", XrmoptionSepArg, NULL },
1178     { "-td", "timeDelay", XrmoptionSepArg, NULL },
1179     { "-timeControl", "timeControl", XrmoptionSepArg, NULL },
1180     { "-tc", "timeControl", XrmoptionSepArg, NULL },
1181     { "-gameIn", "gameIn", XrmoptionSepArg, NULL },
1182     { "-gi", "gameIn", XrmoptionSepArg, NULL },
1183     { "-loadGameFile", "loadGameFile", XrmoptionSepArg, NULL },
1184     { "-lgf", "loadGameFile", XrmoptionSepArg, NULL },
1185     { "-loadPositionFile", "loadPositionFile", XrmoptionSepArg, NULL },
1186     { "-lpf", "loadPositionFile", XrmoptionSepArg, NULL },
1187     { "-saveGameFile", "saveGameFile", XrmoptionSepArg, NULL },
1188     { "-sgf", "saveGameFile", XrmoptionSepArg, NULL },
1189     { "-savePositionFile", "savePositionFile", XrmoptionSepArg, NULL },
1190     { "-spf", "savePositionFile", XrmoptionSepArg, NULL },
1191     { "-challengeDisplay", "challengeDisplay", XrmoptionSepArg, NULL },
1192     { "-cd", "challengeDisplay", XrmoptionSepArg, NULL },
1193     { "-matchMode", "matchMode", XrmoptionSepArg, NULL },
1194     { "-mm", "matchMode", XrmoptionSepArg, NULL },
1195     { "-monoMode", "monoMode", XrmoptionSepArg, NULL },
1196     { "-mono", "monoMode", XrmoptionSepArg, NULL },
1197     { "-debugMode", "debugMode", XrmoptionSepArg, NULL },
1198     { "-debug", "debugMode", XrmoptionSepArg, NULL },
1199     { "-clockMode", "clockMode", XrmoptionSepArg, NULL },
1200     { "-clock", "clockMode", XrmoptionSepArg, NULL },
1201     { "-boardSize", "boardSize", XrmoptionSepArg, NULL },
1202     { "-size", "boardSize", XrmoptionSepArg, NULL },
1203     { "-searchTime", "searchTime", XrmoptionSepArg, NULL },
1204     { "-st", "searchTime", XrmoptionSepArg, NULL },
1205     { "-searchDepth", "searchDepth", XrmoptionSepArg, NULL },
1206     { "-sd", "searchDepth", XrmoptionSepArg, NULL },
1207     { "-showCoords", "showCoords", XrmoptionSepArg, NULL },
1208     { "-coords", "showCoords", XrmoptionSepArg, NULL },
1209     { "-iconic", "Iconic", XrmoptionNoArg, "True" }
1210 };
1211
1212
1213
1214 XtActionsRec boardActions[] =
1215 {
1216     { "DrawPosition",   (XtActionProc) DrawPosition },
1217     { "HandleUserMove", (XtActionProc) HandleUserMove },
1218     { "ResetProc",      (XtActionProc) ResetProc },
1219     { "ResetFileProc",  (XtActionProc) ResetFileProc },
1220     { "LoadGameProc",   (XtActionProc) LoadGameProc },
1221     { "QuitProc",       (XtActionProc) QuitProc },
1222     { "ForwardProc",    (XtActionProc) ForwardProc },
1223     { "BackwardProc",   (XtActionProc) BackwardProc },
1224     { "PauseProc",      (XtActionProc) PauseProc },
1225     { "Iconify",        (XtActionProc) Iconify },
1226     { "FileNameAction", (XtActionProc) FileNameAction },
1227     { "PieceMenuPopup", (XtActionProc) PieceMenuPopup },
1228     { "SetBlackToPlay", (XtActionProc) SetBlackToPlay },
1229     { "SetWhiteToPlay", (XtActionProc) SetWhiteToPlay }
1230 };
1231
1232
1233 char translationsTable[] =
1234 "<Expose>: DrawPosition() \n \
1235 <Btn1Down>: HandleUserMove() \n \
1236 <Btn1Up>: HandleUserMove() \n \
1237 <Btn2Down>: XawPositionSimpleMenu(menuW) PieceMenuPopup(menuW) \n \
1238 <Btn3Down>: XawPositionSimpleMenu(menuB) PieceMenuPopup(menuB) \n \
1239 <Key>r: ResetFileProc() ResetProc() \n \
1240 <Key>R: ResetFileProc() ResetProc() \n \
1241 <Key>g: LoadGameProc() \n \
1242 <Key>G: LoadGameProc() \n \
1243 <Key>q: QuitProc() \n \
1244 <Key>Q: QuitProc() \n \
1245 <Message>WM_PROTOCOLS: QuitProc() \n \
1246 <Key>f: ForwardProc() \n \
1247 <Key>F: ForwardProc() \n \
1248 <Key>b: BackwardProc() \n \
1249 <Key>B: BackwardProc() \n \
1250 <Key>p: PauseProc() \n \
1251 <Key>P: PauseProc() \n \
1252 <Key>i: Iconify() \n \
1253 <Key>I: Iconify() \n \
1254 <Key>c: Iconify() \n \
1255 <Key>C: Iconify() \n";
1256
1257
1258 char translationsTableReduced[] =
1259 "<Expose>: DrawPosition() \n \
1260 <Btn1Down>: HandleUserMove() \n \
1261 <Btn1Up>: HandleUserMove() \n \
1262 <Message>WM_PROTOCOLS: QuitProc() \n";
1263
1264
1265 char blackTranslations[] = "<BtnDown>: SetBlackToPlay()\n";
1266 char whiteTranslations[] = "<BtnDown>: SetWhiteToPlay()\n";
1267
1268 String xshogiResources[] =
1269 {
1270     DEFAULT_FONT,
1271     "*Dialog*value.translations: #override "
1272     "\\n <Key>Return: FileNameAction()",
1273     NULL
1274 };
1275
1276
1277 int global_argc;       /* number of command args */
1278 char *global_argv[10]; /* pointers to up to 10 command args */
1279
1280
1281
1282 static struct DisplayData *player;
1283
1284
1285 typedef struct
1286 {
1287     char mode[2];
1288     char name[100];
1289 } FileModeInfo;
1290
1291 static FileModeInfo fmi;
1292
1293
1294 /*
1295  * This is a hack that allows the parser to tell the program
1296  * that the game it's loading has ended.
1297  */
1298
1299 int loaded_game_finished = 0;
1300
1301
1302 /**********************************************************************
1303  *
1304  * End of globals.
1305  *
1306  **********************************************************************/
1307
1308
1309 void
1310 CreatePlayerWindow(void)
1311 {
1312     int mainFontPxlSize, coordFontPxlSize;
1313     int min, sec, matched;
1314     XSetWindowAttributes window_attributes;
1315     char buf[MSG_SIZ];
1316     Arg args[10];
1317     Dimension timerWidth, boardWidth, commandsWidth, w, h;
1318     int local;
1319     int fromRemotePlayer = (player == &remotePlayer);
1320
1321     player->monoMode = player->appData.monoMode;
1322     player->showCoords = player->appData.showCoords;
1323
1324     /*
1325      * Parse timeControl resource.
1326      */
1327
1328     if (player->appData.timeControl != NULL)
1329     {
1330         matched = sscanf(player->appData.timeControl, "%d:%d", &min, &sec);
1331
1332         if (matched == 1)
1333         {
1334             timeControl = min * 60 * 1000;
1335         }
1336         else if (matched == 2)
1337         {
1338             timeControl = (min * 60 + sec) * 1000;
1339         }
1340         else
1341         {
1342             fprintf(stderr, "%s: bad timeControl option %s\n",
1343                     programName, player->appData.timeControl);
1344             Usage();
1345         }
1346     }
1347
1348     /*
1349      * Parse searchTime resource
1350      */
1351
1352     if (player->appData.searchTime != NULL)
1353     {
1354         matched = sscanf(player->appData.searchTime, "%d:%d", &min, &sec);
1355
1356         if (matched == 1)
1357         {
1358             searchTime = min * 60;
1359         }
1360         else if (matched == 2)
1361         {
1362             searchTime = min * 60 + sec;
1363         }
1364         else
1365         {
1366             fprintf(stderr, "%s: bad searchTime option %s\n",
1367                     programName, player->appData.searchTime);
1368             Usage();
1369         }
1370     }
1371
1372     if ((player->appData.searchTime != NULL)
1373         || (player->appData.searchDepth > 0)
1374         || player->appData.noShogiProgram)
1375     {
1376         player->appData.clockMode = False;
1377     }
1378
1379     player->Iconic = False;
1380     player->boardSize = Small;
1381     player->squareSize = SMALL_SQUARE_SIZE;
1382     player->flipView = (player == &remotePlayer);
1383     player->promotionUp = False;
1384
1385     /*
1386      * Determine boardSize.
1387      */
1388
1389     if (strcasecmp(player->appData.boardSize, "Large") == 0)
1390     {
1391         player->boardSize = Large;
1392     }
1393     else if (strcasecmp(player->appData.boardSize, "Medium") == 0)
1394     {
1395         player->boardSize = Medium;
1396     }
1397     else if (strcasecmp(player->appData.boardSize, "Small") == 0)
1398     {
1399         player->boardSize = Small;
1400     }
1401     else
1402     {
1403         fprintf(stderr, "%s: bad boardSize option %s\n",
1404                 programName, player->appData.boardSize);
1405         Usage();
1406     }
1407
1408     if ((local = (player == &localPlayer)))
1409     {
1410         player->xDisplay = XtDisplay(player->shellWidget);
1411         player->xScreen = DefaultScreen(player->xDisplay);
1412     }
1413
1414 #undef DONT_ADJUST_BOARDSIZE
1415 #ifndef DONT_ADJUST_BOARDSIZE
1416     if (((DisplayWidth(player->xDisplay, player->xScreen) < 800)
1417          || (DisplayHeight(player->xDisplay, player->xScreen) < 800))
1418         && (player->boardSize == Large))
1419     {
1420         player->boardSize = Medium;
1421     }
1422 #endif
1423
1424     switch (player->boardSize)
1425     {
1426     case Small:
1427         player->squareSize = SMALL_SQUARE_SIZE;
1428         mainFontPxlSize    = 11;
1429         coordFontPxlSize   = 10;
1430         break;
1431
1432     case Medium:
1433         player->squareSize = MEDIUM_SQUARE_SIZE;
1434         mainFontPxlSize    = 17;
1435         coordFontPxlSize   = 12;
1436         break;
1437
1438     default:
1439     case Large:
1440         player->squareSize = LARGE_SQUARE_SIZE;
1441         mainFontPxlSize    = 17;
1442         coordFontPxlSize   = 14;
1443         break;
1444     }
1445
1446     /*
1447      * Detect if there are not enough colors are available and adapt.
1448      */
1449
1450     if (DefaultDepth(player->xDisplay, player->xScreen) <= 2)
1451         player->monoMode = True;
1452
1453     /*
1454      * Determine what fonts to use.
1455      */
1456
1457     player->appData.mainFont
1458         = FindFont(player->appData.mainFont, mainFontPxlSize);
1459     player->mainFontID
1460         = XLoadFont(player->xDisplay, player->appData.mainFont);
1461     player->mainFontStruct
1462         = XQueryFont(player->xDisplay, player->mainFontID);
1463     player->appData.coordFont
1464         = FindFont(player->appData.coordFont, coordFontPxlSize);
1465     player->coordFontID
1466         = XLoadFont(player->xDisplay, player->appData.coordFont);
1467     player->coordFontStruct
1468         = XQueryFont(player->xDisplay, player->coordFontID);
1469
1470     /*
1471      * Set default arguments.
1472      */
1473
1474     XtSetArg(player->shellArgs[0], XtNwidth, 0);
1475     XtSetArg(player->shellArgs[1], XtNheight, 0);
1476     XtSetArg(player->shellArgs[2], XtNminWidth, 0);
1477     XtSetArg(player->shellArgs[3], XtNminHeight, 0);
1478     XtSetArg(player->shellArgs[4], XtNmaxWidth, 0);
1479     XtSetArg(player->shellArgs[5], XtNmaxHeight, 0);
1480
1481     XtSetArg(player->boardArgs[0], XtNborderWidth, 0);
1482     XtSetArg(player->boardArgs[1], XtNwidth,
1483              LINE_GAP + (BOARD_SIZE + 4)
1484              * (SMALL_SQUARE_SIZE + LINE_GAP));
1485     XtSetArg(player->boardArgs[2], XtNheight,
1486              LINE_GAP + BOARD_SIZE
1487              * (SMALL_SQUARE_SIZE + LINE_GAP));
1488
1489     XtSetArg(player->commandsArgs[0], XtNborderWidth, 0);
1490     XtSetArg(player->commandsArgs[1], XtNdefaultColumns, 4);
1491     XtSetArg(player->commandsArgs[2], XtNforceColumns, True);
1492     XtSetArg(player->commandsArgs[3], XtNcolumnSpacing, 12);
1493     XtSetArg(player->commandsArgs[4], XtNlist, (XtArgVal) buttonStrings);
1494     XtSetArg(player->commandsArgs[5], XtNnumberStrings, buttonCount);
1495     XtSetArg(player->commandsArgs[6], XtNfont, player->mainFontStruct);
1496
1497     XtSetArg(player->messageArgs[0], XtNborderWidth, 0);
1498     XtSetArg(player->messageArgs[1], XtNjustify, (XtArgVal) XtJustifyLeft);
1499     XtSetArg(player->messageArgs[2], XtNlabel, (XtArgVal) "starting...");
1500
1501     XtSetArg(player->timerArgs[0], XtNborderWidth, 0);
1502     XtSetArg(player->timerArgs[1], XtNjustify, (XtArgVal) XtJustifyLeft);
1503
1504     XtSetArg(player->titleArgs[0], XtNborderWidth, 0);
1505     XtSetArg(player->titleArgs[1], XtNjustify, (XtArgVal) XtJustifyLeft);
1506
1507     boardWidth = LINE_GAP
1508         + (BOARD_SIZE + 4) * (player->squareSize + LINE_GAP);
1509
1510     XtSetArg(player->boardArgs[1], XtNwidth, boardWidth);
1511     XtSetArg(player->boardArgs[2], XtNheight,
1512              LINE_GAP + BOARD_SIZE * (player->squareSize + LINE_GAP));
1513
1514     /*
1515      * widget hierarchy
1516      */
1517
1518     player->formWidget = XtCreateManagedWidget("form",
1519                                                formWidgetClass,
1520                                                player->shellWidget, NULL, 0);
1521
1522     player->widgetList[0] = player->blackTimerWidget
1523         = XtCreateWidget((local ? "black time:" : "rblack time:"),
1524                          labelWidgetClass,
1525                          player->formWidget, player->timerArgs,
1526                          XtNumber(player->timerArgs));
1527
1528     XtSetArg(args[0], XtNfont, player->mainFontStruct);
1529     XtSetValues(player->blackTimerWidget, args, 1);
1530
1531     player->widgetList[1] = player->whiteTimerWidget
1532         = XtCreateWidget((local ? "white time:" : "rwhite time:"),
1533                          labelWidgetClass,
1534                          player->formWidget, player->timerArgs,
1535                          XtNumber(player->timerArgs));
1536
1537     XtSetArg(args[0], XtNfont, player->mainFontStruct);
1538     XtSetValues(player->whiteTimerWidget, args, 1);
1539
1540     player->widgetList[2] = player->titleWidget
1541         = XtCreateWidget((local ? "" : "r"), labelWidgetClass,
1542                          player->formWidget, player->titleArgs,
1543                          XtNumber(player->titleArgs));
1544
1545     XtSetArg(args[0], XtNfont, player->mainFontStruct);
1546     XtSetValues(player->titleWidget, args, 1);
1547
1548     player->widgetList[3] = player->messageWidget
1549         = XtCreateWidget((local ? "message" : "rmessage"),
1550                          labelWidgetClass, player->formWidget,
1551                          player->messageArgs,
1552                          XtNumber(player->messageArgs));
1553
1554     XtSetArg(args[0], XtNfont, player->mainFontStruct);
1555     XtSetValues(player->messageWidget, args, 1);
1556
1557     player->widgetList[4] = player->commandsWidget
1558         = XtCreateWidget((local ? "commands" : "rcommand"),
1559                          listWidgetClass, player->formWidget,
1560                          player->commandsArgs,
1561                          XtNumber(player->commandsArgs));
1562
1563     player->widgetList[5] = player->boardWidget
1564         = XtCreateWidget((local ? "board" : "rboard"),
1565                          widgetClass, player->formWidget,
1566                          player->boardArgs,
1567                          XtNumber(player->boardArgs));
1568
1569     XtManageChildren(player->widgetList, XtNumber(player->widgetList));
1570
1571     /*
1572      * Calculate the width of the timer labels.
1573      */
1574
1575     XtSetArg(args[0], XtNfont, &player->mainFontStruct);
1576     XtGetValues(player->blackTimerWidget, args, 1);
1577
1578     if (player->appData.clockMode)
1579     {
1580         timerWidth = XTextWidth(player->mainFontStruct,
1581                                 "Black: 8:88:88 ", 15);
1582     }
1583     else
1584     {
1585         timerWidth = XTextWidth(player->mainFontStruct, "Black  ", 7);
1586     }
1587
1588     XtSetArg(args[0], XtNwidth, timerWidth);
1589     XtSetValues(player->blackTimerWidget, args, 1);
1590     XtSetValues(player->whiteTimerWidget, args, 1);
1591
1592     XtSetArg(args[0], XtNbackground, &player->timerForegroundPixel);
1593     XtSetArg(args[1], XtNforeground, &player->timerBackgroundPixel);
1594     XtGetValues(player->blackTimerWidget, args, 2);
1595
1596     /*
1597      * Calculate the width of the name and message labels.
1598      */
1599
1600     XtSetArg(args[0], XtNwidth, &commandsWidth);
1601     XtGetValues(player->commandsWidget, args, 1);
1602     w = ((commandsWidth > boardWidth) ? commandsWidth : boardWidth);
1603     XtSetArg(args[0], XtNwidth, w - timerWidth * 2 - 12);
1604     XtSetValues(player->titleWidget, args, 1);
1605     XtSetArg(args[0], XtNwidth, w - 8);
1606     XtSetValues(player->messageWidget, args, 1);
1607
1608     /*
1609      * 'formWidget' uses these constraints but they are stored
1610      * in the children.
1611      */
1612
1613     XtSetArg(args[0], XtNfromHoriz, player->blackTimerWidget);
1614     XtSetValues(player->whiteTimerWidget, args, 1);
1615     XtSetArg(args[0], XtNfromHoriz, player->whiteTimerWidget);
1616     XtSetValues(player->titleWidget, args, 1);
1617     XtSetArg(args[0], XtNfromVert, player->blackTimerWidget);
1618     XtSetValues(player->messageWidget, args, 1);
1619     XtSetArg(args[0], XtNfromVert, player->messageWidget);
1620     XtSetValues(player->commandsWidget, args, 1);
1621     XtSetArg(args[0], XtNfromVert, player->commandsWidget);
1622     XtSetValues(player->boardWidget, args, 1);
1623
1624     XtRealizeWidget(player->shellWidget);
1625
1626     player->xBoardWindow = XtWindow(player->boardWidget);
1627
1628     /*
1629      * Create an icon.
1630      */
1631
1632     player->iconPixmap =
1633         XCreateBitmapFromData(player->xDisplay,
1634                               XtWindow(player->shellWidget),
1635                               (char *)icon_bits, icon_width, icon_height);
1636
1637     XtSetArg(args[0], XtNiconPixmap, player->iconPixmap);
1638     XtSetValues(player->shellWidget, args, 1);
1639
1640     /*
1641      * Create a cursor for the board widget.
1642      */
1643
1644     window_attributes.cursor = XCreateFontCursor(player->xDisplay, XC_hand2);
1645     XChangeWindowAttributes(player->xDisplay, player->xBoardWindow,
1646                             CWCursor, &window_attributes);
1647
1648     /*
1649      * Inhibit shell resizing.
1650      */
1651
1652     player->shellArgs[0].value = (XtArgVal) &w;
1653     player->shellArgs[1].value = (XtArgVal) &h;
1654     XtGetValues(player->shellWidget, player->shellArgs, 2);
1655     player->shellArgs[4].value = player->shellArgs[2].value = w;
1656     player->shellArgs[5].value = player->shellArgs[3].value = h;
1657     XtSetValues(player->shellWidget, &player->shellArgs[2], 4);
1658
1659     /*
1660      * Determine value of black pixel.
1661      */
1662
1663     player->black_pixel_is_zero =
1664         (XBlackPixel(player->xDisplay, player->xScreen) == 0);
1665
1666     CreateGCs();
1667     CreateGrid();
1668     CreatePieces();
1669
1670     if (!fromRemotePlayer)
1671         CreatePieceMenus();
1672
1673     XtAddCallback(player->commandsWidget, XtNcallback, SelectCommand,
1674                   (XtPointer)fromRemotePlayer);
1675
1676     if (!fromRemotePlayer)
1677         XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1678
1679     if (fromRemotePlayer)
1680     {
1681         XtSetArg(args[0], XtNtranslations,
1682                  XtParseTranslationTable(translationsTableReduced));
1683         /* Disable key commands because often keys are pressed
1684            in the board window if using another talk window. */
1685         XtSetValues(player->boardWidget, &args[0], 1);
1686         XtSetValues(localPlayer.boardWidget, &args[0], 1);
1687     }
1688     else
1689     {
1690         XtSetArg(args[0], XtNtranslations,
1691                  XtParseTranslationTable(translationsTable));
1692         XtSetValues(player->boardWidget, &args[0], 1);
1693         XtSetArg(args[0], XtNtranslations,
1694                  XtParseTranslationTable(blackTranslations));
1695         XtSetValues(player->blackTimerWidget, &args[0], 1);
1696         XtSetArg(args[0], XtNtranslations,
1697                  XtParseTranslationTable(whiteTranslations));
1698         XtSetValues(player->whiteTimerWidget, &args[0], 1);
1699     }
1700
1701     XtAddEventHandler(player->boardWidget, ExposureMask | ButtonPressMask
1702                       | ButtonReleaseMask | Button1MotionMask | KeyPressMask,
1703                       False, (XtEventHandler)EventProc,
1704                       (XtPointer)(player == &remotePlayer));
1705
1706     sprintf(buf, "xshogi version %s based on "
1707             "xboard version %s",
1708             PACKAGE_VERSION, XBOARD_VERSION);
1709
1710     /*
1711      * If there is to be a machine match, set it up.
1712      */
1713
1714     if (matchMode != MatchFalse && player != &remotePlayer)
1715     {
1716         if (player->appData.noShogiProgram)
1717         {
1718             fprintf(stderr,
1719                     "%s: can't have a match with no shogi programs!\n",
1720                     programName);
1721             exit(1);
1722         }
1723
1724         DisplayMessage(buf, fromRemotePlayer);
1725         TwoMachinesProc(NULL, NULL, NULL, NULL);
1726     }
1727     else
1728     {
1729         Reset(True);
1730         DisplayMessage(buf, fromRemotePlayer);
1731     }
1732 }
1733
1734
1735
1736
1737 int
1738 main(int argc, char **argv)
1739 {
1740     setbuf(stdout, NULL);
1741     setbuf(stderr, NULL);
1742
1743     /*
1744      * Copy pointers to command line arguments and number of such pointers.
1745      * (argc, argv will be destroyed by XtAppInitialize)
1746      */
1747
1748     for (global_argc = 0; global_argc < argc; global_argc++)
1749         global_argv[global_argc] = argv[global_argc];
1750
1751     programName = strrchr(argv[0], '/');
1752
1753     if (programName == NULL)
1754         programName = argv[0];
1755     else
1756         programName++;
1757
1758     localPlayer.shellWidget
1759         = XtAppInitialize(&appContext, "XShogi", shellOptions,
1760                           XtNumber(shellOptions), &argc, argv,
1761                           xshogiResources, NULL, 0);
1762
1763     if (argc > 1)
1764         Usage();
1765
1766     if ((shogiDir = (char *)getenv("SHOGIDIR")) == NULL)
1767     {
1768         shogiDir = ".";
1769     }
1770     else
1771     {
1772         if (chdir(shogiDir) != 0)
1773         {
1774             fprintf(stderr, "%s: can't cd to SHOGIDIR\n",
1775                     programName);
1776             perror(shogiDir);
1777             exit(1);
1778         }
1779     }
1780
1781     XtGetApplicationResources(localPlayer.shellWidget,
1782                               &localPlayer.appData, clientResources,
1783                               XtNumber(clientResources), NULL, 0);
1784
1785     xshogiDebug = localPlayer.appData.debugMode;
1786
1787     /*
1788      * Determine matchMode state -- poor man's resource converter.
1789      */
1790
1791     if (strcasecmp(localPlayer.appData.matchMode, "Init") == 0)
1792     {
1793         matchMode = MatchInit;
1794     }
1795     else if (strcasecmp(localPlayer.appData.matchMode, "Position") == 0)
1796     {
1797         matchMode = MatchPosition;
1798     }
1799     else if (strcasecmp(localPlayer.appData.matchMode, "Opening") == 0)
1800     {
1801         matchMode = MatchOpening;
1802     }
1803     else if (strcasecmp(localPlayer.appData.matchMode, "False") == 0)
1804     {
1805         matchMode = MatchFalse;
1806     }
1807     else
1808     {
1809         fprintf(stderr, "%s: bad matchMode option %s\n",
1810                 programName, localPlayer.appData.matchMode);
1811         Usage();
1812     }
1813
1814     buttonStrings = gnuButtonStrings;
1815     buttonProcs = gnuButtonProcs;
1816     buttonCount = XtNumber(gnuButtonStrings);
1817
1818     player = &localPlayer;
1819
1820     CreatePlayerWindow();
1821
1822     XtAppMainLoop(appContext);
1823
1824     return 0;
1825 }
1826
1827
1828
1829 /*
1830  * Find a font that matches "pattern" that is as close as
1831  * possible to the targetPxlSize.  Prefer fonts that are k
1832  * pixels smaller to fonts that are k pixels larger.  The
1833  * pattern must be in the X Consortium standard format,
1834  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1835  * The return value should be freed with XtFree when no
1836  * longer needed.
1837  */
1838
1839 char *
1840 FindFont(char *pattern, int targetPxlSize)
1841 {
1842     char **fonts, *p, *best;
1843     int i, j, nfonts, minerr, err, pxlSize;
1844
1845     fonts = XListFonts(player->xDisplay, pattern, 999999, &nfonts);
1846
1847     if (nfonts < 1)
1848     {
1849         fprintf(stderr, "%s: No fonts match pattern %s\n",
1850                 programName, pattern);
1851         exit(1);
1852     }
1853
1854     best = "";
1855     minerr = 999999;
1856
1857     for (i = 0; i < nfonts; i++)
1858     {
1859         j = 0;
1860         p = fonts[i];
1861
1862         if (*p != '-')
1863             continue;
1864
1865         while (j < 7)
1866         {
1867             if (*p == NULLCHAR)
1868                 break;
1869
1870             if (*p++ == '-')
1871                 j++;
1872         }
1873
1874         if (j < 7)
1875             continue;
1876
1877         pxlSize = atoi(p);
1878
1879         if (pxlSize == targetPxlSize)
1880         {
1881             best = fonts[i];
1882             break;
1883         }
1884
1885         err = pxlSize - targetPxlSize;
1886
1887         if (abs(err) < abs(minerr)
1888             || ((minerr > 0) && (err < 0) && (-err == minerr)))
1889         {
1890             best = fonts[i];
1891             minerr = err;
1892         }
1893     }
1894
1895     p = (char *)XtMalloc(strlen(best) + 1);
1896     strcpy(p, best);
1897     XFreeFontNames(fonts);
1898     return p;
1899 }
1900
1901
1902
1903
1904 void
1905 CreateGCs(void)
1906 {
1907     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
1908         | GCBackground | GCFunction | GCPlaneMask;
1909     XGCValues gc_values;
1910
1911     gc_values.plane_mask = AllPlanes;
1912     gc_values.line_width = LINE_GAP;
1913     gc_values.line_style = LineSolid;
1914     gc_values.function = GXcopy;
1915
1916     gc_values.foreground = XBlackPixel(player->xDisplay, player->xScreen);
1917     gc_values.background = XBlackPixel(player->xDisplay, player->xScreen);
1918     player->lineGC = XtGetGC(player->shellWidget, value_mask, &gc_values);
1919
1920     gc_values.background = XWhitePixel(player->xDisplay, player->xScreen);
1921     player->coordGC = XtGetGC(player->shellWidget, value_mask, &gc_values);
1922     XSetFont(player->xDisplay, player->coordGC, player->coordFontID);
1923
1924     if (player->monoMode)
1925     {
1926         gc_values.foreground
1927             = XWhitePixel(player->xDisplay, player->xScreen);
1928         gc_values.background
1929             = XWhitePixel(player->xDisplay, player->xScreen);
1930
1931         /* empty square off board */
1932         player->squareOffBoardGC
1933             = XtGetGC(player->shellWidget, value_mask, &gc_values);
1934
1935         /* empty square on board */
1936         player->squareGC
1937             = XtGetGC(player->shellWidget, value_mask, &gc_values);
1938     }
1939     else
1940     {
1941         Pixel bg;         /* background color */
1942         Arg   args[1];
1943
1944         /* Get background color. */
1945         XtSetArg(args[0], XtNbackground, &bg);
1946         XtGetValues(player->shellWidget, args, 1);
1947
1948         /* empty square off board */
1949         gc_values.foreground = gc_values.background = bg;
1950         player->squareOffBoardGC
1951             = XtGetGC(player->shellWidget, value_mask, &gc_values);
1952
1953         /* empty square on board */
1954         gc_values.foreground
1955             = player->appData.squareColor;
1956         gc_values.background
1957             = player->appData.squareColor;
1958         player->squareGC
1959             = XtGetGC(player->shellWidget, value_mask, &gc_values);
1960
1961         /* piece off board */
1962         gc_values.foreground
1963             = player->appData.pieceColor;
1964         gc_values.background = bg;
1965         player->oPieceGC
1966             = XtGetGC(player->shellWidget, value_mask, &gc_values);
1967
1968         /* piece on board */
1969         gc_values.foreground
1970             = player->appData.pieceColor;
1971         gc_values.background
1972             = player->appData.squareColor;
1973         player->pieceGC
1974             = XtGetGC(player->shellWidget, value_mask, &gc_values);
1975
1976         /* piece symbol */
1977         /*
1978          * FIXME: charPieceColor seems to have no effect;
1979          *    the bitmap is *always* black.
1980          */
1981         gc_values.function = (player->black_pixel_is_zero ? GXand : GXor);
1982
1983         gc_values.foreground
1984             = player->appData.charPieceColor;
1985         gc_values.background
1986             = player->appData.charPieceColor;
1987
1988         player->charPieceGC
1989             = XtGetGC(player->shellWidget, value_mask, &gc_values);
1990     }
1991 }
1992
1993
1994
1995
1996 void
1997 CreatePieces(void)
1998 {
1999     XSynchronize(player->xDisplay, True);   /* Work-around for xlib/xt
2000                                                buffering bug */
2001
2002     if (player->appData.westernPieceSet)
2003     {
2004         ReadBitmap(player->appData.reverseBigSolidBitmap,
2005                    &player->reverseBigSolidBitmap,
2006                    NULL,
2007                    bigsolidR_bits, bigsolidR_m_bits, bigsolidR_l_bits);
2008
2009         ReadBitmap(player->appData.reverseSmallSolidBitmap,
2010                    &player->reverseSmallSolidBitmap,
2011                    NULL,
2012                    smallsolidR_bits, smallsolidR_m_bits, smallsolidR_l_bits);
2013
2014         ReadBitmap(player->appData.normalBigSolidBitmap,
2015                    &player->normalBigSolidBitmap,
2016                    NULL,
2017                    bigsolid_bits, bigsolid_m_bits, bigsolid_l_bits);
2018
2019         ReadBitmap(player->appData.normalSmallSolidBitmap,
2020                    &player->normalSmallSolidBitmap,
2021                    NULL,
2022                    smallsolid_bits, smallsolid_m_bits, smallsolid_l_bits);
2023
2024         ReadBitmap(player->appData.reversePawnBitmap,
2025                    &player->reversePawnBitmap,
2026                    &player->reverseSmallSolidBitmap,
2027                    pawnRW_bits, pawnRW_bits, pawnRW_bits);
2028
2029         ReadBitmap(player->appData.reverseLanceBitmap,
2030                    &player->reverseLanceBitmap,
2031                    &player->reverseSmallSolidBitmap,
2032                    lanceRW_bits, lanceRW_bits, lanceRW_bits);
2033
2034         ReadBitmap(player->appData.reverseKnightBitmap,
2035                    &player->reverseKnightBitmap,
2036                    &player->reverseSmallSolidBitmap,
2037                    knightRW_bits, knightRW_bits, knightRW_bits);
2038
2039         ReadBitmap(player->appData.reverseSilverBitmap,
2040                    &player->reverseSilverBitmap,
2041                    &player->reverseBigSolidBitmap,
2042                    silverRW_bits, silverRW_bits, silverRW_bits);
2043
2044         ReadBitmap(player->appData.reverseGoldBitmap,
2045                    &player->reverseGoldBitmap,
2046                    &player->reverseBigSolidBitmap,
2047                    goldRW_bits, goldRW_bits, goldRW_bits);
2048
2049         ReadBitmap(player->appData.reverseRookBitmap,
2050                    &player->reverseRookBitmap,
2051                    &player->reverseBigSolidBitmap,
2052                    rookRW_bits, rookRW_bits, rookRW_bits);
2053
2054         ReadBitmap(player->appData.reverseBishopBitmap,
2055                    &player->reverseBishopBitmap,
2056                    &player->reverseBigSolidBitmap,
2057                    bishopRW_bits, bishopRW_bits, bishopRW_bits);
2058
2059         ReadBitmap(player->appData.reversePPawnBitmap,
2060                    &player->reversePPawnBitmap,
2061                    &player->reverseSmallSolidBitmap,
2062                    pawnPRW_bits, pawnPRW_bits, pawnPRW_bits);
2063
2064         ReadBitmap(player->appData.reversePLanceBitmap,
2065                    &player->reversePLanceBitmap,
2066                    &player->reverseSmallSolidBitmap,
2067                    lancePRW_bits, lancePRW_bits, lancePRW_bits);
2068
2069         ReadBitmap(player->appData.reversePKnightBitmap,
2070                    &player->reversePKnightBitmap,
2071                    &player->reverseSmallSolidBitmap,
2072                    knightPRW_bits, knightPRW_bits, knightPRW_bits);
2073
2074         ReadBitmap(player->appData.reversePSilverBitmap,
2075                    &player->reversePSilverBitmap,
2076                    &player->reverseBigSolidBitmap,
2077                    silverPRW_bits, silverPRW_bits, silverPRW_bits);
2078
2079         ReadBitmap(player->appData.reversePRookBitmap,
2080                    &player->reversePRookBitmap,
2081                    &player->reverseBigSolidBitmap,
2082                    rookPRW_bits, rookPRW_bits, rookPRW_bits);
2083
2084         ReadBitmap(player->appData.reversePBishopBitmap,
2085                    &player->reversePBishopBitmap,
2086                    &player->reverseBigSolidBitmap,
2087                    bishopPRW_bits, bishopPRW_bits, bishopPRW_bits);
2088
2089         ReadBitmap(player->appData.reverseKingBitmap,
2090                    &player->reverseKingBitmap,
2091                    &player->reverseBigSolidBitmap,
2092                    kingRW_bits, kingRW_bits, kingRW_bits);
2093
2094         ReadBitmap(player->appData.normalPawnBitmap,
2095                    &player->normalPawnBitmap,
2096                    &player->normalSmallSolidBitmap,
2097                    pawnW_bits, pawnW_bits, pawnW_bits);
2098
2099         ReadBitmap(player->appData.normalLanceBitmap,
2100                    &player->normalLanceBitmap,
2101                    &player->normalSmallSolidBitmap,
2102                    lanceW_bits, lanceW_bits, lanceW_bits);
2103
2104         ReadBitmap(player->appData.normalKnightBitmap,
2105                    &player->normalKnightBitmap,
2106                    &player->normalSmallSolidBitmap,
2107                    knightW_bits, knightW_bits, knightW_bits);
2108
2109         ReadBitmap(player->appData.normalSilverBitmap,
2110                    &player->normalSilverBitmap,
2111                    &player->normalBigSolidBitmap,
2112                    silverW_bits, silverW_bits, silverW_bits);
2113
2114         ReadBitmap(player->appData.normalGoldBitmap,
2115                    &player->normalGoldBitmap,
2116                    &player->normalBigSolidBitmap,
2117                    goldW_bits, goldW_bits, goldW_bits);
2118
2119         ReadBitmap(player->appData.normalRookBitmap,
2120                    &player->normalRookBitmap,
2121                    &player->normalBigSolidBitmap,
2122                    rookW_bits, rookW_bits, rookW_bits);
2123
2124         ReadBitmap(player->appData.normalBishopBitmap,
2125                    &player->normalBishopBitmap,
2126                    &player->normalBigSolidBitmap,
2127                    bishopW_bits, bishopW_bits, bishopW_bits);
2128
2129         ReadBitmap(player->appData.normalPPawnBitmap,
2130                    &player->normalPPawnBitmap,
2131                    &player->normalSmallSolidBitmap,
2132                    pawnPW_bits, pawnPW_bits, pawnPW_bits);
2133
2134         ReadBitmap(player->appData.normalPLanceBitmap,
2135                    &player->normalPLanceBitmap,
2136                    &player->normalSmallSolidBitmap,
2137                    lancePW_bits, lancePW_bits, lancePW_bits);
2138
2139         ReadBitmap(player->appData.normalPKnightBitmap,
2140                    &player->normalPKnightBitmap,
2141                    &player->normalSmallSolidBitmap,
2142                    knightPW_bits, knightPW_bits, knightPW_bits);
2143
2144         ReadBitmap(player->appData.normalPSilverBitmap,
2145                    &player->normalPSilverBitmap,
2146                    &player->normalBigSolidBitmap,
2147                    silverPW_bits, silverPW_bits, silverPW_bits);
2148
2149         ReadBitmap(player->appData.normalPRookBitmap,
2150                    &player->normalPRookBitmap,
2151                    &player->normalBigSolidBitmap,
2152                    rookPW_bits, rookPW_bits, rookPW_bits);
2153
2154         ReadBitmap(player->appData.normalPBishopBitmap,
2155                    &player->normalPBishopBitmap,
2156                    &player->normalBigSolidBitmap,
2157                    bishopPW_bits, bishopPW_bits, bishopPW_bits);
2158
2159         ReadBitmap(player->appData.normalKingBitmap,
2160                    &player->normalKingBitmap,
2161                    &player->normalBigSolidBitmap,
2162                    kingW_bits, kingW_bits, kingW_bits);
2163     }
2164     else
2165     {
2166         ReadBitmap(player->appData.reverseBigSolidBitmap,
2167                    &player->reverseBigSolidBitmap,
2168                    NULL,
2169                    bigsolidR_bits, bigsolidR_m_bits, bigsolidR_l_bits);
2170
2171         ReadBitmap(player->appData.reverseSmallSolidBitmap,
2172                    &player->reverseSmallSolidBitmap,
2173                    NULL,
2174                    smallsolidR_bits, smallsolidR_m_bits, smallsolidR_l_bits);
2175
2176         ReadBitmap(player->appData.normalBigSolidBitmap,
2177                    &player->normalBigSolidBitmap,
2178                    NULL,
2179                    bigsolid_bits, bigsolid_m_bits, bigsolid_l_bits);
2180
2181         ReadBitmap(player->appData.normalSmallSolidBitmap,
2182                    &player->normalSmallSolidBitmap,
2183                    NULL,
2184                    smallsolid_bits, smallsolid_m_bits, smallsolid_l_bits);
2185
2186         ReadBitmap(player->appData.reversePawnBitmap,
2187                    &player->reversePawnBitmap,
2188                    &player->reverseSmallSolidBitmap,
2189                    pawnR_bits, pawnR_m_bits, pawnR_l_bits);
2190
2191         ReadBitmap(player->appData.reverseLanceBitmap,
2192                    &player->reverseLanceBitmap,
2193                    &player->reverseSmallSolidBitmap,
2194                    lanceR_bits, lanceR_m_bits, lanceR_l_bits);
2195
2196         ReadBitmap(player->appData.reverseKnightBitmap,
2197                    &player->reverseKnightBitmap,
2198                    &player->reverseSmallSolidBitmap,
2199                    knightR_bits, knightR_m_bits, knightR_l_bits);
2200
2201         ReadBitmap(player->appData.reverseSilverBitmap,
2202                    &player->reverseSilverBitmap,
2203                    &player->reverseBigSolidBitmap,
2204                    silverR_bits, silverR_m_bits, silverR_l_bits);
2205
2206         ReadBitmap(player->appData.reverseGoldBitmap,
2207                    &player->reverseGoldBitmap,
2208                    &player->reverseBigSolidBitmap,
2209                    goldR_bits, goldR_m_bits, goldR_l_bits);
2210
2211         ReadBitmap(player->appData.reverseRookBitmap,
2212                    &player->reverseRookBitmap,
2213                    &player->reverseBigSolidBitmap,
2214                    rookR_bits, rookR_m_bits, rookR_l_bits);
2215
2216         ReadBitmap(player->appData.reverseBishopBitmap,
2217                    &player->reverseBishopBitmap,
2218                    &player->reverseBigSolidBitmap,
2219                    bishopR_bits, bishopR_m_bits, bishopR_l_bits);
2220
2221         ReadBitmap(player->appData.reversePPawnBitmap,
2222                    &player->reversePPawnBitmap,
2223                    &player->reverseSmallSolidBitmap,
2224                    pawnPR_bits, pawnPR_m_bits, pawnPR_l_bits);
2225
2226         ReadBitmap(player->appData.reversePLanceBitmap,
2227                    &player->reversePLanceBitmap,
2228                    &player->reverseSmallSolidBitmap,
2229                    lancePR_bits, lancePR_m_bits, lancePR_l_bits);
2230
2231         ReadBitmap(player->appData.reversePKnightBitmap,
2232                    &player->reversePKnightBitmap,
2233                    &player->reverseSmallSolidBitmap,
2234                    knightPR_bits, knightPR_m_bits, knightPR_l_bits);
2235
2236         ReadBitmap(player->appData.reversePSilverBitmap,
2237                    &player->reversePSilverBitmap,
2238                    &player->reverseBigSolidBitmap,
2239                    silverPR_bits, silverPR_m_bits, silverPR_l_bits);
2240
2241         ReadBitmap(player->appData.reversePRookBitmap,
2242                    &player->reversePRookBitmap,
2243                    &player->reverseBigSolidBitmap,
2244                    rookPR_bits, rookPR_m_bits, rookPR_l_bits);
2245
2246         ReadBitmap(player->appData.reversePBishopBitmap,
2247                    &player->reversePBishopBitmap,
2248                    &player->reverseBigSolidBitmap,
2249                    bishopPR_bits, bishopPR_m_bits, bishopPR_l_bits);
2250
2251         ReadBitmap(player->appData.reverseKingBitmap,
2252                    &player->reverseKingBitmap,
2253                    &player->reverseBigSolidBitmap,
2254                    kingR_bits, kingR_m_bits, kingR_l_bits);
2255
2256         ReadBitmap(player->appData.normalPawnBitmap,
2257                    &player->normalPawnBitmap,
2258                    &player->normalSmallSolidBitmap,
2259                    pawn_bits, pawn_m_bits, pawn_l_bits);
2260
2261         ReadBitmap(player->appData.normalLanceBitmap,
2262                    &player->normalLanceBitmap,
2263                    &player->normalSmallSolidBitmap,
2264                    lance_bits, lance_m_bits, lance_l_bits);
2265
2266         ReadBitmap(player->appData.normalKnightBitmap,
2267                    &player->normalKnightBitmap,
2268                    &player->normalSmallSolidBitmap,
2269                    knight_bits, knight_m_bits, knight_l_bits);
2270
2271         ReadBitmap(player->appData.normalSilverBitmap,
2272                    &player->normalSilverBitmap,
2273                    &player->normalBigSolidBitmap,
2274                    silver_bits, silver_m_bits, silver_l_bits);
2275
2276         ReadBitmap(player->appData.normalGoldBitmap,
2277                    &player->normalGoldBitmap,
2278                    &player->normalBigSolidBitmap,
2279                    gold_bits, gold_m_bits, gold_l_bits);
2280
2281         ReadBitmap(player->appData.normalRookBitmap,
2282                    &player->normalRookBitmap,
2283                    &player->normalBigSolidBitmap,
2284                    rook_bits, rook_m_bits, rook_l_bits);
2285
2286         ReadBitmap(player->appData.normalBishopBitmap,
2287                    &player->normalBishopBitmap,
2288                    &player->normalBigSolidBitmap,
2289                    bishop_bits, bishop_m_bits, bishop_l_bits);
2290
2291         ReadBitmap(player->appData.normalPPawnBitmap,
2292                    &player->normalPPawnBitmap,
2293                    &player->normalSmallSolidBitmap,
2294                    pawnP_bits, pawnP_m_bits, pawnP_l_bits);
2295
2296         ReadBitmap(player->appData.normalPLanceBitmap,
2297                    &player->normalPLanceBitmap,
2298                    &player->normalSmallSolidBitmap,
2299                    lanceP_bits, lanceP_m_bits, lanceP_l_bits);
2300
2301         ReadBitmap(player->appData.normalPKnightBitmap,
2302                    &player->normalPKnightBitmap,
2303                    &player->normalSmallSolidBitmap,
2304                    knightP_bits, knightP_m_bits, knightP_l_bits);
2305
2306         ReadBitmap(player->appData.normalPSilverBitmap,
2307                    &player->normalPSilverBitmap,
2308                    &player->normalBigSolidBitmap,
2309                    silverP_bits, silverP_m_bits, silverP_l_bits);
2310
2311         ReadBitmap(player->appData.normalPRookBitmap,
2312                    &player->normalPRookBitmap,
2313                    &player->normalBigSolidBitmap,
2314                    rookP_bits, rookP_m_bits, rookP_l_bits);
2315
2316         ReadBitmap(player->appData.normalPBishopBitmap,
2317                    &player->normalPBishopBitmap,
2318                    &player->normalBigSolidBitmap,
2319                    bishopP_bits, bishopP_m_bits, bishopP_l_bits);
2320
2321         ReadBitmap(player->appData.normalKingBitmap,
2322                    &player->normalKingBitmap,
2323                    &player->normalBigSolidBitmap,
2324                    king_bits, king_m_bits, king_l_bits);
2325
2326     }
2327
2328     XSynchronize(player->xDisplay, False);  /* Work-around for xlib/xt
2329                                                buffering bug */
2330 }
2331
2332
2333
2334
2335 int
2336 ReadBitmapFile(Display *display, Drawable d, char *filename,
2337                unsigned int *width_return,
2338                unsigned int *height_return,
2339                Pixmap *bitmap_return,
2340                int *x_hot_return, int *y_hot_return)
2341 {
2342     int n;
2343
2344     if ((n = XReadBitmapFile(display, d, filename,
2345                              width_return, height_return,
2346                              bitmap_return, x_hot_return, y_hot_return))
2347         != BitmapSuccess)
2348     {
2349         return n;
2350     }
2351     else
2352     {
2353         /* transform a 1 plane pixmap to a k plane pixmap */
2354         return BitmapSuccess;
2355     }
2356 }
2357
2358
2359
2360
2361 /*
2362  * Create the X pixmap from .xbm file bitmap data.  This may
2363  * have to be revised considerably.
2364  */
2365
2366 void
2367 ReadBitmap(String name, Pixmap *pm, Pixmap *qm,
2368            unsigned char *small_bits, 
2369                    unsigned char *medium_bits, 
2370                    unsigned char *large_bits)
2371 {
2372     int x_hot, y_hot;
2373     unsigned int w, h;
2374
2375     if ((name == NULL)
2376         || (ReadBitmapFile(player->xDisplay, player->xBoardWindow, name,
2377                           &w, &h, pm, &x_hot, &y_hot) != BitmapSuccess)
2378         || (w != player->squareSize)
2379         || (h != player->squareSize))
2380     {
2381         unsigned long fg, bg;
2382         unsigned int depth;
2383
2384         depth = DisplayPlanes(player->xDisplay, player->xScreen);
2385
2386         if (player->monoMode)
2387         {
2388             fg = XBlackPixel(player->xDisplay, player->xScreen);
2389             bg = XWhitePixel(player->xDisplay, player->xScreen);
2390         }
2391         else if (qm == NULL)
2392         {
2393             fg = player->appData.oneColor;
2394             bg = player->appData.zeroColor;
2395         }
2396         else
2397         {
2398             fg = (player->black_pixel_is_zero ? 0 : ~0);
2399             bg = (player->black_pixel_is_zero ? ~0 : 0);
2400         }
2401
2402         switch (player->boardSize)
2403         {
2404         case Large:
2405             *pm = XCreatePixmapFromBitmapData(player->xDisplay,
2406                                               player->xBoardWindow,
2407                                               (char *)large_bits,
2408                                               player->squareSize,
2409                                               player->squareSize,
2410                                               fg, bg, depth);
2411             break;
2412
2413         case Medium:
2414             *pm = XCreatePixmapFromBitmapData(player->xDisplay,
2415                                               player->xBoardWindow,
2416                                               (char *)medium_bits,
2417                                               player->squareSize,
2418                                               player->squareSize,
2419                                               fg, bg, depth);
2420             break;
2421
2422         case Small:
2423             *pm = XCreatePixmapFromBitmapData(player->xDisplay,
2424                                               player->xBoardWindow,
2425                                               (char *)small_bits,
2426                                               player->squareSize,
2427                                               player->squareSize,
2428                                               fg, bg, depth);
2429             break;
2430         }
2431     }
2432 }
2433
2434
2435
2436
2437 void
2438 CreateGrid(void)
2439 {
2440     int i, offset;
2441
2442     offset = 2 * (player->squareSize + LINE_GAP);
2443
2444     for (i = 0; i < BOARD_SIZE + 1; i++)
2445     {
2446         player->gridSegments[i].x1 = offset;
2447         player->gridSegments[i + BOARD_SIZE + 1].y1 = 0;
2448         player->gridSegments[i].y1 = player->gridSegments[i].y2
2449             = LINE_GAP / 2 + (i * (player->squareSize + LINE_GAP));
2450         player->gridSegments[i].x2 = LINE_GAP + BOARD_SIZE *
2451             (player->squareSize + LINE_GAP) + offset;
2452         player->gridSegments[i + BOARD_SIZE + 1].x1
2453             = player->gridSegments[i + BOARD_SIZE + 1].x2 = LINE_GAP / 2
2454             + (i * (player->squareSize + LINE_GAP)) + offset;
2455         player->gridSegments[i + BOARD_SIZE + 1].y2
2456             = BOARD_SIZE * (player->squareSize + LINE_GAP);
2457     }
2458 }
2459
2460
2461
2462
2463 void
2464 CreatePieceMenus(void)
2465 {
2466     int i;
2467     Widget entry;
2468     Arg args[1];
2469     ShogiSquare selection;
2470
2471     XtSetArg(args[0], XtNlabel, "Black");
2472     blackPieceMenu = XtCreatePopupShell("menuW", simpleMenuWidgetClass,
2473                                         localPlayer.boardWidget, args, 1);
2474
2475     for (i = 0; i < PIECE_MENU_SIZE; i++)
2476     {
2477         String item = pieceMenuStrings[i];
2478
2479         if (strcmp(item, "----") == 0)
2480         {
2481             entry = XtCreateManagedWidget(item, smeLineObjectClass,
2482                                           blackPieceMenu, NULL, 0);
2483         }
2484         else
2485         {
2486             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2487                                           blackPieceMenu, NULL, 0);
2488             selection = pieceMenuTranslation[0][i];
2489             XtAddCallback(entry, XtNcallback,
2490                           (XtCallbackProc)PieceMenuSelect,
2491                           (XtPointer)selection);
2492
2493             if (selection == BlackPawn)
2494             {
2495                 XtSetArg(args[0], XtNpopupOnEntry, entry);
2496                 XtSetValues(blackPieceMenu, args, 1);
2497             }
2498         }
2499     }
2500
2501     XtSetArg(args[0], XtNlabel, "White");
2502     whitePieceMenu = XtCreatePopupShell("menuB", simpleMenuWidgetClass,
2503                                         localPlayer.boardWidget, args, 1);
2504
2505     for (i = 0; i < PIECE_MENU_SIZE; i++)
2506     {
2507         String item = pieceMenuStrings[i];
2508
2509         if (strcmp(item, "----") == 0)
2510         {
2511             entry = XtCreateManagedWidget(item, smeLineObjectClass,
2512                                           whitePieceMenu, NULL, 0);
2513         }
2514         else
2515         {
2516             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2517                                           whitePieceMenu, NULL, 0);
2518             selection = pieceMenuTranslation[1][i];
2519             XtAddCallback(entry, XtNcallback,
2520                           (XtCallbackProc)PieceMenuSelect,
2521                           (XtPointer)selection);
2522
2523             if (selection == WhitePawn)
2524             {
2525                 XtSetArg(args[0], XtNpopupOnEntry, entry);
2526                 XtSetValues(whitePieceMenu, args, 1);
2527             }
2528         }
2529     }
2530
2531     XtRegisterGrabAction(PieceMenuPopup, True,
2532                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
2533                          GrabModeAsync, GrabModeAsync);
2534 }
2535
2536
2537
2538
2539 void
2540 PieceMenuPopup(Widget w, XEvent *event, String *params, Cardinal *num_params)
2541 {
2542     if (event->type != ButtonPress)
2543         return;
2544
2545     if (gameMode != EditPosition)
2546         return;
2547
2548     if (((pmFromX = EventToXSquare(event->xbutton.x)) < 1)
2549         || (pmFromX > BOARD_SIZE + 2)
2550         || ((pmFromY = EventToSquare(event->xbutton.y)) < 0))
2551     {
2552         pmFromX = pmFromY = -1;
2553         return;
2554     }
2555
2556     if (localPlayer.flipView)
2557         pmFromX = BOARD_SIZE + 3 - pmFromX;
2558     else
2559         pmFromY = BOARD_SIZE - 1 - pmFromY;
2560
2561     XtPopupSpringLoaded(XtNameToWidget(localPlayer.boardWidget, params[0]));
2562 }
2563
2564
2565
2566
2567 static void
2568 PieceMenuSelect(Widget w, ShogiSquare piece, char *junk)
2569 {
2570     if ((pmFromX < 0) || (pmFromY < 0))
2571         return;
2572
2573     if (off_board(pmFromX))
2574     {
2575         int i, c;
2576         switch (piece)
2577         {
2578         case ClearBoard:
2579             break;
2580
2581         case BlackPlay:
2582             break;
2583
2584         case WhitePlay:
2585             break;
2586
2587         default:
2588             i = pieceToCatchedIndex[piece];
2589             c = (piece >= WhitePawn);
2590             catches[0][c][i]++;
2591             DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
2592             XSync(localPlayer.xDisplay, False);
2593             return;
2594         }
2595     }
2596
2597     pmFromX -= 2;
2598
2599     switch (piece)
2600     {
2601     case ClearBoard:
2602         for (pmFromY = 0; pmFromY < BOARD_SIZE; pmFromY++)
2603             for (pmFromX = 0; pmFromX < BOARD_SIZE; pmFromX++)
2604                 boards[0][pmFromY][pmFromX] = EmptySquare;
2605
2606         ClearCatches(catches[0]);
2607         DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
2608         break;
2609
2610     case BlackPlay:  /* not currently on menu */
2611         SetBlackToPlay();
2612         break;
2613
2614     case WhitePlay:  /* not currently on menu */
2615         SetWhiteToPlay();
2616         break;
2617
2618     default:
2619         boards[0][pmFromY][pmFromX] = piece;
2620         DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
2621         break;
2622     }
2623
2624     XSync(localPlayer.xDisplay, False);
2625 }
2626
2627
2628
2629
2630 static void
2631 SetBlackToPlay(void)
2632 {
2633     int saveCM;
2634
2635     if (gameMode != EditPosition)
2636         return;
2637
2638     whitePlaysFirst = False;
2639     saveCM = currentMove;
2640     currentMove = 0;  /* kludge */
2641     DisplayClocks(ReDisplayTimers);
2642     currentMove = saveCM;
2643 }
2644
2645
2646
2647
2648 static void
2649 SetWhiteToPlay(void)
2650 {
2651     int saveCM;
2652
2653     if (gameMode != EditPosition)
2654         return;
2655
2656     whitePlaysFirst = True;
2657     saveCM = currentMove;
2658     currentMove = 1;  /* kludge */
2659     DisplayClocks(ReDisplayTimers);
2660     currentMove = saveCM;
2661 }
2662
2663
2664
2665
2666 /*
2667  * If the user selects on a border boundary or off the board, return failure.
2668  * Otherwise map the event coordinate to the square.
2669  */
2670
2671 int
2672 EventToSquare(int x)
2673 {
2674     if (x < LINE_GAP)
2675         return -1;
2676
2677     x -= LINE_GAP;
2678
2679     if ((x % (player->squareSize + LINE_GAP)) >= player->squareSize)
2680         return -1;
2681
2682     x /= (player->squareSize + LINE_GAP);
2683
2684     if (x >= BOARD_SIZE)
2685         return -1;
2686
2687     return x;
2688 }
2689
2690
2691
2692
2693 int
2694 EventToXSquare(int x)
2695 {
2696     if (x < LINE_GAP)
2697         return -1;
2698
2699     x -= LINE_GAP;
2700
2701     if ((x % (player->squareSize + LINE_GAP)) >= player->squareSize)
2702         return -1;
2703
2704     x /= (player->squareSize + LINE_GAP);
2705
2706     if (x >= BOARD_SIZE + 4)
2707         return -1;
2708
2709     return x;
2710 }
2711
2712
2713
2714
2715 ShogiSquare
2716 CharToPiece(int c, int p)
2717 {
2718     if (p)
2719     {
2720         switch (c)
2721         {
2722         default:
2723         case '.':   return EmptySquare;
2724         case 'P':   return BlackPPawn;
2725         case 'L':   return BlackPLance;
2726         case 'N':   return BlackPKnight;
2727         case 'S':   return BlackPSilver;
2728         case 'G':   return BlackGold;
2729         case 'R':   return BlackPRook;
2730         case 'B':   return BlackPBishop;
2731         case 'K':   return BlackKing;
2732         case 'p':   return WhitePPawn;
2733         case 'l':   return WhitePLance;
2734         case 'n':   return WhitePKnight;
2735         case 's':   return WhitePSilver;
2736         case 'g':   return WhiteGold;
2737         case 'r':   return WhitePRook;
2738         case 'b':   return WhitePBishop;
2739         case 'k':   return WhiteKing;
2740         }
2741     }
2742     else
2743     {
2744         switch (c)
2745         {
2746         default:
2747         case '.':   return EmptySquare;
2748         case 'P':   return BlackPawn;
2749         case 'L':   return BlackLance;
2750         case 'N':   return BlackKnight;
2751         case 'S':   return BlackSilver;
2752         case 'G':   return BlackGold;
2753         case 'R':   return BlackRook;
2754         case 'B':   return BlackBishop;
2755         case 'K':   return BlackKing;
2756         case 'p':   return WhitePawn;
2757         case 'l':   return WhiteLance;
2758         case 'n':   return WhiteKnight;
2759         case 's':   return WhiteSilver;
2760         case 'g':   return WhiteGold;
2761         case 'r':   return WhiteRook;
2762         case 'b':   return WhiteBishop;
2763         case 'k':   return WhiteKing;
2764         }
2765     }
2766 }
2767
2768
2769
2770
2771 /*
2772  * Convert coordinates to normal algebraic notation.
2773  * promoPiece must be NULLCHAR if not a promotion.
2774  */
2775
2776 ShogiMove
2777 MakeAlg(int fromX, int fromY, int toX, int toY,
2778         char promoPiece, int currentBoardIndex, char *out)
2779 {
2780     ShogiSquare piece;
2781     char *outp = out;
2782
2783     if (fromX > 80)
2784     {
2785         piece = (fromX - 81);
2786         *outp++ = catchedIndexToChar[piece];
2787         *outp++ = '*';
2788         *outp++ = '9' - toX;
2789         *outp++ = 'i' - toY;
2790         *outp++ = NULLCHAR;
2791         return (BlackOnMove(forwardMostMove) ? BlackDrop : WhiteDrop);
2792     }
2793     else
2794     {
2795         *outp++ = '9' - fromX;
2796         *outp++ = 'i' - fromY;
2797         *outp++ = '9' - toX;
2798         *outp++ = 'i' - toY;
2799         *outp++ = promoPiece;
2800         *outp++ = NULLCHAR;
2801
2802         if (promoPiece == NULLCHAR)
2803         {
2804             return NormalMove;
2805         }
2806         else
2807         {
2808             return (BlackOnMove(forwardMostMove)
2809                     ? BlackPromotion : WhitePromotion);
2810         }
2811     }
2812 }
2813
2814
2815
2816
2817 void
2818 DrawSquare(int row, int column, ShogiSquare piece)
2819 {
2820     int square_color, x, y, direction, font_ascent, font_descent;
2821     char string[2];
2822     XCharStruct overall;
2823     struct DisplayData *player;
2824
2825     for (player = &localPlayer; True; player = &remotePlayer)
2826     {
2827         int offset, remote;
2828
2829         remote = (player == &remotePlayer);
2830         offset = 2 * (player->squareSize + LINE_GAP);
2831
2832         if (player->flipView)
2833         {
2834             x = LINE_GAP + ((BOARD_SIZE - 1) - column) *
2835                 (player->squareSize + LINE_GAP) + offset;
2836             y = LINE_GAP + row * (player->squareSize + LINE_GAP);
2837         }
2838         else
2839         {
2840             x = LINE_GAP + column * (player->squareSize + LINE_GAP) + offset;
2841             y = LINE_GAP + ((BOARD_SIZE - 1) - row) *
2842                 (player->squareSize + LINE_GAP);
2843         }
2844
2845         square_color = (((column + row) % 2) ? LIGHT : DARK);
2846
2847         if (piece == EmptySquare)
2848         {
2849             if (column < 0 || column >= BOARD_SIZE)
2850             {
2851                 /* empty square off board */
2852                 XFillRectangle(player->xDisplay, player->xBoardWindow,
2853                                player->squareOffBoardGC,
2854                                x, y, player->squareSize,
2855                                player->squareSize);
2856             }
2857             else
2858             {
2859                 /* empty square on board */
2860                 XFillRectangle(player->xDisplay,
2861                                player->xBoardWindow,
2862                                player->squareGC,
2863                                x, y,
2864                                player->squareSize,
2865                                player->squareSize);
2866             }
2867         }
2868         else if (player->monoMode) /* Draw a piece in mono mode. */
2869         {
2870             XCopyArea(player->xDisplay,
2871                       ((((((int)piece) < ((int)WhitePawn)))
2872                         ^ player->flipView)
2873                        ? *pieceToNormal[remote][(int)piece]
2874                        : *pieceToReverse[remote][(int)piece]),
2875                       player->xBoardWindow,
2876                       (player->monoMode
2877                        ? player->squareOffBoardGC  /* ??? FIXME? */
2878                        : player->pieceGC),
2879                       0, 0,
2880                       player->squareSize, player->squareSize, x, y);
2881         }
2882         else /* Draw a piece in color mode. */
2883         {
2884             if ((column < 0) || (column >= BOARD_SIZE))  /* off board */
2885             {
2886                 /* draw piece background */
2887
2888                 XCopyPlane(player->xDisplay,
2889                            ((((((int)piece) < ((int)WhitePawn)))
2890                              ^ player->flipView)
2891                             ? *pieceToNormalSolid[remote][(int)piece]
2892                             : *pieceToReverseSolid[remote][(int)piece]),
2893                            player->xBoardWindow,
2894                            player->oPieceGC,
2895                            0, 0,
2896                            player->squareSize, player->squareSize, x, y, 1);
2897
2898                 /* draw piece bitmap */
2899
2900                 XCopyArea(player->xDisplay,
2901                           ((((((int)piece) < ((int)WhitePawn)))
2902                             ^ player->flipView)
2903                            ? *pieceToNormal[remote][(int)piece]
2904                            : *pieceToReverse[remote][(int)piece]),
2905                           player->xBoardWindow,
2906                           player->charPieceGC,
2907                           0, 0,
2908                           player->squareSize, player->squareSize, x, y);
2909             }
2910             else  /* on board */
2911             {
2912                 /* draw piece background */
2913
2914                 XCopyPlane(player->xDisplay,
2915                            ((((((int)piece) < ((int)WhitePawn)))
2916                              ^ player->flipView)
2917                             ? *pieceToNormalSolid[remote][(int)piece]
2918                             : *pieceToReverseSolid[remote][(int)piece]),
2919                            player->xBoardWindow,
2920                            player->pieceGC,
2921                            0, 0,
2922                            player->squareSize, player->squareSize, x, y, 1);
2923
2924                 /* draw piece bitmap */
2925
2926                 XCopyArea(player->xDisplay,
2927                           ((((((int)piece) < ((int)WhitePawn)))
2928                             ^ player->flipView)
2929                            ? *pieceToNormal[remote][(int)piece]
2930                            : *pieceToReverse[remote][(int)piece]),
2931                           player->xBoardWindow,
2932                           player->charPieceGC,
2933                           0, 0,
2934                           player->squareSize, player->squareSize, x, y);
2935              }
2936         }
2937
2938         string[1] = NULLCHAR;
2939
2940         if (player->showCoords
2941             && (column >= 0) && (column < 9)
2942             && (row == (player->flipView ? 8 : 0)))
2943         {
2944             string[0] = '9' - column;
2945             XTextExtents(player->coordFontStruct, string, 1, &direction,
2946                          &font_ascent, &font_descent, &overall);
2947
2948             if (player->monoMode)
2949             {
2950                 XDrawImageString(player->xDisplay,
2951                                  player->xBoardWindow, player->coordGC,
2952                                  x + player->squareSize - overall.width - 2,
2953                                  y + player->squareSize - font_descent - 1,
2954                                  string, 1);
2955             }
2956             else
2957             {
2958                 XDrawString(player->xDisplay, player->xBoardWindow,
2959                             player->coordGC,
2960                             x + player->squareSize - overall.width - 2,
2961                             y + player->squareSize - font_descent - 1,
2962                             string, 1);
2963             }
2964         }
2965
2966         if (player->showCoords
2967             && (row >= 0) && (row < 9)
2968             && (column == (player->flipView ? 8 : 0)))
2969         {
2970             string[0] = 'i' - row;
2971             XTextExtents(player->coordFontStruct, string, 1, &direction,
2972                          &font_ascent, &font_descent, &overall);
2973
2974             if (player->monoMode)
2975             {
2976                 XDrawImageString(player->xDisplay,
2977                                  player->xBoardWindow, player->coordGC,
2978                                  x + 2, y + font_ascent + 1, string, 1);
2979             }
2980             else
2981             {
2982                 XDrawString(player->xDisplay, player->xBoardWindow,
2983                             player->coordGC,
2984                             x + 2, y + font_ascent + 1, string, 1);
2985             }
2986         }
2987
2988         if (!updateRemotePlayer || (player == &remotePlayer))
2989             break;
2990     }
2991 }
2992
2993
2994
2995
2996 void
2997 EventProc(Widget widget, XtPointer client_data, XEvent *event)
2998 {
2999     if (event->type == MappingNotify)
3000     {
3001         XRefreshKeyboardMapping((XMappingEvent *) event);
3002         return;
3003     }
3004
3005     if (!XtIsRealized(widget))
3006         return;
3007
3008     if ((event->type == ButtonPress) || (event->type == ButtonRelease))
3009     {
3010         if (event->xbutton.button != Button1)
3011             return;
3012     }
3013
3014     switch (event->type)
3015     {
3016     case Expose:
3017         DrawPosition(widget, event, NULL, NULL);
3018         break;
3019
3020     default:
3021         return;
3022     }
3023 }
3024
3025
3026
3027
3028 /*
3029  * event handler for redrawing the board
3030  */
3031
3032 void
3033 DrawPosition(Widget w, XEvent *event, String *prms, Cardinal *nprms)
3034 {
3035     Arg args[1];
3036     int i, j;
3037     static Board lastBoard;
3038     static Catched lastCatches;
3039     static int lastBoardValid = 0;
3040     static int lastFlipView = 0, lastRemoteFlipView = 1;
3041
3042     if (!player->Iconic)
3043     {
3044         XtSetArg(args[0], XtNiconic, False);
3045         XtSetValues(localPlayer.shellWidget, args, 1);
3046     }
3047
3048     /*
3049      * It would be simpler to clear the window with XClearWindow()
3050      * but this causes a very distracting flicker.
3051      */
3052
3053     if ((w == localPlayer.boardWidget)
3054         && (event == NULL)
3055         && lastBoardValid
3056         && (lastFlipView == localPlayer.flipView)
3057         && (!updateRemotePlayer
3058             || (lastRemoteFlipView == remotePlayer.flipView)))
3059     {
3060         for (i = 0; i < BOARD_SIZE; i++)
3061         {
3062             for (j = 0; j < BOARD_SIZE; j++)
3063             {
3064                 if (boards[currentMove][i][j] != lastBoard[i][j])
3065                     DrawSquare(i, j, boards[currentMove][i][j]);
3066             }
3067         }
3068
3069         for (i = 0; i < 2; i++)
3070         {
3071             for (j = 0; j < 8; j++)
3072             {
3073                 if (catches[currentMove][i][j] != lastCatches[i][j])
3074                 {
3075                     UpdateCatched(i, 0, False, True, currentMove);
3076                     break;
3077                 }
3078             }
3079         }
3080     }
3081     else
3082     {
3083         XDrawSegments(localPlayer.xDisplay,
3084                       localPlayer.xBoardWindow, localPlayer.lineGC,
3085                       localPlayer.gridSegments, (BOARD_SIZE + 1) * 2);
3086
3087         if (updateRemotePlayer)
3088         {
3089             XDrawSegments(remotePlayer.xDisplay,
3090                           remotePlayer.xBoardWindow, remotePlayer.lineGC,
3091                           remotePlayer.gridSegments, (BOARD_SIZE + 1) * 2);
3092         }
3093
3094         for (i = 0; i < BOARD_SIZE; i++)
3095             for (j = 0; j < BOARD_SIZE; j++)
3096                 DrawSquare(i, j, boards[currentMove][i][j]);
3097
3098         UpdateCatched(0, 0, False, True, currentMove);
3099         UpdateCatched(1, 0, False, True, currentMove);
3100     }
3101
3102     CopyBoard(lastBoard, boards[currentMove]);
3103     CopyCatches(lastCatches, catches[currentMove]);
3104     lastBoardValid = 1;
3105     lastFlipView = localPlayer.flipView;
3106
3107     if (updateRemotePlayer)
3108         lastRemoteFlipView = remotePlayer.flipView;
3109
3110     XSync(localPlayer.xDisplay, False);
3111
3112     if (updateRemotePlayer)
3113         XSync(remotePlayer.xDisplay, False);
3114 }
3115
3116
3117
3118
3119 void
3120 InitPosition(int redraw)
3121 {
3122     currentMove = forwardMostMove = backwardMostMove = 0;
3123     CopyBoard(boards[0], initialPosition);
3124     ClearCatches(catches[0]);
3125
3126     if (redraw)
3127         DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
3128 }
3129
3130
3131
3132
3133 void
3134 CopyBoard(Board to, Board from)
3135 {
3136     int i, j;
3137
3138     for (i = 0; i < BOARD_SIZE; i++)
3139         for (j = 0; j < BOARD_SIZE; j++)
3140             to[i][j] = from[i][j];
3141 }
3142
3143
3144
3145
3146 void
3147 CopyCatches(Catched to, Catched from)
3148 {
3149     int i, j;
3150
3151     for (i = 0; i < 2; i++)
3152         for (j = 0; j < 8; j++)
3153             to[i][j] = from[i][j];
3154 }
3155
3156
3157
3158
3159 void
3160 SendCurrentBoard(FILE *fp)
3161 {
3162     SendBoard(fp, boards[currentMove], catches[currentMove]);
3163 }
3164
3165
3166
3167
3168 void
3169 SendBoard(FILE *fp, Board board, Catched catches)
3170 {
3171     char message[MSG_SIZ];
3172     ShogiSquare *bp;
3173     int i, j;
3174
3175     SendToProgram("edit\n", fp);
3176     SendToProgram("#\n", fp);
3177
3178     for (i = BOARD_SIZE - 1; i >= 0; i--)
3179     {
3180         bp = &board[i][0];
3181
3182         for (j = 0; j < BOARD_SIZE; j++, bp++)
3183         {
3184             if (((int) *bp) < (int)WhitePawn)
3185             {
3186                 sprintf(message, "%c%c%c%s\n",
3187                         pieceToChar[(int) *bp],
3188                         '9' - j, 'i' - i,
3189                         (pieceIsPromoted[(int) *bp] ? "+" : ""));
3190                 SendToProgram(message, fp);
3191             }
3192         }
3193     }
3194
3195     for (i = 0; i <= 7; i++)
3196     {
3197         int n;
3198
3199         for (n = catches[0][i]; n > 0; n--)
3200         {
3201             sprintf(message, "%c*\n",
3202                     catchedIndexToChar[i]);
3203             SendToProgram(message, fp);
3204         }
3205     }
3206
3207     SendToProgram("c\n", fp);
3208
3209     for (i = BOARD_SIZE - 1; i >= 0; i--)
3210     {
3211         bp = &board[i][0];
3212
3213         for (j = 0; j < BOARD_SIZE; j++, bp++)
3214         {
3215             if ((((int) *bp) != ((int)EmptySquare))
3216                 && (((int) *bp) >= ((int)WhitePawn)))
3217             {
3218                 sprintf(message, "%c%c%c%s\n",
3219                         pieceToChar[((int) *bp) - ((int)WhitePawn)],
3220                         '9' - j, 'i' - i,
3221                         (pieceIsPromoted[(int) *bp] ? "+" : ""));
3222                 SendToProgram(message, fp);
3223             }
3224         }
3225     }
3226
3227     for (i = 0; i <= 7; i++)
3228     {
3229         int n;
3230
3231         for (n = catches[1][i]; n > 0; n--)
3232         {
3233             sprintf(message, "%c*\n",
3234                     catchedIndexToChar[i]);
3235             SendToProgram(message, fp);
3236         }
3237     }
3238
3239     SendToProgram(".\n", fp);
3240 }
3241
3242
3243
3244
3245 static int
3246 PromotionPossible(int fromY, int toY, ShogiSquare piece)
3247 {
3248     if (((int)piece) < ((int)WhitePawn))
3249     {
3250         if ((fromY < 6) && (toY < 6))
3251             return False;
3252     }
3253     else
3254     {
3255         if ((fromY > 2) && (toY > 2))
3256             return False;
3257     }
3258
3259     return piecePromotable[(int)piece];
3260
3261 }
3262
3263
3264
3265
3266 static void
3267 ShowCount(int row, int column, int n)
3268 {
3269     int offset = 2 * (player->squareSize + LINE_GAP);
3270     int x, y, direction, font_ascent, font_descent;
3271     char string[2];
3272     XCharStruct overall;
3273     struct DisplayData *player;
3274
3275     DrawSquare(row, column, EmptySquare);
3276
3277     if (n <= 1)
3278         return;
3279
3280     for (player = &localPlayer; True; player = &remotePlayer)
3281     {
3282         if (player->flipView)
3283         {
3284             x = LINE_GAP + ((BOARD_SIZE - 1) - column) *
3285                 (player->squareSize + LINE_GAP) + offset;
3286             y = LINE_GAP + row * (player->squareSize + LINE_GAP);
3287         }
3288         else
3289         {
3290             x = LINE_GAP + column * (player->squareSize + LINE_GAP) + offset;
3291             y = LINE_GAP + ((BOARD_SIZE - 1) - row) *
3292                 (player->squareSize + LINE_GAP);
3293         }
3294
3295         x -= player->squareSize / 2;
3296
3297         string[1] = NULLCHAR;
3298
3299         if (n > 9)
3300             string[0] = '*';
3301         else
3302             string[0] = '0' + n;
3303
3304         XTextExtents(player->coordFontStruct, string, 1, &direction,
3305                      &font_ascent, &font_descent, &overall);
3306
3307         if (player->monoMode)
3308         {
3309             XDrawImageString(player->xDisplay, player->xBoardWindow,
3310                              player->coordGC,
3311                              x + player->squareSize - overall.width - 2,
3312                              y + player->squareSize - font_descent - 1,
3313                              string, 1);
3314         }
3315         else
3316         {
3317             XDrawString(player->xDisplay, player->xBoardWindow,
3318                         player->coordGC,
3319                         x + player->squareSize - overall.width - 2,
3320                         y + player->squareSize - font_descent - 1,
3321                         string, 1);
3322         }
3323
3324         if (!updateRemotePlayer || (player == &remotePlayer))
3325             break;
3326     }
3327 }
3328
3329
3330
3331
3332 void
3333 UpdateCatched(int Color, int Figure, int Drop, int DropAll, int currentMove)
3334 {
3335     int n, F;
3336     int x, y;
3337
3338     /* Determine first row and column. */
3339
3340     if (Color)
3341     {
3342         x = -1;
3343         y = BOARD_SIZE - 1;
3344     }
3345     else
3346     {
3347         x = BOARD_SIZE;
3348         y = 0;
3349     }
3350
3351     if (DropAll)
3352         n = 0;
3353     else
3354         n = catches[currentMove][Color][Figure];
3355
3356     /* Update the display for captured pieces
3357        if no piece of the dropped type is there (Drop && n==1)
3358        or if a piece type is removed (NOT Drop && n==0).
3359        In the other cases update only the count. */
3360
3361     if (DropAll || (Drop && (n == 1)) || (!Drop && (n == 0)))
3362     {
3363         /* show all captured pieces */
3364         n = 0;
3365
3366         for (F = pawn; F <= king; F++)
3367         {
3368             int c;
3369
3370             if ((c = catches[currentMove][Color][F]) > 0)
3371             {
3372                 n++;
3373                 DrawSquare(y, x, catchedIndexToPiece[Color][F]);
3374                 ShowCount(y, (Color ? (x - 1) : (x + 1)), c);
3375
3376                 if (Color)
3377                     y--;
3378                 else
3379                     y++;
3380             }
3381         }
3382
3383         if (DropAll)
3384         {
3385             for (; n < 9; n++)
3386             {
3387                 DrawSquare(y, x, EmptySquare);
3388                 ShowCount(y, (Color ? (x - 1) : (x + 1)), 0);
3389
3390                 if (Color)
3391                     y--;
3392                 else
3393                     y++;
3394             }
3395         }
3396         else if (!Drop)
3397         {
3398             /* remove one line! */
3399             DrawSquare(y, x, EmptySquare);
3400             ShowCount(y, (Color ? (x - 1) : (x + 1)), 0);
3401         }
3402     }
3403     else
3404     {
3405         /* show the actual count */
3406         for (F = pawn; F <= Figure - 1; F++)
3407         {
3408             if (catches[currentMove][Color][F] > 0)
3409             {
3410                 if (Color)
3411                     y--;
3412                 else
3413                     y++;
3414             }
3415         }
3416
3417         ShowCount(y, (Color ? (x - 1) : (x + 1)), n);
3418     }
3419 }
3420
3421
3422
3423
3424 #ifdef BLINK_COUNT
3425
3426 static int BlinkCount = 0;
3427 static int BlinkRow, BlinkCol;
3428 static ShogiSquare BlinkPiece;
3429
3430
3431 void
3432 BlinkSquareProc(void)
3433 {
3434     if (BlinkCount > 0)
3435     {
3436         BlinkCount--;
3437         DrawSquare (BlinkRow, BlinkCol,
3438                     ((BlinkCount & 1) ? EmptySquare : BlinkPiece));
3439
3440         if (BlinkCount > 0)
3441         {
3442             blinkSquareXID
3443                 = XtAppAddTimeOut(appContext,
3444                                   150,
3445                                   (XtTimerCallbackProc)BlinkSquareProc,
3446                                   NULL);
3447         }
3448     }
3449     else
3450     {
3451         BlinkCount = 0;
3452     }
3453 }
3454
3455
3456
3457
3458 void
3459 BlinkSquare(int row, int col, ShogiSquare piece)
3460 {
3461     BlinkCount = 2 * BLINK_COUNT + 1;
3462     BlinkRow = row;
3463     BlinkCol = col;
3464     BlinkPiece = piece;
3465     BlinkSquareProc();
3466 }
3467
3468
3469 #endif /* BLINK_COUNT */
3470
3471
3472
3473
3474 static int
3475 PieceOfCatched(int color, int x, int y, int currentMove)
3476 {
3477     int F, n;
3478
3479     if (color)
3480     {
3481         if (x != 1)
3482             return (no_piece);
3483
3484         y = 8 - y;
3485     }
3486     else
3487     {
3488         if (x != 11)
3489             return no_piece;
3490     }
3491
3492     for (F = pawn, n = 0; F <= king; F++)
3493     {
3494         if (catches[currentMove][color][F] > 0)
3495         {
3496             if (n == y)
3497                 return F;
3498
3499             n++;
3500         }
3501     }
3502
3503     return no_piece;
3504 }
3505
3506
3507
3508
3509 /*
3510  * event handler for parsing user moves
3511  */
3512
3513 void
3514 HandleUserMove(Widget w, XEvent *event)
3515 {
3516     ShogiMove move_type;
3517     ShogiSquare from_piece;
3518     int to_x, to_y, fromRemotePlayer;
3519
3520     if (updateRemotePlayer)
3521     {
3522         if (((w != localPlayer.boardWidget)
3523              && (w != remotePlayer.boardWidget))
3524             || (matchMode != MatchFalse))
3525         {
3526             return;
3527         }
3528
3529         fromRemotePlayer = (w == remotePlayer.boardWidget);
3530     }
3531     else
3532     {
3533         if ((w != localPlayer.boardWidget) || (matchMode != MatchFalse))
3534             return;
3535
3536         fromRemotePlayer = False;
3537     }
3538
3539     player = (fromRemotePlayer ? &remotePlayer : &localPlayer);
3540
3541     if (player->promotionUp)
3542     {
3543         XtPopdown(player->promotionShell);
3544         XtDestroyWidget(player->promotionShell);
3545         player->promotionUp = False;
3546         fromX = fromY = -1;
3547     }
3548
3549     switch (gameMode)
3550     {
3551     case EndOfGame:
3552     case PlayFromGameFile:
3553     case TwoMachinesPlay:
3554         return;
3555
3556     case MachinePlaysBlack:
3557         if (BlackOnMove(forwardMostMove))
3558         {
3559             DisplayMessage("It is not your turn", fromRemotePlayer);
3560             return;
3561         }
3562
3563         break;
3564
3565     case MachinePlaysWhite:
3566         if (!BlackOnMove(forwardMostMove))
3567         {
3568             DisplayMessage("It is not your turn", fromRemotePlayer);
3569             return;
3570         }
3571
3572         break;
3573
3574     case ForceMoves:
3575         forwardMostMove = currentMove;
3576         break;
3577
3578     default:
3579         break;
3580     }
3581
3582     if (currentMove != forwardMostMove)
3583     {
3584         DisplayMessage("Displayed position is not current",
3585                        fromRemotePlayer);
3586         return;
3587     }
3588
3589     switch (event->type)
3590     {
3591     case ButtonPress:
3592         if ((fromX >= 0) || (fromY >= 0))
3593             return;
3594
3595         if (((fromX = EventToXSquare(event->xbutton.x)) < 1)
3596             || (fromX > BOARD_SIZE + 2)
3597             || ((fromY = EventToSquare(event->xbutton.y)) < 0))
3598         {
3599             fromX = fromY = -1;
3600             return;
3601         }
3602
3603         if (player->flipView)
3604             fromX = BOARD_SIZE + 3 - fromX;
3605         else
3606             fromY = BOARD_SIZE - 1 - fromY;
3607
3608         break;
3609
3610     case ButtonRelease:
3611         if ((fromX < 0) || (fromY < 0))
3612             return;
3613
3614         if (((to_x = EventToXSquare(event->xbutton.x)) < 1)
3615             || (to_x > BOARD_SIZE + 2)
3616             || ((to_y = EventToSquare(event->xbutton.y)) < 0))
3617         {
3618             if (gameMode == EditPosition && !off_board(fromX))
3619             {
3620                 fromX -= 2;
3621                 boards[0][fromY][fromX] = EmptySquare;
3622                 DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
3623                 XSync(localPlayer.xDisplay, False);
3624
3625                 if (updateRemotePlayer)
3626                     XSync(remotePlayer.xDisplay, False);
3627             }
3628
3629             fromX = fromY = -1;
3630             return;
3631         }
3632
3633         if (player->flipView)
3634             to_x = BOARD_SIZE + 3 - to_x;
3635         else
3636             to_y = BOARD_SIZE - 1 - to_y;
3637
3638         if ((fromX == to_x) && (fromY == to_y))
3639         {
3640             fromX = fromY = -1;
3641             return;
3642         }
3643
3644         if (gameMode == EditPosition)
3645         {
3646             ShogiSquare piece;
3647
3648             if (off_board(fromX))
3649             {
3650                 /* Remove a catched piece */
3651                 int i, c;
3652                 c = ((fromX < 5) ^ player->flipView);
3653                 i = PieceOfCatched(c, fromX, fromY, 0);
3654
3655                 if (i == no_piece)
3656                 {
3657                     fromX = fromY = -1;
3658                     return;
3659                 }
3660                 else
3661                 {
3662                     piece = catchedIndexToPiece[c][i];
3663                     catches[0][c][i]--;
3664                 }
3665             }
3666             else
3667             {
3668                 /* remove piece from board field */
3669                 fromX -= 2;
3670                 piece = boards[0][fromY][fromX];
3671                 boards[0][fromY][fromX] = EmptySquare;
3672             }
3673
3674             if (!off_board(to_x))
3675             {
3676                 /* drop piece to board field */
3677                 ShogiSquare catched_piece;
3678                 to_x -= 2;
3679                 catched_piece = boards[0][to_y][to_x];
3680
3681                 if (catched_piece != EmptySquare)
3682                 {
3683                     /* put piece to catched pieces */
3684                     int i = pieceToCatchedIndex[catched_piece];
3685                     int c = (catched_piece < WhitePawn);
3686                     catches[0][c][i]++;
3687                 }
3688
3689                 /* place moved piece */
3690                 boards[0][to_y][to_x] = piece;
3691             }
3692
3693             fromX = fromY = -1;
3694             DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
3695             XSync(localPlayer.xDisplay, False);
3696
3697             if (updateRemotePlayer)
3698                 XSync(remotePlayer.xDisplay, False);
3699
3700             return;
3701         }
3702
3703         if (off_board(fromX))
3704         {
3705             int c     = (BlackOnMove(forwardMostMove) ? 0 : 1);
3706             int piece = PieceOfCatched(c, fromX, fromY, currentMove);
3707
3708             if (piece == no_piece)
3709             {
3710                 fromX = fromY = -1;
3711                 return;
3712             }
3713             else
3714             {
3715                 if (updateRemotePlayer
3716                     && (BlackOnMove(forwardMostMove) == fromRemotePlayer))
3717                 {
3718                     DisplayMessage("Do not try to drop your opponent's pieces!",
3719                                    fromRemotePlayer);
3720                     fromX = fromY = -1;
3721                     return;
3722                 }
3723
3724                 fromX = fromY = piece + 81;
3725                 to_x -= 2;
3726                 move_type = (BlackOnMove(forwardMostMove)
3727                              ? BlackDrop : WhiteDrop);
3728                 MakeMove(&move_type, fromX, fromY, to_x, to_y);
3729
3730 #ifdef BLINK_COUNT
3731                 if (updateRemotePlayer)
3732                     BlinkSquare(to_y, to_x, boards[currentMove][to_y][to_x]);
3733 #endif
3734
3735                 FinishUserMove(move_type, to_x, to_y);
3736                 break;
3737             }
3738         }
3739         else if (off_board(to_x))
3740         {
3741             fromX = fromY = -1;
3742             return;
3743         }
3744         else
3745         {
3746             fromX -= 2;
3747             to_x -= 2;
3748             from_piece = boards[currentMove][fromY][fromX];
3749
3750             if ((from_piece != EmptySquare)
3751                 && updateRemotePlayer
3752                 && ((from_piece < WhitePawn) == fromRemotePlayer))
3753             {
3754                 DisplayMessage("Do not try to move your opponent's pieces!",
3755                                fromRemotePlayer);
3756                 fromX = fromY = -1;
3757                 return;
3758             }
3759
3760             if (PromotionPossible(fromY, to_y, from_piece))
3761             {
3762                 PromotionPopUp(from_piece, to_x, to_y, fromRemotePlayer);
3763                 return;
3764             }
3765
3766             move_type = NormalMove;
3767             MakeMove(&move_type, fromX, fromY, to_x, to_y);
3768
3769 #ifdef BLINK_COUNT
3770             if (updateRemotePlayer)
3771                 BlinkSquare(to_y, to_x, boards[currentMove][to_y][to_x]);
3772 #endif
3773
3774             FinishUserMove(move_type, to_x, to_y);
3775             break;
3776         }
3777     }
3778 }
3779
3780
3781
3782
3783 void
3784 FinishUserMove(ShogiMove move_type, int to_x, int to_y)
3785 {
3786     char user_move[MSG_SIZ];
3787
3788     /* output move for gnushogi */
3789     switch (move_type)
3790     {
3791     case BlackPromotion:
3792     case WhitePromotion:
3793         sprintf(user_move, "%c%c%c%c+\n",
3794                 '9' - fromX, 'i' - fromY, '9' - to_x, 'i' - to_y);
3795         break;
3796
3797     case BlackDrop:
3798     case WhiteDrop:
3799         sprintf(user_move, "%c*%c%c\n",
3800                 catchedIndexToChar[fromX - 81], '9' - to_x, 'i' - to_y);
3801         break;
3802
3803     case NormalMove:
3804         sprintf(user_move, "%c%c%c%c\n",
3805                 '9' - fromX, 'i' - fromY, '9' - to_x, 'i' - to_y);
3806         break;
3807
3808     default:
3809         fprintf(stderr, "%s: internal error; bad move_type\n",
3810                 (char *)programName);
3811         break;
3812     }
3813
3814     Attention(firstProgramPID);
3815
3816     if (firstSendTime)
3817         SendTimeRemaining(toFirstProgFP);
3818
3819     SendToProgram(user_move, toFirstProgFP);
3820     strcpy(moveList[currentMove - 1], user_move);
3821
3822     fromX = fromY = -1;
3823
3824     if (gameMode == PauseGame)
3825     {
3826         /* a user move restarts a paused game*/
3827         PauseProc(NULL, NULL, NULL, NULL);
3828     }
3829
3830     switch (gameMode)
3831     {
3832     case ForceMoves:
3833         break;
3834
3835     case BeginningOfGame:
3836         if (localPlayer.appData.noShogiProgram)
3837             lastGameMode = gameMode = ForceMoves;
3838         else
3839             lastGameMode = gameMode = MachinePlaysWhite;
3840
3841         ModeHighlight();
3842         break;
3843
3844     case MachinePlaysWhite:
3845     case MachinePlaysBlack:
3846     default:
3847         break;
3848     }
3849 }
3850
3851
3852
3853
3854 /* Simple parser for moves from gnushogi. */
3855 void
3856 ParseMachineMove(char *machine_move, ShogiMove *move_type,
3857                  int *from_x, int *from_y, int *to_x, int *to_y)
3858 {
3859 #define no_digit(c) (c < '0' || c > '9')
3860     {
3861         if (no_digit(machine_move[0]))
3862         {
3863             switch (machine_move[0])
3864             {
3865             case 'P':
3866                 *from_x = 81;
3867                 break;
3868
3869             case 'L':
3870                 *from_x = 82;
3871                 break;
3872
3873             case 'N':
3874                 *from_x = 83;
3875                 break;
3876
3877             case 'S':
3878                 *from_x = 84;
3879                 break;
3880
3881             case 'G':
3882                 *from_x = 85;
3883                 break;
3884
3885             case 'B':
3886                 *from_x = 86;
3887                 break;
3888
3889             case 'R':
3890                 *from_x = 87;
3891                 break;
3892
3893             case 'K':
3894                 *from_x = 88;
3895                 break;
3896
3897             default:
3898                 *from_x = -1;
3899             }
3900
3901             *from_y = *from_x;
3902             *to_x   = '9' - machine_move[2];
3903             *to_y   = 'i' - machine_move[3];
3904         }
3905         else
3906         {
3907             *from_x = '9' - machine_move[0] ;
3908             *from_y = 'i' - machine_move[1];
3909             *to_x   = '9' - machine_move[2];
3910             *to_y   = 'i' - machine_move[3];
3911
3912             switch (machine_move[4])
3913             {
3914             case '+':
3915                 *move_type = (BlackOnMove(forwardMostMove)
3916                     ? BlackPromotion : WhitePromotion);
3917                 break;
3918
3919             default:
3920                 *move_type = NormalMove;
3921                 break;
3922             }
3923         }
3924     }
3925 }
3926
3927
3928
3929
3930 void
3931 SkipString(char **mpr)
3932 {
3933     while (**mpr == ' ')
3934         (*mpr)++;
3935
3936     while ((**mpr != ' ') && (**mpr != NULLCHAR) && (**mpr != '\n'))
3937         (*mpr)++;
3938
3939     while (**mpr == ' ')
3940         (*mpr)++;
3941 }
3942
3943
3944
3945
3946 void
3947 HandleMachineMove(char *message, FILE *fp)
3948 {
3949     char machine_move[MSG_SIZ], buf1[MSG_SIZ], buf2[MSG_SIZ];
3950     int from_x, from_y, to_x, to_y;
3951     ShogiMove move_type;
3952     char *mpr;
3953
3954 #ifdef SYNCHTIME
3955     long time_remaining;
3956 #endif
3957
3958     maybeThinking = False;
3959
3960     if (strncmp(message, "warning:", 8) == 0)
3961     {
3962         DisplayMessage(message, False);
3963
3964         if (updateRemotePlayer)
3965             DisplayMessage(message, True);
3966
3967         return;
3968     }
3969
3970     /*
3971      * If shogi program startup fails, exit with an error message.
3972      * Attempts to recover here are futile.
3973      */
3974
3975     if ((strstr(message, "unknown host") != NULL)
3976         || (strstr(message, "No remote directory") != NULL)
3977         || (strstr(message, "not found") != NULL)
3978         || (strstr(message, "No such file") != NULL)
3979         || (strstr(message, "Permission denied") != NULL))
3980     {
3981         fprintf(stderr,
3982                 "%s: failed to start shogi program %s on %s: %s\n",
3983                 programName,
3984                 ((fp == fromFirstProgFP)
3985                  ? localPlayer.appData.firstShogiProgram
3986                  : localPlayer.appData.secondShogiProgram),
3987                 ((fp == fromFirstProgFP)
3988                  ? localPlayer.appData.firstHost
3989                  : localPlayer.appData.secondHost),
3990                 message);
3991         ShutdownShogiPrograms(message);
3992         exit(1);
3993     }
3994
3995     /*
3996      * If the move is illegal, cancel it and redraw the board.
3997      */
3998
3999     if (strncmp(message, "Illegal move", 12) == 0)
4000     {
4001         if (fp == fromFirstProgFP && firstSendTime == 2)
4002         {
4003             /* First program doesn't have the "time" command */
4004             firstSendTime = 0;
4005             return;
4006         }
4007         else if (fp == fromSecondProgFP && secondSendTime == 2)
4008         {
4009             /* Second program doesn't have the "time" command */
4010             secondSendTime = 0;
4011             return;
4012         }
4013
4014         if (forwardMostMove <= backwardMostMove)
4015             return;
4016
4017         if (gameMode == PauseGame)
4018             PauseProc(NULL, NULL, NULL, NULL);
4019
4020         if (gameMode == PlayFromGameFile)
4021         {
4022             /* Stop reading this game file */
4023             gameMode = ForceMoves;
4024             ModeHighlight();
4025         }
4026
4027         currentMove = --forwardMostMove;
4028
4029         if ((gameMode == PlayFromGameFile)
4030             || (gameMode == ForceMoves))
4031             DisplayClocks(ReDisplayTimers);
4032         else
4033             DisplayClocks(SwitchTimers);
4034
4035         sprintf(buf1, "Illegal move: %s", parseList[currentMove]);
4036         DisplayMessage(buf1, False);
4037
4038         if (updateRemotePlayer)
4039             DisplayMessage(buf1, True);
4040
4041 #ifdef BLINK_COUNT
4042         /*
4043          * Disable blinking of the target square.
4044          */
4045
4046         if (BlinkCount > 0)
4047         {
4048             /* If BlinkCount is even, the piece is currently displayed. */
4049             if (!(BlinkCount & 1))
4050                 DrawSquare (BlinkRow, BlinkCol, EmptySquare);
4051
4052             /* BlinkCount = 0 will force the next blink timeout
4053              * to do nothing. */
4054             BlinkCount = 0;
4055         }
4056 #endif
4057
4058         DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
4059
4060         XSync(localPlayer.xDisplay, False);
4061
4062         if (updateRemotePlayer)
4063             XSync(remotePlayer.xDisplay, False);
4064
4065         return;
4066     }
4067
4068     if (strstr(message, "GNU Shogi") != NULL)
4069     {
4070         at_least_gnushogi_1_2p03 = True;
4071         return;
4072     }
4073
4074     if (strncmp(message, "Hint:", 5) == 0)
4075     {
4076         char promoPiece;
4077         sscanf(message, "Hint: %s", machine_move);
4078         ParseMachineMove(machine_move, &move_type,
4079                          &from_x, &from_y, &to_x, &to_y);
4080
4081         if (move_type == WhitePromotion || move_type == BlackPromotion)
4082             promoPiece = '+';
4083         else
4084             promoPiece = NULLCHAR;
4085
4086         move_type = MakeAlg(from_x, from_y, to_x, to_y, promoPiece,
4087                             currentMove, buf1);
4088         sprintf(buf2, "Hint: %s", buf1);
4089         DisplayMessage(buf2, False);
4090
4091         if (updateRemotePlayer)
4092             DisplayMessage(buf2, True);
4093
4094         return;
4095     }
4096
4097     if (strncmp(message, "Clocks:", 7) == 0)
4098     {
4099         sscanf(message, "Clocks: %ld %ld",
4100                &blackTimeRemaining, &whiteTimeRemaining);
4101         DisplayClocks(ReDisplayTimers);
4102
4103         return;
4104     }
4105
4106     /*
4107      * win, lose or draw
4108      */
4109
4110     if (strncmp(message, "Black", 5) == 0)
4111     {
4112         ShutdownShogiPrograms("Black wins");
4113         return;
4114     }
4115     else if (strncmp(message, "White", 5) == 0)
4116     {
4117         ShutdownShogiPrograms("White wins");
4118         return;
4119     }
4120     else if (strncmp(message, "Repetition", 10) == 0)
4121     {
4122         ShutdownShogiPrograms("Repetition");
4123         return;
4124     }
4125     else if (strncmp(message, "opponent mates!", 15) == 0)
4126     {
4127         switch ((gameMode == PauseGame) ? pausePreviousMode : gameMode)
4128         {
4129         case MachinePlaysWhite:
4130             ShutdownShogiPrograms("Black wins");
4131             break;
4132
4133         case MachinePlaysBlack:
4134             ShutdownShogiPrograms("White wins");
4135             break;
4136
4137         case TwoMachinesPlay:
4138             ShutdownShogiPrograms((fp == fromFirstProgFP)
4139                                   ? "Black wins" : "White wins");
4140             break;
4141
4142         default:
4143             /* can't happen */
4144             break;
4145         }
4146
4147         return;
4148     }
4149     else if (strncmp(message, "computer mates!", 15) == 0)
4150     {
4151         switch ((gameMode == PauseGame) ? pausePreviousMode : gameMode)
4152         {
4153         case MachinePlaysWhite:
4154             ShutdownShogiPrograms("White wins");
4155             break;
4156
4157         case MachinePlaysBlack:
4158             ShutdownShogiPrograms("Black wins");
4159             break;
4160
4161         case TwoMachinesPlay:
4162             ShutdownShogiPrograms((fp == fromFirstProgFP)
4163                                   ? "White wins" : "Black wins");
4164             break;
4165
4166         default:
4167             /* can't happen */
4168             break;
4169         }
4170
4171         return;
4172     }
4173     else if (strncmp(message, "Draw", 4) == 0)
4174     {
4175         ShutdownShogiPrograms("Draw");
4176         return;
4177     }
4178
4179     /*
4180      * normal machine reply move
4181      */
4182     maybeThinking = True;
4183
4184     if (strstr(message, "...") != NULL)
4185     {
4186         sscanf(message, "%s %s %s", buf1, buf2, machine_move);
4187
4188 #ifdef SYNCHTIME
4189         mpr = message;
4190         SkipString(&mpr); /* skip move number */
4191         SkipString(&mpr); /* skip ... */
4192         SkipString(&mpr); /* skip move */
4193
4194         if ((gameMode != TwoMachinesPlay) && (gameMode != ForceMoves)
4195             && ((*mpr == '-') || ((*mpr >= '0') && (*mpr <= '9'))))
4196         {
4197             /* synchronize with shogi program clock */
4198             sscanf(mpr, "%ld", &time_remaining);
4199
4200             if (xshogiDebug)
4201             {
4202                 printf("from '%s' synchronize %s clock %ld\n",
4203                        message,
4204                        (BlackOnMove(forwardMostMove)
4205                         ? "Black's"
4206                         : "White's"),
4207                        time_remaining);
4208             }
4209
4210             if (BlackOnMove(forwardMostMove))
4211                 blackTimeRemaining = time_remaining;
4212             else
4213                 whiteTimeRemaining = time_remaining;
4214         }
4215 #endif
4216
4217         if (machine_move[0] == NULLCHAR)
4218             return;
4219     }
4220     else
4221     {
4222         mpr = message;
4223
4224 #ifdef SYNCHTIME
4225         if (strstr(message, "time") == NULL)
4226         {
4227             /* remaining time will be determined from move */
4228             SkipString(&mpr); /* skip move number */
4229             SkipString(&mpr); /* skip move */
4230         }
4231
4232         if ((gameMode != TwoMachinesPlay) && (gameMode != ForceMoves)
4233             && ((*mpr == '-') || ((*mpr >= '0') && (*mpr <= '9'))))
4234         {
4235             /* synchronize with shogi program clock */
4236             sscanf(mpr, "%ld", &time_remaining);
4237
4238             if (xshogiDebug)
4239             {
4240                 printf("from '%s' synchronize %s clock %ld\n",
4241                        message,
4242                        ((!BlackOnMove(forwardMostMove))
4243                         ? "Black's" : "White's"),
4244                        time_remaining);
4245             }
4246
4247             if (!BlackOnMove(forwardMostMove))
4248                 blackTimeRemaining = time_remaining;
4249             else
4250                 whiteTimeRemaining = time_remaining;
4251         }
4252         else
4253 #endif
4254
4255             if (xshogiDebug)
4256                 printf("ignore noise: '%s'\n", message);
4257
4258         return; /* ignore noise */
4259     }
4260
4261     strcpy(moveList[forwardMostMove], machine_move);
4262
4263     ParseMachineMove(machine_move, &move_type, &from_x, &from_y,
4264                      &to_x, &to_y);
4265
4266     if (gameMode != PauseGame)
4267         currentMove = forwardMostMove;  /* display latest move */
4268
4269     MakeMove(&move_type, from_x, from_y, to_x, to_y);
4270
4271 #ifdef BLINK_COUNT
4272     if (gameMode != TwoMachinesPlay)
4273         BlinkSquare(to_y, to_x, boards[currentMove][to_y][to_x]);
4274 #endif
4275
4276     if ((gameMode != PauseGame) && localPlayer.appData.ringBellAfterMoves)
4277         putc(BELLCHAR, stderr);
4278
4279     if ((gameMode == TwoMachinesPlay)
4280         || ((gameMode == PauseGame)
4281             && (pausePreviousMode == TwoMachinesPlay)))
4282     {
4283         strcat(machine_move, "\n");
4284
4285         if (BlackOnMove(forwardMostMove))
4286         {
4287             Attention(secondProgramPID);
4288
4289             if (secondSendTime)
4290                 SendTimeRemaining(toSecondProgFP);
4291
4292             SendToProgram(machine_move, toSecondProgFP);
4293
4294             if (firstMove)
4295             {
4296                 firstMove = False;
4297                 SendToProgram(localPlayer.appData.blackString,
4298                               toSecondProgFP);
4299             }
4300         }
4301         else
4302         {
4303             Attention(firstProgramPID);
4304
4305             if (firstSendTime)
4306                 SendTimeRemaining(toFirstProgFP);
4307
4308             SendToProgram(machine_move, toFirstProgFP);
4309
4310             if (firstMove)
4311             {
4312                 firstMove = False;
4313                 SendToProgram(localPlayer.appData.blackString,
4314                               toFirstProgFP);
4315             }
4316         }
4317     }
4318 }
4319
4320
4321
4322
4323 void
4324 ReadGameFile(void)
4325 {
4326     for (;;)
4327     {
4328         if (!ReadGameFileProc())
4329             return;
4330
4331         if (matchMode == MatchOpening)
4332             continue;
4333
4334         readGameXID
4335             = XtAppAddTimeOut(appContext,
4336                               (int)(1000 * localPlayer.appData.timeDelay),
4337                               (XtTimerCallbackProc) ReadGameFile, NULL);
4338         break;
4339     }
4340 }
4341
4342
4343
4344 /*
4345  * FIXME: there is a naming inconsistency: here ReadGameFileProc() is
4346  * called by ReadGameFile() while in other places XXXProc() calls XXX().
4347  */
4348
4349 int
4350 ReadGameFileProc(void)
4351 {
4352     ShogiMove move_type;
4353     char move[MSG_SIZ], buf[MSG_SIZ];
4354
4355     if (gameFileFP == NULL)
4356         return (int)False;
4357
4358     if (gameMode == PauseGame)
4359         return True;
4360
4361     if (gameMode != PlayFromGameFile)
4362     {
4363         fclose(gameFileFP);
4364         gameFileFP = NULL;
4365         return (int)False;
4366     }
4367
4368     if (commentUp)
4369     {
4370         XtPopdown(commentShell);
4371         XtDestroyWidget(commentShell);
4372         commentUp = False;
4373     }
4374
4375     fgets(move, MSG_SIZ, gameFileFP);
4376     move[strlen(move) - 1] = NULLCHAR;
4377     sprintf(buf, "# %s game file", programName);
4378
4379     if (strncmp(move, buf, strlen(buf)))
4380     {
4381         strcat(move, ": no xshogi game file");
4382         DisplayMessage(move, False);
4383         return (int)False;
4384     }
4385
4386     DisplayName(move);
4387     rewind(gameFileFP);
4388
4389     parseGameFile();
4390
4391     move_type = (ShogiMove)0;
4392
4393     lastGameMode = gameMode;
4394     gameMode = ForceMoves;
4395     ModeHighlight();
4396
4397     if (!loaded_game_finished)
4398         DisplayMessage("End of game file", False);
4399
4400     if (readGameXID != 0)
4401     {
4402         XtRemoveTimeOut(readGameXID);
4403         readGameXID = 0;
4404     }
4405
4406     fclose(gameFileFP);
4407     gameFileFP = NULL;
4408
4409     return (int)False;
4410 }
4411
4412
4413
4414
4415 /*
4416  * Apply a move to the given board.  Oddity: move_type is ignored on input
4417  * unless the move is seen to be a pawn promotion, in which case move_type
4418  * tells us what to promote to.
4419  */
4420
4421 void
4422 ApplyMove(ShogiMove *move_type, int from_x, int from_y,
4423           int to_x, int to_y, int currentMove)
4424 {
4425     ShogiSquare piece, cpiece;
4426     char pieceChar;
4427     int  i, c;
4428
4429     if (from_x > 80)
4430     {
4431         i = from_x - 81;
4432         c = (BlackOnMove(currentMove) ? 1 : 0);
4433         cpiece = catchedIndexToPiece[c][i];
4434         boards[currentMove][to_y][to_x] = cpiece;
4435         catches[currentMove][c][i]--;
4436     }
4437     else if (PromotionPossible(from_y, to_y,
4438                                piece = boards[currentMove][from_y][from_x]))
4439     {
4440         cpiece = boards[currentMove][to_y][to_x];
4441
4442         if (cpiece != EmptySquare)
4443         {
4444             i = pieceToCatchedIndex[cpiece];
4445             c = (cpiece < WhitePawn);
4446             catches[currentMove][c][i]++;
4447         }
4448
4449         if (*move_type == NormalMove)
4450         {
4451             boards[currentMove][to_y][to_x] = piece;
4452         }
4453         else
4454         {
4455             boards[currentMove][to_y][to_x] = piece = pieceToPromoted[piece];
4456             pieceChar = '+';
4457         }
4458
4459         boards[currentMove][from_y][from_x] = EmptySquare;
4460     }
4461     else
4462     {
4463         ShogiSquare piece = boards[currentMove][to_y][to_x];
4464
4465         if (piece != EmptySquare)
4466         {
4467             i = pieceToCatchedIndex[piece];
4468             c = (piece < WhitePawn);
4469             catches[currentMove][c][i]++;
4470         }
4471
4472         *move_type = NormalMove;
4473         boards[currentMove][to_y][to_x] =
4474             boards[currentMove][from_y][from_x];
4475         boards[currentMove][from_y][from_x] = EmptySquare;
4476     }
4477 }
4478
4479
4480
4481
4482 /*
4483  * MakeMove() displays moves.  If they are illegal, GNU shogi will detect
4484  * this and send an Illegal move message.  XShogi will then retract the move.
4485  * The clockMode False case is tricky because it displays the player on move.
4486  */
4487
4488 void
4489 MakeMove(ShogiMove *move_type, int from_x, int from_y, int to_x, int to_y)
4490 {
4491     char message[MSG_SIZ], movestr[MSG_SIZ];
4492     char promoPiece = NULLCHAR;
4493
4494     forwardMostMove++;
4495
4496     CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);
4497     CopyCatches(catches[forwardMostMove], catches[forwardMostMove - 1]);
4498
4499     ApplyMove(move_type, from_x, from_y, to_x, to_y, forwardMostMove);
4500
4501     endMessage[0] = NULLCHAR;
4502
4503     timeRemaining[0][forwardMostMove] = blackTimeRemaining;
4504     timeRemaining[1][forwardMostMove] = whiteTimeRemaining;
4505
4506     if ((gameMode == PauseGame) && (pausePreviousMode != PlayFromGameFile))
4507         return;
4508
4509     currentMove = forwardMostMove;
4510
4511     if (gameMode == PlayFromGameFile)
4512     {
4513         sprintf(message, "%d. %s%s",
4514                 ((currentMove + 1) / 2),
4515                 (BlackOnMove(currentMove) ? "... " : ""),
4516                 currentMoveString);
4517         strcpy(parseList[currentMove - 1], currentMoveString);
4518     }
4519     else
4520     {
4521         if ((*move_type == WhitePromotion) || (*move_type == BlackPromotion))
4522             promoPiece = '+';
4523         else
4524             promoPiece = NULLCHAR;
4525
4526         MakeAlg(from_x, from_y, to_x, to_y, promoPiece,
4527                 currentMove - 1, movestr);
4528         sprintf(message, "%d. %s%s",
4529                 ((currentMove + 1) / 2),
4530                 (BlackOnMove(currentMove) ? "... " : ""),
4531                 movestr);
4532         strcpy(parseList[currentMove - 1], movestr);
4533     }
4534
4535     DisplayMessage(message, False);
4536
4537     if ((gameMode == PlayFromGameFile) || (gameMode == ForceMoves)
4538         || ((gameMode == PauseGame)
4539             && (pausePreviousMode == PlayFromGameFile)))
4540     {
4541         DisplayClocks(ReDisplayTimers);
4542     }
4543     else
4544     {
4545         DisplayClocks(SwitchTimers);
4546     }
4547
4548     DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
4549
4550     XSync(localPlayer.xDisplay, False);
4551
4552     if (updateRemotePlayer)
4553     {
4554         DisplayMessage(message, True);
4555         XSync(remotePlayer.xDisplay, False);
4556     }
4557 }
4558
4559
4560
4561
4562 void
4563 InitShogiProgram(char *host_name, char *program_name, int *pid,
4564                  FILE **to, FILE **from, XtIntervalId *xid, int *sendTime)
4565 {
4566     char  arg_buf[10];
4567     char *arg1, *arg2;
4568     int   to_prog[2], from_prog[2];
4569     FILE *from_fp, *to_fp;
4570     int   dummy_source;
4571     XtInputId dummy_id;
4572
4573     if (localPlayer.appData.noShogiProgram)
4574         return;
4575
4576     signal(SIGPIPE, CatchPipeSignal);
4577     pipe(to_prog);
4578     pipe(from_prog);
4579
4580     if ((*pid = fork()) == 0)
4581     {
4582         signal(SIGPIPE, CatchPipeSignal);
4583
4584         dup2(to_prog[0], 0);
4585         dup2(from_prog[1], 1);
4586         close(to_prog[0]);
4587         close(to_prog[1]);
4588         close(from_prog[0]);
4589         close(from_prog[1]);
4590         dup2(1, fileno(stderr));    /* force stderr to the pipe */
4591
4592         if (localPlayer.appData.searchTime != NULL)
4593         {
4594             sprintf(arg_buf, "%d", searchTime);
4595             arg1 = arg_buf;
4596             arg2 = (char *)NULL;
4597         }
4598         else if (localPlayer.appData.searchDepth > 0)
4599         {
4600             sprintf(arg_buf, "%d", localPlayer.appData.searchDepth);
4601             arg1 = "1";
4602             arg2 = "9999";
4603         }
4604         else
4605         {
4606             sprintf(arg_buf, "%d", localPlayer.appData.movesPerSession);
4607             arg1 = arg_buf;
4608             arg2 = localPlayer.appData.timeControl;
4609         }
4610
4611         if (strcmp(host_name, "localhost") == 0)
4612         {
4613             execlp(program_name, program_name, arg1, arg2,
4614                    (char *)NULL);
4615         }
4616         else
4617         {
4618             execlp(localPlayer.appData.remoteShell,
4619                    localPlayer.appData.remoteShell,
4620                    host_name, program_name, arg1, arg2,
4621                    (char *)NULL);
4622         }
4623
4624         perror(program_name);
4625         exit(1);
4626     }
4627
4628     close(to_prog[0]);
4629     close(from_prog[1]);
4630
4631     *from = from_fp = fdopen(from_prog[0], "r");
4632     *to   = to_fp   = fdopen(to_prog[1],   "w");
4633     setbuf(from_fp, NULL);
4634     setbuf(to_fp,   NULL);
4635
4636     ReceiveFromProgram(from_fp, &dummy_source, &dummy_id); /* "GNU Shogi"*/
4637
4638     if (!at_least_gnushogi_1_2p03)
4639     {
4640         fprintf(stderr, "you must have at least gnushogi-1.2p03\n");
4641         exit(1);
4642     }
4643
4644     if (*pid == 0)
4645         return;
4646
4647     *xid = XtAppAddInput(appContext, fileno(from_fp),
4648                          (XtPointer)XtInputReadMask,
4649                          (XtInputCallbackProc)ReceiveFromProgram,
4650                          (XtPointer)from_fp);
4651
4652     SendToProgram(localPlayer.appData.initString, *to);
4653
4654     if (localPlayer.appData.gameIn)
4655         SendToProgram("gamein\n", *to);
4656
4657     SendSearchDepth(*to);
4658
4659     if (*sendTime == 2)
4660     {
4661         /* Does program have "time" command? */
4662         char buf[MSG_SIZ];
4663
4664         sprintf(buf, "time %ld\n", blackTimeRemaining / 10);
4665         SendToProgram(buf, to_fp);
4666         ReceiveFromProgram(from_fp, &dummy_source, &dummy_id);
4667
4668         if (*sendTime == 2)
4669         {
4670             *sendTime = 1;  /* yes! */
4671             sprintf(buf, "otime %ld\n", whiteTimeRemaining / 10);
4672             SendToProgram(buf, to_fp);
4673             ReceiveFromProgram(from_fp, &dummy_source, &dummy_id);
4674         }
4675     }
4676 }
4677
4678
4679
4680
4681 void
4682 ShutdownShogiPrograms(char *why)
4683 {
4684     lastGameMode = gameMode;
4685     gameMode = EndOfGame;
4686     ModeHighlight();
4687     CopyBoard(boards[currentMove + 1], boards[currentMove]);
4688     CopyCatches(catches[currentMove + 1], catches[currentMove]);
4689     strncpy(parseList[currentMove], why, MOVE_LEN);
4690     parseList[currentMove][MOVE_LEN - 1] = NULLCHAR;
4691     currentMove++;
4692     DisplayMessage(why, False);
4693
4694     if (readGameXID != 0)
4695         XtRemoveTimeOut(readGameXID);
4696
4697     readGameXID = 0;
4698
4699     if (firstProgramPID != 0)
4700     {
4701         fclose(fromFirstProgFP);
4702         fclose(toFirstProgFP);
4703         fromFirstProgFP = toFirstProgFP = NULL;
4704
4705         if (kill(firstProgramPID, SIGTERM) == 0)
4706             WAIT0;
4707     }
4708
4709     firstProgramPID = 0;
4710
4711     if (firstProgramXID != 0)
4712         XtRemoveInput(firstProgramXID);
4713
4714     firstProgramXID = 0;
4715
4716     if (secondProgramPID != 0)
4717     {
4718         fclose(fromSecondProgFP);
4719         fclose(toSecondProgFP);
4720         fromSecondProgFP = toSecondProgFP = NULL;
4721
4722         if (kill(secondProgramPID, SIGTERM) == 0)
4723             WAIT0;
4724     }
4725
4726     secondProgramPID = 0;
4727
4728     if (secondProgramXID != 0)
4729         XtRemoveInput(secondProgramXID);
4730
4731     secondProgramXID = 0;
4732
4733     DisplayClocks(StopTimers);
4734
4735     if (matchMode != MatchFalse)
4736     {
4737         if (localPlayer.appData.saveGameFile[0] != NULLCHAR)
4738             SaveGame(localPlayer.appData.saveGameFile);
4739
4740         exit(0);
4741     }
4742 }
4743
4744
4745
4746
4747 void
4748 CommentPopUp(char *label)
4749 {
4750     Arg args[2];
4751     Position x, y;
4752     Dimension bw_width, pw_width;
4753
4754     if (commentUp)
4755     {
4756         XtPopdown(commentShell);
4757         XtDestroyWidget(commentShell);
4758         commentUp = False;
4759     }
4760
4761     DisplayMessage("Comment", False);
4762
4763     XtSetArg(args[0], XtNwidth, &bw_width);
4764     XtGetValues(localPlayer.formWidget, args, 1);
4765
4766     XtSetArg(args[0], XtNresizable, True);
4767     XtSetArg(args[1], XtNwidth, bw_width - 8);
4768
4769     commentShell = XtCreatePopupShell("Comment",
4770                                       transientShellWidgetClass,
4771                                       localPlayer.commandsWidget, args, 2);
4772
4773     XtSetArg(args[0], XtNlabel, label);
4774
4775     (void)XtCreateManagedWidget("commentLabel", labelWidgetClass,
4776                                 commentShell, args, 1);
4777
4778     XtRealizeWidget(commentShell);
4779
4780     XtSetArg(args[0], XtNwidth, &pw_width);
4781     XtGetValues(commentShell, args, 1);
4782
4783     XtTranslateCoords(localPlayer.shellWidget,
4784                       (bw_width - pw_width) / 2, -50, &x, &y);
4785
4786     XtSetArg(args[0], XtNx, x);
4787     XtSetArg(args[1], XtNy, y);
4788     XtSetValues(commentShell, args, 2);
4789
4790     XtPopup(commentShell, XtGrabNone);
4791     commentUp = True;
4792 }
4793
4794
4795
4796
4797 void
4798 FileNamePopUp(char *label, Boolean (*proc) (char *))
4799 {
4800     Arg args[2];
4801     Widget popup, dialog;
4802     Position x, y;
4803     Dimension bw_width, pw_width;
4804
4805     fileProc = proc;
4806
4807     XtSetArg(args[0], XtNwidth, &bw_width);
4808     XtGetValues(localPlayer.boardWidget, args, 1);
4809
4810     XtSetArg(args[0], XtNresizable, True);
4811     XtSetArg(args[1], XtNwidth, DIALOG_SIZE);
4812
4813     popup = XtCreatePopupShell("File Name Prompt",
4814                                transientShellWidgetClass,
4815                                localPlayer.commandsWidget, args, 2);
4816
4817     XtSetArg(args[0], XtNlabel, label);
4818     XtSetArg(args[1], XtNvalue, "");
4819
4820     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4821                                    popup, args, 2);
4822
4823     XawDialogAddButton(dialog, "ok", FileNameCallback, (XtPointer) dialog);
4824     XawDialogAddButton(dialog, "cancel", FileNameCallback,
4825                        (XtPointer) dialog);
4826
4827     XtRealizeWidget(popup);
4828
4829     XtSetArg(args[0], XtNwidth, &pw_width);
4830     XtGetValues(popup, args, 1);
4831
4832     XtTranslateCoords(localPlayer.boardWidget,
4833                       (bw_width - pw_width) / 2, 10, &x, &y);
4834
4835     XtSetArg(args[0], XtNx, x);
4836     XtSetArg(args[1], XtNy, y);
4837     XtSetValues(popup, args, 2);
4838
4839     XtPopup(popup, XtGrabExclusive);
4840     filenameUp = True;
4841
4842     XtSetKeyboardFocus(localPlayer.shellWidget, popup);
4843 }
4844
4845
4846
4847
4848 void
4849 FileNameCallback(Widget w, XtPointer client_data, XtPointer call_data)
4850 {
4851     String name;
4852     Arg args[1];
4853
4854     XtSetArg(args[0], XtNlabel, &name);
4855     XtGetValues(w, args, 1);
4856
4857     if (strcmp(name, "cancel") == 0)
4858     {
4859         XtPopdown(w = XtParent(XtParent(w)));
4860         XtDestroyWidget(w);
4861         filenameUp = False;
4862         ModeHighlight();
4863         return;
4864     }
4865
4866     FileNameAction(w, NULL, NULL, NULL);
4867 }
4868
4869
4870
4871
4872 void
4873 FileNameAction(Widget w, XEvent *event, String *prms, Cardinal *nprms)
4874 {
4875     char buf[MSG_SIZ];
4876     String name;
4877
4878     name = XawDialogGetValueString(w = XtParent(w));
4879
4880     if ((name != NULL) && (*name != NULLCHAR))
4881     {
4882         strcpy(buf, name);
4883         XtPopdown(w = XtParent(w));
4884         XtDestroyWidget(w);
4885         filenameUp = False;
4886         (*fileProc)(buf);  /* I can't see a way not
4887                               to use a global here */
4888         ModeHighlight();
4889         return;
4890     }
4891
4892     XtPopdown(w = XtParent(w));
4893     XtDestroyWidget(w);
4894     filenameUp = False;
4895     ModeHighlight();
4896 }
4897
4898
4899
4900
4901 void
4902 PromotionPopUp(ShogiSquare piece, int to_x, int to_y, int fromRemotePlayer)
4903 {
4904     Arg args[2];
4905     Widget dialog;
4906     Position x, y;
4907     Dimension bw_width, bw_height, pw_width, pw_height;
4908
4909     player = (fromRemotePlayer ? &remotePlayer : &localPlayer);
4910
4911     pmi.piece = piece;
4912     pmi.to_x = to_x;
4913     pmi.to_y = to_y;
4914
4915     XtSetArg(args[0], XtNwidth, &bw_width);
4916     XtSetArg(args[1], XtNheight, &bw_height);
4917     XtGetValues(player->boardWidget, args, 2);
4918
4919     XtSetArg(args[0], XtNresizable, True);
4920
4921     player->promotionShell
4922         = XtCreatePopupShell("Promotion",
4923                              transientShellWidgetClass,
4924                              player->commandsWidget, args, 1);
4925
4926     XtSetArg(args[0], XtNlabel, "Promote piece?");
4927     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4928                                    player->promotionShell, args, 1);
4929
4930     XawDialogAddButton(dialog, "Yes", PromotionCallback,
4931                        (XtPointer) dialog);
4932     XawDialogAddButton(dialog, "No", PromotionCallback,
4933                        (XtPointer) dialog);
4934     XawDialogAddButton(dialog, "cancel", PromotionCallback,
4935                        (XtPointer) dialog);
4936
4937     XtRealizeWidget(player->promotionShell);
4938
4939     XtSetArg(args[0], XtNwidth, &pw_width);
4940     XtSetArg(args[1], XtNheight, &pw_height);
4941     XtGetValues(player->promotionShell, args, 2);
4942
4943     XtTranslateCoords(player->boardWidget,
4944                       ((bw_width - pw_width) / 2),
4945                       (LINE_GAP
4946                        + player->squareSize / 3
4947                        + (((piece == BlackPawn) ^ (player->flipView))
4948                           ? 0
4949                           : (6 * (player->squareSize + LINE_GAP)))),
4950                       &x, &y);
4951
4952     XtSetArg(args[0], XtNx, x);
4953     XtSetArg(args[1], XtNy, y);
4954     XtSetValues(player->promotionShell, args, 2);
4955
4956     XtPopup(player->promotionShell, XtGrabNone);
4957
4958     player->promotionUp = True;
4959 }
4960
4961
4962
4963
4964 void
4965 PromotionCallback(Widget w, XtPointer client_data, XtPointer call_data)
4966 {
4967     String name;
4968     Arg args[1];
4969     ShogiMove move_type;
4970     struct DisplayData *player;
4971
4972     XtSetArg(args[0], XtNlabel, &name);
4973     XtGetValues(w, args, 1);
4974
4975     w = XtParent(XtParent(w));
4976     player = ((w == remotePlayer.promotionShell)
4977               ? &remotePlayer : &localPlayer);
4978     XtPopdown(w);
4979     XtDestroyWidget(w);
4980     player->promotionUp = False;
4981
4982     if (fromX == -1)
4983         return;
4984
4985     if (strcmp(name, "Yes") == 0)
4986     {
4987         if ((int)pmi.piece < (int)WhitePawn)
4988             move_type = BlackPromotion;
4989         else
4990             move_type = WhitePromotion;
4991     }
4992     else if (strcmp(name, "No") == 0)
4993     {
4994         move_type = NormalMove;
4995     }
4996     else /* strcmp(name, "cancel") == 0 */
4997     {
4998         fromX = fromY = -1;
4999         return;
5000     }
5001
5002     MakeMove(&move_type, fromX, fromY, pmi.to_x, pmi.to_y);
5003
5004 #ifdef BLINK_COUNT
5005     if (updateRemotePlayer)
5006     {
5007         BlinkSquare(pmi.to_y, pmi.to_x,
5008                     boards[currentMove][pmi.to_y][pmi.to_x]);
5009     }
5010 #endif
5011
5012     FinishUserMove(move_type, pmi.to_x, pmi.to_y);
5013 }
5014
5015
5016
5017
5018 void
5019 FileModePopUp(char *name)
5020 {
5021     Arg args[2];
5022     Widget dialog;
5023     Position x, y;
5024     Dimension bw_width, bw_height, pw_width, pw_height;
5025
5026     struct DisplayData *player = &localPlayer;
5027
5028     strcpy(fmi.name, name);
5029
5030     XtSetArg(args[0], XtNwidth, &bw_width);
5031     XtSetArg(args[1], XtNheight, &bw_height);
5032     XtGetValues(player->boardWidget, args, 2);
5033
5034     XtSetArg(args[0], XtNresizable, True);
5035     player->filemodeShell
5036         = XtCreatePopupShell("FileMode",
5037                              transientShellWidgetClass,
5038                              player->commandsWidget, args, 1);
5039
5040     XtSetArg(args[0], XtNlabel, "Append to existing file?");
5041     dialog = XtCreateManagedWidget("filemode", dialogWidgetClass,
5042                                    player->filemodeShell, args, 1);
5043
5044     XawDialogAddButton(dialog, "Yes", FileModeCallback,
5045                        (XtPointer) dialog);
5046     XawDialogAddButton(dialog, "No", FileModeCallback,
5047                        (XtPointer) dialog);
5048     XawDialogAddButton(dialog, "cancel", FileModeCallback,
5049                        (XtPointer) dialog);
5050
5051     XtRealizeWidget(player->filemodeShell);
5052
5053     XtSetArg(args[0], XtNwidth, &pw_width);
5054     XtSetArg(args[1], XtNheight, &pw_height);
5055     XtGetValues(player->filemodeShell, args, 2);
5056
5057     XtTranslateCoords(player->boardWidget, (bw_width - pw_width) / 2,
5058                       LINE_GAP + player->squareSize/3 +
5059                       (6*(player->squareSize + LINE_GAP)),
5060                       &x, &y);
5061
5062     XtSetArg(args[0], XtNx, x);
5063     XtSetArg(args[1], XtNy, y);
5064     XtSetValues(player->filemodeShell, args, 2);
5065
5066     XtPopup(player->filemodeShell, XtGrabNone);
5067
5068     filemodeUp = True;
5069 }
5070
5071
5072
5073
5074 void
5075 FileModeCallback(Widget w, XtPointer client_data, XtPointer call_data)
5076 {
5077     String name;
5078     Arg args[1];
5079
5080     XtSetArg(args[0], XtNlabel, &name);
5081     XtGetValues(w, args, 1);
5082
5083     XtPopdown(w = XtParent(XtParent(w)));
5084     XtDestroyWidget(w);
5085
5086     if (strcmp(name, "Yes") == 0)
5087     {
5088         strcpy(fmi.mode, "a");
5089     }
5090     else if (strcmp(name, "No") == 0)
5091     {
5092         strcpy(fmi.mode, "w");
5093     }
5094     else /* strcmp(name, "cancel") == 0 */
5095     {
5096         filemodeUp = False;
5097         return;
5098     }
5099
5100     XtPopdown(localPlayer.filemodeShell);
5101     XtDestroyWidget(localPlayer.filemodeShell);
5102
5103     SaveGame(fmi.name);
5104
5105     filemodeUp = False;
5106 }
5107
5108
5109
5110
5111 void
5112 SelectCommand(Widget w, XtPointer client_data, XtPointer call_data)
5113 {
5114     Cardinal fromRemotePlayer = (Cardinal)client_data;
5115
5116     XawListReturnStruct *list_return = XawListShowCurrent(w);
5117
5118     player = fromRemotePlayer ? &remotePlayer : &localPlayer;
5119
5120     fromX = fromY = -1;
5121
5122     if (player->promotionUp)
5123     {
5124         XtPopdown(player->promotionShell);
5125         XtDestroyWidget(player->promotionShell);
5126         player->promotionUp = False;
5127     }
5128
5129     (*buttonProcs[list_return->list_index])
5130         (w, NULL, NULL, &fromRemotePlayer);
5131
5132     if (!filenameUp)
5133         ModeHighlight();
5134 }
5135
5136
5137
5138
5139 void
5140 HighlightProcButton(XtActionProc proc)
5141 {
5142     int i = 0;
5143
5144     if (proc == NULL)
5145     {
5146         XawListUnhighlight(localPlayer.commandsWidget);
5147
5148         if (updateRemotePlayer)
5149             XawListUnhighlight(remotePlayer.commandsWidget);
5150
5151         return;
5152     }
5153
5154     for (;;)
5155     {
5156         if (buttonProcs[i] == NULL)
5157         {
5158             XawListUnhighlight(localPlayer.commandsWidget);
5159
5160             if (updateRemotePlayer)
5161                 XawListUnhighlight(remotePlayer.commandsWidget);
5162
5163             return;
5164         }
5165
5166         if (buttonProcs[i] == proc)
5167         {
5168             XawListHighlight(localPlayer.commandsWidget, i);
5169
5170             if (updateRemotePlayer)
5171                 XawListHighlight(remotePlayer.commandsWidget, i);
5172
5173             return;
5174         }
5175
5176         i++;
5177     }
5178 }
5179
5180
5181
5182
5183 void
5184 ModeHighlight(void)
5185 {
5186     switch (gameMode)
5187     {
5188     case BeginningOfGame:
5189         if (localPlayer.appData.noShogiProgram)
5190             HighlightProcButton(ForceProc);
5191         else
5192             HighlightProcButton(MachineBlackProc);
5193
5194         break;
5195
5196     case MachinePlaysBlack:
5197         HighlightProcButton(MachineBlackProc);
5198         break;
5199
5200     case MachinePlaysWhite:
5201         HighlightProcButton(MachineWhiteProc);
5202         break;
5203
5204     case TwoMachinesPlay:
5205         HighlightProcButton(TwoMachinesProc);
5206         break;
5207
5208     case ForceMoves:
5209         HighlightProcButton(ForceProc);
5210
5211         break;
5212
5213     case PlayFromGameFile:
5214         HighlightProcButton(LoadGameProc);
5215         break;
5216
5217     case PauseGame:
5218         HighlightProcButton(PauseProc);
5219         break;
5220
5221     case EditPosition:
5222         HighlightProcButton(EditPositionProc);
5223         break;
5224
5225     case EndOfGame:
5226     default:
5227         HighlightProcButton(NULL);
5228         break;
5229     }
5230 }
5231
5232
5233
5234
5235 /*
5236  * Button procedures
5237  */
5238
5239 void
5240 QuitRemotePlayerProc(void)
5241 {
5242     /* This should be modified... */
5243     XCloseDisplay(remotePlayer.xDisplay);
5244     /* XtDestroyWidget(remotePlayer.shellWidget); */
5245     updateRemotePlayer = False;
5246     DisplayMessage("Remote player has pressed Quit", False);
5247     fromX = fromY = -1;
5248 }
5249
5250
5251
5252 void
5253 QuitProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5254 {
5255     if (updateRemotePlayer)
5256         QuitRemotePlayerProc();
5257
5258     ShutdownShogiPrograms("Quitting");
5259     exit(0);
5260 }
5261
5262
5263
5264 void
5265 LoadGameProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5266 {
5267     int fromRemotePlayer = *nprms;
5268
5269     if (fromRemotePlayer)
5270     {
5271         DisplayMessage("only opponent may load game", fromRemotePlayer);
5272         return;
5273     }
5274
5275     if (gameMode != BeginningOfGame)
5276     {
5277         DisplayMessage("Press Reset first.", False);
5278         return;
5279     }
5280
5281     if (localPlayer.appData.loadGameFile == NULL)
5282         FileNamePopUp("Game file name?", LoadGame);
5283     else
5284         (void) LoadGame(localPlayer.appData.loadGameFile);
5285 }
5286
5287
5288
5289
5290 Boolean
5291 LoadGame(char *name)
5292 {
5293     char buf[MSG_SIZ];
5294
5295     loaded_game_finished = 0;
5296
5297     if (gameMode != BeginningOfGame)
5298     {
5299         DisplayMessage("Press Reset first", False);
5300         return (int)False;
5301     }
5302
5303     if (localPlayer.appData.loadGameFile != name)
5304     {
5305         if (localPlayer.appData.loadGameFile)
5306             XtFree(localPlayer.appData.loadGameFile);
5307
5308         localPlayer.appData.loadGameFile = XtMalloc(strlen(name) + 1);
5309         strcpy(localPlayer.appData.loadGameFile, name);
5310     }
5311
5312     if ((gameFileFP = fopen(name, "r")) == NULL)
5313     {
5314         sprintf(buf, "Can't open %s", name);
5315         DisplayMessage(buf, False);
5316         XtFree(localPlayer.appData.loadGameFile);
5317         localPlayer.appData.loadGameFile = NULL;
5318         return (int)False;
5319     }
5320
5321     lastGameMode = gameMode = PlayFromGameFile;
5322     ModeHighlight();
5323     InitPosition(True);
5324     DisplayClocks(StopTimers);
5325
5326     if (firstProgramXID == 0)
5327     {
5328         InitShogiProgram(localPlayer.appData.firstHost,
5329                          localPlayer.appData.firstShogiProgram,
5330                          &firstProgramPID, &toFirstProgFP,
5331                          &fromFirstProgFP, &firstProgramXID,
5332                          &firstSendTime);
5333     }
5334
5335     SendToProgram(localPlayer.appData.initString, toFirstProgFP);
5336     SendSearchDepth(toFirstProgFP);
5337     SendToProgram("force\n", toFirstProgFP);
5338
5339     currentMove = forwardMostMove = backwardMostMove = 0;
5340
5341     ReadGameFile();
5342
5343     return True;
5344 }
5345
5346
5347
5348
5349 /*
5350  * Restart the shogi program and feed it all the moves made so far.
5351  * Used when the user wants to back up from end of game, when gnushogi
5352  * has already exited.  Assumes gameMode == EndOfGame.
5353  */
5354
5355 void
5356 ResurrectShogiProgram(void)
5357 {
5358     char buf[MSG_SIZ];
5359     int i;
5360
5361     if (currentMove > 0)
5362         currentMove--;  /* delete "Black wins" or the like */
5363
5364     InitShogiProgram(localPlayer.appData.firstHost,
5365                      localPlayer.appData.firstShogiProgram,
5366                      &firstProgramPID, &toFirstProgFP, &fromFirstProgFP,
5367                      &firstProgramXID, &firstSendTime);
5368
5369     SendToProgram(localPlayer.appData.initString, toFirstProgFP);
5370     SendSearchDepth(toFirstProgFP);
5371     SendToProgram("force\n", toFirstProgFP);
5372     gameMode = lastGameMode = ForceMoves;
5373     ModeHighlight();
5374
5375     i = (whitePlaysFirst ? 1 : 0);
5376
5377     if (startedFromSetupPosition)
5378         SendBoard(toFirstProgFP, boards[i], catches[i]);
5379
5380     for (; i < currentMove; i++)
5381     {
5382         strcpy(buf, moveList[i]);
5383         SendToProgram(buf, toFirstProgFP);
5384     }
5385
5386     if (!firstSendTime)
5387     {
5388         /* can't tell gnushogi what its clock should read,
5389            so we bow to its notion. */
5390         DisplayClocks(ResetTimers);
5391         timeRemaining[0][currentMove] = blackTimeRemaining;
5392         timeRemaining[1][currentMove] = whiteTimeRemaining;
5393     }
5394 }
5395
5396
5397
5398
5399 void
5400 MachineWhiteProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5401 {
5402     int fromRemotePlayer = *nprms;
5403
5404     if (updateRemotePlayer)
5405     {
5406         DisplayMessage("no machine moves in challenge mode",
5407                        fromRemotePlayer);
5408
5409         return;
5410     }
5411
5412     if (gameMode == PauseGame)
5413         PauseProc(w, event, prms, nprms);
5414
5415     if (gameMode == PlayFromGameFile)
5416         ForceProc(w, event, prms, nprms);
5417
5418     if (gameMode == EditPosition)
5419         EditPositionDone();
5420
5421     if ((gameMode == EndOfGame)
5422         || (gameMode == PlayFromGameFile)
5423         || (gameMode == TwoMachinesPlay)
5424         || localPlayer.appData.noShogiProgram
5425         || (gameMode == MachinePlaysWhite))
5426     {
5427         return;
5428     }
5429
5430     if (BlackOnMove((gameMode == ForceMoves)
5431                     ? currentMove
5432                     : forwardMostMove))
5433     {
5434         DisplayMessage("It is not White's turn.", False);
5435         return;
5436     }
5437
5438     if (gameMode == ForceMoves)
5439         forwardMostMove = currentMove;
5440
5441     lastGameMode = gameMode = MachinePlaysWhite;
5442     ModeHighlight();
5443     SendToProgram(localPlayer.appData.whiteString, toFirstProgFP);
5444     DisplayClocks(StartTimers);
5445 }
5446
5447
5448
5449
5450 void
5451 MachineBlackProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5452 {
5453     int fromRemotePlayer = *nprms;
5454
5455     if (updateRemotePlayer)
5456     {
5457         DisplayMessage("no machine moves in challenge mode",
5458                        fromRemotePlayer);
5459         return;
5460     }
5461
5462     if (gameMode == PauseGame)
5463         PauseProc(w, event, prms, nprms);
5464
5465     if (gameMode == PlayFromGameFile)
5466         ForceProc(w, event, prms, nprms);
5467
5468     if (gameMode == EditPosition)
5469         EditPositionDone();
5470
5471     if ((gameMode == EndOfGame)
5472         || (gameMode == PlayFromGameFile)
5473         || (gameMode == TwoMachinesPlay)
5474         || localPlayer.appData.noShogiProgram
5475         || (gameMode == MachinePlaysBlack))
5476     {
5477         return;
5478     }
5479
5480     if (!BlackOnMove((gameMode == ForceMoves)
5481                      ? currentMove
5482                      : forwardMostMove))
5483     {
5484         DisplayMessage("It is not Black's turn.", False);
5485         return;
5486     }
5487
5488     if (gameMode == ForceMoves)
5489         forwardMostMove = currentMove;
5490
5491     lastGameMode = gameMode = MachinePlaysBlack;
5492     ModeHighlight();
5493     SendToProgram(localPlayer.appData.blackString, toFirstProgFP);
5494     DisplayClocks(StartTimers);
5495 }
5496
5497
5498
5499
5500 void
5501 ForwardProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5502 {
5503     char buf[MSG_SIZ];
5504     int target;
5505     unsigned int state;
5506
5507     int fromRemotePlayer = *nprms;
5508
5509     if (updateRemotePlayer)
5510     {
5511         DisplayMessage("Forward button disabled.", fromRemotePlayer);
5512         return;
5513     }
5514
5515     if ((gameMode == EndOfGame) || (gameMode == EditPosition))
5516         return;
5517
5518     if (gameMode == PlayFromGameFile)
5519         PauseProc(w, event, prms, nprms);
5520
5521     if (currentMove >= forwardMostMove)
5522         return;
5523
5524     if (event == NULL)
5525     {
5526         /* Kludge */
5527         Window root, child;
5528         int root_x, root_y;
5529         int win_x, win_y;
5530         XQueryPointer(localPlayer.xDisplay, localPlayer.xBoardWindow,
5531                       &root, &child, &root_x, &root_y,
5532                       &win_x, &win_y, &state);
5533     }
5534     else
5535     {
5536         state = event->xkey.state;
5537     }
5538
5539     if (state & ShiftMask)
5540         target = forwardMostMove;
5541     else
5542         target = currentMove + 1;
5543
5544     if (gameMode == ForceMoves)
5545     {
5546         while (currentMove < target)
5547         {
5548             strcpy(buf, moveList[currentMove++]);
5549             SendToProgram(buf, toFirstProgFP);
5550         }
5551     }
5552     else
5553     {
5554         currentMove = target;
5555     }
5556
5557     if (gameMode == ForceMoves)
5558     {
5559         blackTimeRemaining = timeRemaining[0][currentMove];
5560         whiteTimeRemaining = timeRemaining[1][currentMove];
5561     }
5562
5563     DisplayClocks(ReDisplayTimers);
5564     DisplayMove(currentMove - 1);
5565     DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
5566 }
5567
5568
5569
5570
5571 void
5572 ResetFileProc(void)
5573 {
5574     char *buf = "";
5575
5576     if (updateRemotePlayer)
5577         return;
5578
5579     if (localPlayer.appData.loadGameFile)
5580         XtFree(localPlayer.appData.loadGameFile);
5581
5582     if (localPlayer.appData.loadPositionFile)
5583         XtFree(localPlayer.appData.loadPositionFile);
5584
5585     localPlayer.appData.loadGameFile
5586         = localPlayer.appData.loadPositionFile = NULL;
5587     DisplayName(buf);
5588
5589     if (gameFileFP != NULL)
5590     {
5591         fclose(gameFileFP);
5592         gameFileFP = NULL;
5593     }
5594 }
5595
5596
5597
5598
5599 void
5600 ResetChallenge(void)
5601 {
5602     char *buf = "";
5603
5604     if (localPlayer.appData.challengeDisplay)
5605         XtFree(localPlayer.appData.challengeDisplay);
5606
5607     localPlayer.appData.challengeDisplay = NULL;
5608     DisplayName(buf);
5609 }
5610
5611
5612
5613
5614 void
5615 ResetProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5616 {
5617     int fromRemotePlayer = *nprms;
5618
5619     if (fromRemotePlayer)
5620     {
5621         DisplayMessage("Only your opponent may reset the game.",
5622                        fromRemotePlayer);
5623         return;
5624     }
5625
5626     Reset(True);
5627 }
5628
5629
5630
5631
5632 void
5633 Reset(int redraw)  /* Boolean */
5634 {
5635     ResetFileProc();
5636     ResetChallenge();
5637
5638     localPlayer.flipView = False;
5639     remotePlayer.flipView = True;
5640     startedFromSetupPosition = whitePlaysFirst = False;
5641     matchMode = MatchFalse;
5642     firstMove = True;
5643     blackFlag = whiteFlag = False;
5644     maybeThinking = False;
5645
5646     endMessage[0] = NULLCHAR;
5647
5648     ShutdownShogiPrograms("");
5649     lastGameMode = gameMode = BeginningOfGame;
5650     ModeHighlight();
5651     InitPosition(redraw);
5652     DisplayClocks(ResetTimers);
5653     timeRemaining[0][0] = blackTimeRemaining;
5654     timeRemaining[1][0] = whiteTimeRemaining;
5655     InitShogiProgram(localPlayer.appData.firstHost,
5656                      localPlayer.appData.firstShogiProgram,
5657                      &firstProgramPID, &toFirstProgFP,
5658                      &fromFirstProgFP, &firstProgramXID,
5659                      &firstSendTime);
5660
5661     if (commentUp)
5662     {
5663         XtPopdown(commentShell);
5664         XtDestroyWidget(commentShell);
5665         commentUp = False;
5666     }
5667
5668     if (localPlayer.promotionUp)
5669     {
5670         XtPopdown(localPlayer.promotionShell);
5671         XtDestroyWidget(localPlayer.promotionShell);
5672         localPlayer.promotionUp = False;
5673     }
5674
5675     if (updateRemotePlayer && remotePlayer.promotionUp)
5676     {
5677         XtPopdown(remotePlayer.promotionShell);
5678         XtDestroyWidget(remotePlayer.promotionShell);
5679         remotePlayer.promotionUp = False;
5680     }
5681 }
5682
5683
5684
5685
5686 void
5687 ClearCatches(int (*catches)[8])
5688 {
5689     int c, p;
5690
5691     for (c = 0; c <= 1; c++)
5692         for (p = 0; p <= 7; p++)
5693             catches[c][p] = 0;
5694 }
5695
5696
5697
5698
5699 Boolean
5700 Challenge(char *name)
5701 {
5702     char buf[MSG_SIZ];
5703     int argc;
5704     char **argv;
5705     XrmDatabase database;
5706
5707     if (gameMode != BeginningOfGame)
5708     {
5709         DisplayMessage("Press Reset first.", False);
5710         return (int)False;
5711     }
5712
5713     if (localPlayer.appData.challengeDisplay != name)
5714     {
5715         if (localPlayer.appData.challengeDisplay)
5716             XtFree(localPlayer.appData.challengeDisplay);
5717
5718         localPlayer.appData.challengeDisplay = XtMalloc(strlen(name) + 1);
5719         strcpy(localPlayer.appData.challengeDisplay, name);
5720     }
5721
5722     sprintf(buf, "trying to connect to %s.....", name);
5723     DisplayMessage(buf, False);
5724
5725     argc = global_argc;
5726     argv = global_argv;
5727
5728     if ((remotePlayer.xDisplay
5729          = XtOpenDisplay(appContext, name, "XShogi",
5730                          "XShogi", 0, 0, &argc, argv)) == NULL)
5731     {
5732         sprintf(buf, "Can't open display %s", name);
5733         DisplayMessage(buf, False);
5734         XtFree(localPlayer.appData.challengeDisplay);
5735         localPlayer.appData.challengeDisplay = NULL;
5736         return (int)False;
5737     }
5738
5739     DisplayMessage("connected! creating remote window...", False);
5740
5741     remotePlayer.xScreen = DefaultScreen(remotePlayer.xDisplay);
5742
5743     remotePlayer.shellWidget
5744         = XtAppCreateShell(NULL, "XShogi",
5745                            applicationShellWidgetClass,
5746                            remotePlayer.xDisplay, NULL, 0);
5747
5748     database = XtDatabase(remotePlayer.xDisplay);
5749
5750     XrmParseCommand(&database,
5751                     shellOptions, XtNumber(shellOptions),
5752                     "XShogi", &argc, argv);
5753
5754     XtGetApplicationResources(remotePlayer.shellWidget,
5755                               &remotePlayer.appData, clientResources,
5756                               XtNumber(clientResources), NULL, 0);
5757
5758     player = &remotePlayer;
5759
5760     CreatePlayerWindow();
5761
5762     updateRemotePlayer = True;
5763
5764     DisplayName("REMOTE");
5765     DrawPosition(remotePlayer.boardWidget, NULL, NULL, NULL);
5766     DisplayClocks(ReDisplayTimers);
5767
5768     DisplayMessage("ready to play", False);
5769     DisplayMessage("ready to play", True);
5770
5771     return True;
5772 }
5773
5774
5775
5776
5777 void
5778 ChallengeProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5779 {
5780     int fromRemotePlayer = *nprms;
5781
5782     if (updateRemotePlayer)
5783     {
5784         DisplayMessage("You are already in challenge mode.",
5785                        fromRemotePlayer);
5786         return;
5787     }
5788
5789     if (gameMode != BeginningOfGame)
5790     {
5791         DisplayMessage("Press Reset first.", False);
5792         return;
5793     }
5794
5795     if (localPlayer.appData.challengeDisplay == NULL)
5796         FileNamePopUp("Challenge display?", Challenge);
5797     else
5798         (void) Challenge(localPlayer.appData.challengeDisplay);
5799 }
5800
5801
5802
5803
5804 Boolean
5805 SelectLevel(char *command)
5806 {
5807     char buf[MSG_SIZ];
5808
5809     sprintf(buf, "level %s\n", command);
5810     SendToProgram(buf, toFirstProgFP);
5811
5812     return True;
5813 }
5814
5815
5816
5817
5818 void
5819 SelectLevelProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5820 {
5821     if ((BlackOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack))
5822         || (!BlackOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite)))
5823     {
5824         DisplayMessage("Wait until your turn.", False);
5825     }
5826     else
5827     {
5828         FileNamePopUp("#moves #minutes", SelectLevel);
5829     }
5830 }
5831
5832
5833
5834
5835 void
5836 MoveNowProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5837 {
5838     if ((!BlackOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack))
5839         || (BlackOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite)))
5840     {
5841         DisplayMessage("Wait until machine's turn.", False);
5842     }
5843     else
5844     {
5845         Attention(firstProgramPID);
5846     }
5847 }
5848
5849
5850
5851
5852 void
5853 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
5854 {
5855     int fromRemotePlayer = *nprms;
5856
5857     if (fromRemotePlayer)
5858     {
5859         DisplayMessage("only opponent may load position", fromRemotePlayer);
5860         return;
5861     }
5862
5863     if (gameMode != BeginningOfGame)
5864     {
5865         DisplayMessage("Press Reset first.", False);
5866         return;
5867     }
5868
5869     FileNamePopUp("Position file name?", LoadPosition);
5870 }
5871
5872
5873
5874
5875 Boolean
5876 LoadPosition(char *name)
5877 {
5878     char *p, line[MSG_SIZ], buf[MSG_SIZ];
5879     Board initial_position;
5880     Catched initial_catches;
5881     FILE *fp;
5882     int i, j;
5883
5884     if (gameMode != BeginningOfGame)
5885     {
5886         DisplayMessage("Press Reset first.", False);
5887         return False;
5888     }
5889
5890     if (localPlayer.appData.loadPositionFile != name)
5891     {
5892         if (localPlayer.appData.loadPositionFile)
5893             XtFree(localPlayer.appData.loadPositionFile);
5894
5895         localPlayer.appData.loadPositionFile = XtMalloc(strlen(name) + 1);
5896         strcpy(localPlayer.appData.loadPositionFile, name);
5897     }
5898
5899     if ((fp = fopen(name, "r")) == NULL)
5900     {
5901         sprintf(buf, "Can't open %s", name);
5902         DisplayMessage(buf, False);
5903         XtFree(localPlayer.appData.loadPositionFile);
5904         localPlayer.appData.loadPositionFile = NULL;
5905         return False;
5906     }
5907
5908     lastGameMode = gameMode = ForceMoves;
5909     ModeHighlight();
5910     startedFromSetupPosition = True;
5911
5912     if (firstProgramXID == 0)
5913     {
5914         InitShogiProgram(localPlayer.appData.firstHost,
5915                          localPlayer.appData.firstShogiProgram,
5916                          &firstProgramPID, &toFirstProgFP,
5917                          &fromFirstProgFP, &firstProgramXID,
5918                          &firstSendTime);
5919     }
5920
5921     /*
5922      * Check and skip header information in position file.
5923      */
5924
5925     fgets(line, MSG_SIZ, fp);
5926     line[strlen(line) - 1] = NULLCHAR;
5927     sprintf(buf, "# %s position file", programName);
5928
5929     if (strncmp(line, buf, strlen(buf)))
5930     {
5931         strcat(line, ": no xshogi position file");
5932         DisplayMessage(line, False);
5933         return False;
5934     }
5935
5936     DisplayName(line);
5937     fgets(line, MSG_SIZ, fp); /* skip opponents */
5938
5939     for (i = BOARD_SIZE - 1; i >= 0; i--)
5940     {
5941         fgets(line, MSG_SIZ, fp);
5942
5943         for (p = line, j = 0; j < BOARD_SIZE; p++)
5944         {
5945             int promoted = False;  /* CHECKME: is this valid? */
5946
5947             if (*p == '+')
5948                 promoted = True;
5949
5950             if (*p == ' ')
5951                 promoted = False;
5952
5953             p++;
5954             initial_position[i][j++] = CharToPiece(*p, promoted);
5955         }
5956     }
5957
5958     {
5959         int color;
5960
5961         for (color = 0; color <= 1; color++)
5962         {
5963             fscanf(fp, "%i%i%i%i%i%i%i%i\n",
5964                    &initial_catches[color][pawn],
5965                    &initial_catches[color][lance],
5966                    &initial_catches[color][knight],
5967                    &initial_catches[color][silver],
5968                    &initial_catches[color][gold],
5969                    &initial_catches[color][bishop],
5970                    &initial_catches[color][rook],
5971                    &initial_catches[color][king]);
5972         }
5973     }
5974
5975     whitePlaysFirst = False;
5976
5977     if (!feof(fp))
5978     {
5979         fgets(line, MSG_SIZ, fp);
5980
5981         if (strncmp(line, "white", strlen("white")) == 0)
5982             whitePlaysFirst = True;
5983     }
5984
5985     fclose(fp);
5986
5987     if (whitePlaysFirst)
5988     {
5989         CopyBoard(boards[0], initial_position);
5990         CopyCatches(catches[0], initial_catches);
5991         strcpy(moveList[0], " ...\n");
5992         strcpy(parseList[0], " ...\n");
5993         currentMove = forwardMostMove = backwardMostMove = 1;
5994         CopyBoard(boards[1], initial_position);
5995         CopyCatches(catches[1], initial_catches);
5996         SendToProgram("white\n", toFirstProgFP);
5997         SendToProgram("force\n", toFirstProgFP);
5998         SendCurrentBoard(toFirstProgFP);
5999         DisplayMessage("White to play", False);
6000     }
6001     else
6002     {
6003         currentMove = forwardMostMove = backwardMostMove = 0;
6004         CopyBoard(boards[0], initial_position);
6005         CopyCatches(catches[0], initial_catches);
6006         SendCurrentBoard(toFirstProgFP);
6007         SendToProgram("force\n", toFirstProgFP);
6008         DisplayMessage("Black to play", False);
6009     }
6010
6011     DisplayClocks(ResetTimers);
6012     timeRemaining[0][1] = blackTimeRemaining;
6013     timeRemaining[1][1] = whiteTimeRemaining;
6014
6015     DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
6016     return True;
6017 }
6018
6019
6020
6021
6022 void
6023 EditPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6024 {
6025     int fromRemotePlayer = *nprms;
6026
6027     if (updateRemotePlayer)
6028     {
6029         DisplayMessage("Edit button disabled", fromRemotePlayer);
6030         return;
6031     }
6032
6033     if (gameMode == EditPosition)
6034         return;
6035
6036     ForceProc(w, event, prms, nprms);
6037
6038     if (gameMode != ForceMoves)
6039         return;
6040
6041     DisplayName("<-- Press to set side to play next");
6042     DisplayMessage("Mouse: 1=drag, 2=black, 3=white", False);
6043
6044     lastGameMode = gameMode = EditPosition;
6045     ModeHighlight();
6046
6047     if (currentMove > 0)
6048         CopyBoard(boards[0], boards[currentMove]);
6049
6050     whitePlaysFirst = !BlackOnMove(forwardMostMove);
6051     currentMove = forwardMostMove = backwardMostMove = 0;
6052 }
6053
6054
6055
6056
6057 void
6058 EditPositionDone(void)
6059 {
6060     startedFromSetupPosition = True;
6061     SendToProgram(localPlayer.appData.initString, toFirstProgFP);
6062     SendSearchDepth(toFirstProgFP);
6063
6064     if (whitePlaysFirst)
6065     {
6066         strcpy(moveList[0], " ...\n");
6067         strcpy(parseList[0], " ...\n");
6068         currentMove = forwardMostMove = backwardMostMove = 1;
6069         CopyBoard(boards[1], boards[0]);
6070         CopyCatches(catches[1], catches[0]);
6071         SendToProgram("force\n", toFirstProgFP);
6072         SendCurrentBoard(toFirstProgFP);
6073         DisplayName(" ");
6074         DisplayMessage("White to play", False);
6075     }
6076     else
6077     {
6078         currentMove = forwardMostMove = backwardMostMove = 0;
6079         SendCurrentBoard(toFirstProgFP);
6080         SendToProgram("force\n", toFirstProgFP);
6081         DisplayName(" ");
6082         DisplayMessage("Black to play", False);
6083     }
6084
6085     lastGameMode = gameMode = ForceMoves;
6086 }
6087
6088
6089
6090 /*
6091  * FUNCTION
6092  *     BackwardProc
6093  *
6094  * DESCRIPTION
6095  *     This function executes when undoing a move.
6096  *     FIXME: this function is totally hosed!!!
6097  *
6098  * ARGUMENTS
6099  *     Widget w
6100  *     XEvent *event
6101  *     String *prms
6102  *     Cardinal *nprms
6103  *
6104  * RETURN VALUE
6105  *     void
6106  *
6107  */
6108
6109 void
6110 BackwardProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6111 {
6112     int target;
6113     unsigned int state;
6114
6115     int fromRemotePlayer = *nprms;
6116
6117     if (updateRemotePlayer)
6118     {
6119         DisplayMessage("Backward button disabled", fromRemotePlayer);
6120         return;
6121     }
6122
6123     /*
6124      * Why do we need this here?
6125      */
6126
6127     ForceProc(w, event, prms, nprms);
6128
6129     if ((currentMove <= backwardMostMove) || (gameMode == EditPosition))
6130         return;
6131
6132     if (gameMode == EndOfGame)
6133         ResurrectShogiProgram();
6134
6135     if (gameMode == PlayFromGameFile)
6136         PauseProc(w, event, prms, nprms);
6137
6138     if (event == NULL)
6139     {
6140         /* Kludge */
6141         Window root, child;
6142         int root_x, root_y;
6143         int win_x, win_y;
6144
6145         XQueryPointer(localPlayer.xDisplay, localPlayer.xBoardWindow,
6146                       &root, &child, &root_x, &root_y,
6147                       &win_x, &win_y, &state);
6148     }
6149     else
6150     {
6151         state = event->xkey.state;
6152     }
6153
6154     if (state & ShiftMask)
6155     {
6156         target = backwardMostMove;
6157     }
6158     else
6159     {
6160         target = currentMove - 1;
6161     }
6162
6163     if (gameMode == ForceMoves)
6164     {
6165         Attention(firstProgramPID);
6166
6167         while (currentMove > target)
6168         {
6169             SendToProgram("undo\n", toFirstProgFP);
6170             currentMove--;
6171         }
6172     }
6173     else
6174     {
6175         currentMove = target;
6176     }
6177
6178     if (gameMode == ForceMoves)
6179     {
6180         whiteTimeRemaining = timeRemaining[0][currentMove];
6181         blackTimeRemaining = timeRemaining[1][currentMove];
6182     }
6183
6184     DisplayClocks(ReDisplayTimers);
6185     DisplayMove(currentMove - 1);
6186     DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
6187 }
6188
6189
6190
6191
6192 void
6193 FlipViewProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6194 {
6195     struct DisplayData *player = (*nprms ? &remotePlayer : &localPlayer);
6196
6197     player->flipView = !player->flipView;
6198     DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
6199 }
6200
6201
6202
6203
6204 void
6205 SaveGameProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6206 {
6207     char def[MSG_SIZ];
6208
6209     int fromRemotePlayer = *nprms;
6210
6211     if (fromRemotePlayer)
6212     {
6213         DisplayMessage("only opponent may save game", fromRemotePlayer);
6214         return;
6215     }
6216
6217     def[0] = NULLCHAR;
6218
6219     FileNamePopUp("Filename for saved game?", SaveGame);
6220 }
6221
6222
6223
6224
6225 Boolean
6226 SaveGame(char *name)
6227 {
6228     char buf[MSG_SIZ];
6229     int i, len, move = 0;
6230     time_t tm;
6231
6232     if (!filemodeUp) /* if called via FileModeCallback avoid recursion */
6233     {
6234         if ((gameFileFP = fopen(name, "r")) == NULL)
6235         {
6236             strcpy(fmi.mode, "w");
6237         }
6238         else
6239         {
6240             fclose(gameFileFP);
6241             FileModePopUp(name);
6242             return False; /* CHECKME: what should the return value be? */
6243         }
6244     }
6245
6246     if ((gameFileFP = fopen(name, fmi.mode)) == NULL)
6247     {
6248         sprintf(buf, "Can't open %s (mode %s)", name, fmi.mode);
6249         DisplayMessage(buf, False);
6250         return False;
6251     }
6252
6253     tm = time((time_t *) NULL);
6254
6255     fprintf(gameFileFP, "# %s game file -- %s", programName, ctime(&tm));
6256     PrintOpponents(gameFileFP);
6257
6258     for (i = 0; i < currentMove;)
6259     {
6260         if ((i % 5) == 0)
6261             fprintf(gameFileFP, "\n");
6262
6263         fprintf(gameFileFP, "%d. %s ", ++move, parseList[i++]);
6264
6265         if (i >= currentMove)
6266         {
6267             fprintf(gameFileFP, "\n");
6268             break;
6269         }
6270
6271         if ((len = strlen(parseList[i])) == 0)
6272             break;
6273
6274         fprintf(gameFileFP, "%s ", parseList[i++]);
6275     }
6276
6277     fprintf(gameFileFP, "\n");
6278
6279     fclose(gameFileFP);
6280     gameFileFP = NULL;
6281
6282     return True;
6283 }
6284
6285
6286
6287
6288 void
6289 SwitchProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6290 {
6291     if (localPlayer.appData.noShogiProgram)
6292         return;
6293
6294     switch (gameMode)
6295     {
6296     default:
6297         return;
6298
6299     case MachinePlaysBlack:
6300         if (BlackOnMove(forwardMostMove))
6301         {
6302             DisplayMessage("Wait until your turn", False);
6303             return;
6304         }
6305
6306         lastGameMode = gameMode = MachinePlaysWhite;
6307         ModeHighlight();
6308         break;
6309
6310     case BeginningOfGame:
6311
6312     case MachinePlaysWhite:
6313         if (!BlackOnMove(forwardMostMove))
6314         {
6315             DisplayMessage("Wait until your turn", False);
6316             return;
6317         }
6318
6319         if (forwardMostMove == 0)
6320         {
6321             MachineBlackProc(w, event, prms, nprms);
6322             return;
6323         }
6324
6325         lastGameMode = gameMode = MachinePlaysBlack;
6326         ModeHighlight();
6327         break;
6328     }
6329
6330     Attention(firstProgramPID);
6331     SendToProgram("switch\n", toFirstProgFP);
6332 }
6333
6334
6335
6336
6337 void
6338 ForceProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6339 {
6340     int i;
6341
6342     switch (gameMode)
6343     {
6344     case MachinePlaysBlack:
6345         if (BlackOnMove(forwardMostMove))
6346         {
6347             DisplayMessage("Wait until your turn", False);
6348             return;
6349         }
6350
6351         Attention(firstProgramPID);
6352         SendToProgram("force\n", toFirstProgFP);
6353         break;
6354
6355     case MachinePlaysWhite:
6356         if (!BlackOnMove(forwardMostMove))
6357         {
6358             DisplayMessage("Wait until your turn", False);
6359             return;
6360         }
6361
6362         Attention(firstProgramPID);
6363         SendToProgram("force\n", toFirstProgFP);
6364         break;
6365
6366     case BeginningOfGame:
6367         SendToProgram("force\n", toFirstProgFP);
6368         break;
6369
6370     case PlayFromGameFile:
6371         if (readGameXID != 0)
6372         {
6373             XtRemoveTimeOut(readGameXID);
6374             readGameXID = 0;
6375         }
6376
6377         if (gameFileFP != NULL)
6378         {
6379             fclose(gameFileFP);
6380             gameFileFP = NULL;
6381         }
6382
6383         break;
6384
6385     case EndOfGame:
6386         ResurrectShogiProgram();
6387         break;
6388
6389     case EditPosition:
6390         EditPositionDone();
6391         break;
6392
6393     case TwoMachinesPlay:
6394         ShutdownShogiPrograms("");
6395         ResurrectShogiProgram();
6396         return;
6397
6398     default:
6399         return;
6400     }
6401
6402     if ((gameMode == MachinePlaysWhite)
6403         || (gameMode == MachinePlaysBlack)
6404         || (gameMode == TwoMachinesPlay)
6405         || (gameMode == PlayFromGameFile))
6406     {
6407         i = forwardMostMove;
6408
6409         while (i > currentMove)
6410         {
6411             SendToProgram("undo\n", toFirstProgFP);
6412             i--;
6413         }
6414
6415         blackTimeRemaining = timeRemaining[0][currentMove];
6416         whiteTimeRemaining = timeRemaining[1][currentMove];
6417
6418         if (whiteFlag || blackFlag)
6419         {
6420             whiteFlag = blackFlag = 0;
6421         }
6422
6423         DisplayTitle("");
6424     }
6425
6426     lastGameMode = gameMode = ForceMoves;
6427     ModeHighlight();
6428     DisplayClocks(StopTimers);
6429 }
6430
6431
6432
6433 void
6434 HintProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6435 {
6436     int fromRemotePlayer = *nprms;
6437
6438     if (updateRemotePlayer)
6439     {
6440         DisplayMessage("no hints in challenge mode", fromRemotePlayer);
6441         return;
6442     }
6443
6444     if (localPlayer.appData.noShogiProgram)
6445         return;
6446
6447     switch (gameMode)
6448     {
6449     case MachinePlaysBlack:
6450         if (BlackOnMove(forwardMostMove))
6451         {
6452             DisplayMessage("Wait until your turn", False);
6453             return;
6454         }
6455
6456         break;
6457
6458     case BeginningOfGame:
6459     case MachinePlaysWhite:
6460         if (!BlackOnMove(forwardMostMove))
6461         {
6462             DisplayMessage("Wait until your turn", False);
6463             return;
6464         }
6465
6466         break;
6467
6468     default:
6469         DisplayMessage("No hint available", False);
6470         return;
6471     }
6472
6473     Attention(firstProgramPID);
6474     SendToProgram("hint\n", toFirstProgFP);
6475 }
6476
6477
6478
6479
6480 void
6481 PrintPosition(FILE *fp, int move)
6482 {
6483     int i, j, color;
6484
6485     for (i = BOARD_SIZE - 1; i >= 0; i--)
6486     {
6487         for (j = 0; j < BOARD_SIZE; j++)
6488         {
6489             if (pieceIsPromoted[(int)boards[currentMove][i][j]])
6490                 fprintf(fp, "%c", '+');
6491             else
6492                 fprintf(fp, "%c", ' ');
6493
6494             fprintf(fp, "%c",
6495                     pieceToChar[(int)boards[currentMove][i][j]]);
6496
6497             if (j == BOARD_SIZE - 1)
6498                 fputc('\n', fp);
6499         }
6500     }
6501
6502     for (color = 0; color <= 1; color++)
6503     {
6504         fprintf(fp, "%i %i %i %i %i %i %i %i\n",
6505                 catches[currentMove][color][pawn],
6506                 catches[currentMove][color][lance],
6507                 catches[currentMove][color][knight],
6508                 catches[currentMove][color][silver],
6509                 catches[currentMove][color][gold],
6510                 catches[currentMove][color][bishop],
6511                 catches[currentMove][color][rook],
6512                 catches[currentMove][color][king]);
6513     }
6514
6515     if ((gameMode == EditPosition)
6516         ? !whitePlaysFirst
6517         : BlackOnMove(forwardMostMove))
6518     {
6519         fprintf(fp, "black to play\n");
6520     }
6521     else
6522     {
6523         fprintf(fp, "white to play\n");
6524     }
6525 }
6526
6527
6528
6529
6530 void
6531 PrintOpponents(FILE *fp)
6532 {
6533     char host_name[MSG_SIZ];
6534
6535 #ifdef HAVE_GETHOSTNAME
6536     gethostname(host_name, MSG_SIZ);
6537 #else
6538     strncpy(host_name, "hostname not available", MSG_SIZ);
6539 #endif
6540
6541     switch (lastGameMode)
6542     {
6543     case MachinePlaysWhite:
6544         fprintf(fp, "# %s@%s vs. %s@%s\n",
6545                 localPlayer.appData.firstShogiProgram,
6546                 localPlayer.appData.firstHost,
6547                 getpwuid(getuid())->pw_name,
6548                 host_name);
6549         break;
6550
6551     case MachinePlaysBlack:
6552         fprintf(fp, "# %s@%s vs. %s@%s\n",
6553                 getpwuid(getuid())->pw_name,
6554                 host_name,
6555                 localPlayer.appData.firstShogiProgram,
6556                 localPlayer.appData.firstHost);
6557         break;
6558
6559     case TwoMachinesPlay:
6560         fprintf(fp, "# %s@%s vs. %s@%s\n",
6561                 localPlayer.appData.secondShogiProgram,
6562                 localPlayer.appData.secondHost,
6563                 localPlayer.appData.firstShogiProgram,
6564                 localPlayer.appData.firstHost);
6565         break;
6566
6567     default:
6568         fprintf(fp, "#\n");
6569         break;
6570     }
6571 }
6572
6573
6574
6575
6576 void
6577 SavePositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6578 {
6579     char def[MSG_SIZ];
6580
6581     int fromRemotePlayer = *nprms;
6582
6583     if (fromRemotePlayer)
6584     {
6585         DisplayMessage("only opponent may save game", fromRemotePlayer);
6586         return;
6587     }
6588
6589     def[0] = NULLCHAR;
6590
6591     FileNamePopUp("Filename for saved position?", SavePosition);
6592 }
6593
6594
6595
6596
6597 Boolean
6598 SavePosition(char *name)
6599 {
6600     char buf[MSG_SIZ];
6601     FILE *fp;
6602     time_t tm;
6603
6604     if ((fp = fopen(name, "w")) == NULL)
6605     {
6606         sprintf(buf, "Can't open %s", name);
6607         DisplayMessage(buf, False);
6608         return False;
6609     }
6610
6611     tm = time((time_t *) NULL);
6612
6613     fprintf(fp, "# %s position file -- %s", programName, ctime(&tm));
6614     PrintOpponents(fp);
6615     PrintPosition(fp, currentMove);
6616     fclose(fp);
6617
6618     return True;
6619 }
6620
6621
6622
6623
6624 void
6625 TwoMachinesProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6626 {
6627     int i;
6628     MatchMode matchKind;
6629
6630     int fromRemotePlayer = *nprms;
6631
6632     if (updateRemotePlayer)
6633     {
6634         DisplayMessage("no machine moves in challenge mode",
6635                        fromRemotePlayer);
6636         return;
6637     }
6638
6639     if (gameMode == PauseGame)
6640         PauseProc(w, event, prms, nprms);
6641
6642     if (gameMode == PlayFromGameFile)
6643         ForceProc(w, event, prms, nprms);
6644
6645     if ((gameMode == EndOfGame)
6646         || (gameMode == TwoMachinesPlay)
6647         || localPlayer.appData.noShogiProgram)
6648     {
6649         return;
6650     }
6651
6652     if (matchMode == MatchFalse)
6653     {
6654         switch (gameMode)
6655         {
6656         case PauseGame:
6657         case PlayFromGameFile:
6658             return;
6659
6660         case MachinePlaysBlack:
6661         case MachinePlaysWhite:
6662             ForceProc(w, event, prms, nprms);
6663
6664             if (gameMode != ForceMoves)
6665                 return;
6666
6667             matchKind = MatchOpening;
6668             break;
6669
6670         case ForceMoves:
6671             matchKind = MatchOpening;
6672             break;
6673
6674         case EditPosition:
6675             EditPositionDone();
6676             matchKind = MatchPosition;
6677             break;
6678
6679         case BeginningOfGame:
6680         default:
6681             matchKind = MatchInit;
6682             break;
6683         }
6684     }
6685     else
6686     {
6687         matchKind = matchMode;
6688     }
6689
6690     forwardMostMove = currentMove;
6691
6692     localPlayer.flipView = False;
6693     remotePlayer.flipView = True;
6694     firstMove = False;
6695     DisplayClocks(ResetTimers);
6696     DisplayClocks(StartTimers);
6697
6698     switch (matchKind)
6699     {
6700     case MatchOpening:
6701         if (firstProgramXID == 0)
6702         {
6703             if (localPlayer.appData.loadGameFile == NULL)
6704             {
6705                 DisplayMessage("Select game file first", False);
6706                 return;
6707             }
6708
6709             InitShogiProgram(localPlayer.appData.firstHost,
6710                              localPlayer.appData.firstShogiProgram,
6711                              &firstProgramPID, &toFirstProgFP,
6712                              &fromFirstProgFP, &firstProgramXID,
6713                              &firstSendTime);
6714
6715             if (!LoadGame(localPlayer.appData.loadGameFile))
6716             {
6717                 ShutdownShogiPrograms("Bad game file");
6718                 return;
6719             }
6720
6721             DrawPosition(localPlayer.boardWidget, NULL, NULL, NULL);
6722         }
6723
6724         InitShogiProgram(localPlayer.appData.secondHost,
6725                          localPlayer.appData.secondShogiProgram,
6726                          &secondProgramPID, &toSecondProgFP,
6727                          &fromSecondProgFP, &secondProgramXID,
6728                          &secondSendTime);
6729
6730         if (startedFromSetupPosition)
6731         {
6732             if (whitePlaysFirst)
6733             {
6734                 i = 1;
6735                 SendToProgram("force\n", toSecondProgFP);
6736                 SendBoard(toSecondProgFP, boards[i], catches[i]);
6737             }
6738             else
6739             {
6740                 i = 0;
6741                 SendBoard(toSecondProgFP, boards[i], catches[i]);
6742                 SendToProgram("force\n", toSecondProgFP);
6743             }
6744         }
6745         else
6746         {
6747             i = 0;
6748             SendToProgram("force\n", toSecondProgFP);
6749         }
6750
6751         for (i = backwardMostMove; i < forwardMostMove; i++)
6752             SendToProgram(moveList[i], toSecondProgFP);
6753
6754         lastGameMode = gameMode = TwoMachinesPlay;
6755         ModeHighlight();
6756         firstMove = True;
6757
6758         if (BlackOnMove(forwardMostMove))
6759             SendToProgram(localPlayer.appData.blackString, toSecondProgFP);
6760         else
6761             SendToProgram(localPlayer.appData.whiteString, toFirstProgFP);
6762
6763         break;
6764
6765     case MatchPosition:
6766         if (firstProgramXID == 0)
6767         {
6768             if (localPlayer.appData.loadPositionFile == NULL)
6769             {
6770                 DisplayMessage("Select position file first", False);
6771                 return;
6772             }
6773
6774             InitShogiProgram(localPlayer.appData.firstHost,
6775                              localPlayer.appData.firstShogiProgram,
6776                              &firstProgramPID, &toFirstProgFP,
6777                              &fromFirstProgFP, &firstProgramXID,
6778                              &firstSendTime);
6779
6780             if (!LoadPosition(localPlayer.appData.loadPositionFile))
6781                 return;
6782         }
6783
6784         InitShogiProgram(localPlayer.appData.secondHost,
6785                          localPlayer.appData.secondShogiProgram,
6786                          &secondProgramPID, &toSecondProgFP,
6787                          &fromSecondProgFP, &secondProgramXID,
6788                          &secondSendTime);
6789
6790         if (whitePlaysFirst)
6791             SendToProgram("force\n", toSecondProgFP);
6792
6793         SendCurrentBoard(toSecondProgFP);
6794         lastGameMode = gameMode = TwoMachinesPlay;
6795         ModeHighlight();
6796         firstMove = True;
6797
6798         if (BlackOnMove(forwardMostMove))
6799             SendToProgram(localPlayer.appData.blackString, toSecondProgFP);
6800         else
6801             SendToProgram(localPlayer.appData.whiteString, toFirstProgFP);
6802
6803         break;
6804
6805     case MatchInit:
6806         InitPosition(True);
6807
6808         if (firstProgramXID == 0)
6809         {
6810             InitShogiProgram(localPlayer.appData.firstHost,
6811                              localPlayer.appData.firstShogiProgram,
6812                              &firstProgramPID, &toFirstProgFP,
6813                              &fromFirstProgFP, &firstProgramXID,
6814                              &firstSendTime);
6815         }
6816
6817         InitShogiProgram(localPlayer.appData.secondHost,
6818                          localPlayer.appData.secondShogiProgram,
6819                          &secondProgramPID, &toSecondProgFP,
6820                          &fromSecondProgFP, &secondProgramXID,
6821                          &secondSendTime);
6822
6823         lastGameMode = gameMode = TwoMachinesPlay;
6824         ModeHighlight();
6825         SendToProgram(localPlayer.appData.blackString, toSecondProgFP);
6826
6827     default:
6828         break;
6829     }
6830
6831     if (!firstSendTime || !secondSendTime)
6832     {
6833         DisplayClocks(ResetTimers);
6834         timeRemaining[0][forwardMostMove] = blackTimeRemaining;
6835         timeRemaining[1][forwardMostMove] = whiteTimeRemaining;
6836     }
6837
6838     DisplayClocks(StartTimers);
6839 }
6840
6841
6842
6843
6844 void
6845 PauseProc(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6846 {
6847     static GameMode previous_mode = PauseGame;
6848
6849     switch (gameMode)
6850     {
6851     case ForceMoves:
6852     case EndOfGame:
6853     case EditPosition:
6854     default:
6855         return;
6856
6857     case PauseGame:
6858         gameMode = previous_mode;
6859         ModeHighlight();
6860         previous_mode = PauseGame;
6861         DisplayClocks(StartTimers);
6862         DisplayMessage("", False);
6863
6864         if (updateRemotePlayer)
6865             DisplayMessage("", True);
6866         break;
6867
6868     case PlayFromGameFile:
6869         if (readGameXID == 0)
6870         {
6871             readGameXID =
6872                 XtAppAddTimeOut(appContext,
6873                                 (int)(1000 * localPlayer.appData.timeDelay),
6874                                 (XtTimerCallbackProc) ReadGameFile, NULL);
6875         }
6876         else
6877         {
6878             XtRemoveTimeOut(readGameXID);
6879             readGameXID = 0;
6880         }
6881
6882         DisplayMessage("Pausing", False);
6883
6884         if (updateRemotePlayer)
6885             DisplayMessage("Pausing", True);
6886
6887         break;
6888
6889     case BeginningOfGame:
6890     case MachinePlaysBlack:
6891     case MachinePlaysWhite:
6892     case TwoMachinesPlay:
6893         if (forwardMostMove == 0)   /* Don't pause if no one has moved. */
6894             return;
6895
6896         if (((gameMode == MachinePlaysWhite)
6897              && !BlackOnMove(forwardMostMove))
6898             || ((gameMode == MachinePlaysBlack) &&
6899                 BlackOnMove(forwardMostMove)))
6900         {
6901             DisplayClocks(StopTimers);
6902         }
6903
6904         previous_mode = gameMode;
6905         gameMode = PauseGame;
6906         ModeHighlight();
6907         DisplayClocks(StopTimers);
6908         DisplayMessage("Pausing", False);
6909
6910         if (updateRemotePlayer)
6911             DisplayMessage("Pausing", True);
6912
6913         break;
6914     }
6915 }
6916
6917
6918
6919
6920 void
6921 Iconify(Widget w, XEvent *event, String *prms, Cardinal *nprms)
6922 {
6923     Arg args[1];
6924
6925     fromX = fromY = -1;
6926
6927     XtSetArg(args[0], XtNiconic, True);
6928     XtSetValues(localPlayer.shellWidget, args, 1);
6929 }
6930
6931
6932
6933
6934 void
6935 SendToProgram(char *message, FILE *fp)
6936 {
6937     if (fp == NULL)
6938         return;
6939
6940     lastMsgFP = fp;
6941
6942     if (xshogiDebug)
6943     {
6944         fprintf(stderr, "Sending to %s: %s\n",
6945                 ((fp == toFirstProgFP) ? "first" : "second"), message);
6946     }
6947
6948     if (message[strlen(message) - 1] != '\n')
6949         fprintf(fp, "\n%s\n", message);
6950     else
6951         fputs(message, fp);
6952
6953     fflush(fp);
6954 }
6955
6956
6957
6958
6959 void
6960 ReceiveFromProgram(FILE *fp, int *source, XtInputId *id)
6961 {
6962     char message[MSG_SIZ], *end_str, *number, *name;
6963
6964     if (fgets(message, MSG_SIZ, fp) == NULL)
6965     {
6966         if (fp == fromFirstProgFP)
6967         {
6968             number = "first";
6969             name = localPlayer.appData.firstShogiProgram;
6970         }
6971         else if (fp == fromSecondProgFP)
6972         {
6973             number = "second";
6974             name = localPlayer.appData.secondShogiProgram;
6975         }
6976         else
6977         {
6978             return;
6979         }
6980
6981         if (ferror(fp) == 0)
6982         {
6983             sprintf(message, "%s shogi program (%s) exited unexpectedly",
6984                     number, name);
6985             fprintf(stderr, "%s: %s\n", programName, message);
6986         }
6987         else
6988         {
6989             sprintf(message,
6990                     "error reading from %s shogi program (%s): %s",
6991                     number, name, strerror(ferror(fp)));
6992             fprintf(stderr, "%s: %s\n", programName, message);
6993         }
6994
6995         return;
6996     }
6997
6998     if ((end_str = (char *)strchr(message, '\r')) != NULL)
6999         *end_str = NULLCHAR;
7000
7001     if ((end_str = (char *)strchr(message, '\n')) != NULL)
7002         *end_str = NULLCHAR;
7003
7004     if (xshogiDebug || localPlayer.appData.debugMode)
7005     {
7006         fprintf(stderr, "Received from %s: %s\n",
7007                 ((fp == fromFirstProgFP) ? "first" : "second"), message);
7008     }
7009
7010     HandleMachineMove(message, fp);
7011 }
7012
7013
7014
7015
7016 void
7017 SendSearchDepth(FILE *fp)
7018 {
7019     char message[MSG_SIZ];
7020
7021     if (localPlayer.appData.searchDepth <= 0)
7022         return;
7023
7024     sprintf(message, "depth\n%d\nhelp\n", localPlayer.appData.searchDepth);
7025     /* Note kludge: "help" command forces gnushogi to print
7026      * out something that ends with a newline. */
7027     SendToProgram(message, fp);
7028 }
7029
7030
7031
7032
7033 void
7034 DisplayMessage(char *message, int toRemotePlayer)
7035 {
7036     Arg arg;
7037
7038     XtSetArg(arg, XtNlabel, message);
7039
7040     if (!toRemotePlayer)
7041         XtSetValues(localPlayer.messageWidget, &arg, 1);
7042
7043     if (updateRemotePlayer && toRemotePlayer)
7044         XtSetValues(remotePlayer.messageWidget, &arg, 1);
7045 }
7046
7047
7048
7049
7050 void
7051 DisplayName(char *name)
7052 {
7053     Arg arg;
7054
7055     XtSetArg(arg, XtNlabel, name);
7056     XtSetValues(localPlayer.titleWidget, &arg, 1);
7057
7058     if (updateRemotePlayer)
7059         XtSetValues(remotePlayer.titleWidget, &arg, 1);
7060 }
7061
7062
7063
7064
7065 void SendTimeRemaining(FILE *fp)
7066 {
7067     char message[MSG_SIZ];
7068     long comtime, opptime;
7069
7070     if (BlackOnMove(forwardMostMove) == (fp == toFirstProgFP))
7071     {
7072         comtime = blackTimeRemaining;
7073         opptime = whiteTimeRemaining;
7074     }
7075     else
7076     {
7077         comtime = whiteTimeRemaining;
7078         opptime = blackTimeRemaining;
7079     }
7080
7081     if (comtime <= 0)
7082         comtime = 1000;
7083
7084     if (opptime <= 0)
7085         opptime = 1000;
7086
7087     sprintf(message, "time %ld\n",  comtime / 10);
7088     SendToProgram(message, fp);
7089     sprintf(message, "otime %ld\n", opptime / 10);
7090     SendToProgram(message, fp);
7091 }
7092
7093
7094
7095
7096 void DisplayMove(int moveNumber)
7097 {
7098     char message[MSG_SIZ];
7099
7100     if (moveNumber < 0)
7101     {
7102         if (moveNumber == forwardMostMove - 1)
7103             DisplayMessage(endMessage, False);
7104         else
7105             DisplayMessage("", False);
7106     }
7107     else
7108     {
7109         sprintf(message, "%d. %s%s  %s",
7110                 (moveNumber / 2 + 1),
7111                 (BlackOnMove(moveNumber) ? "" : "... "),
7112                 parseList[moveNumber],
7113                 (moveNumber == (forwardMostMove - 1)) ? endMessage : "");
7114         DisplayMessage(message, False);
7115     }
7116 }
7117
7118
7119
7120
7121 void DisplayTitle(char *title)
7122 {
7123     Arg arg;
7124
7125     XtSetArg(arg, XtNlabel, title);
7126     XtSetValues(localPlayer.titleWidget, &arg, 1);
7127 }
7128
7129
7130
7131
7132 /*
7133  * This routine used to send a SIGINT (^C interrupt) to gnushogi to awaken it
7134  * if it might be busy thinking on our time.  This normally isn't needed,
7135  * but is useful on systems where the FIONREAD ioctl doesn't work since
7136  * on those systems the gnushogi feature that lets you interrupt its thinking
7137  * just by typing a command does not work.
7138  *
7139  * Now gnushogi periodically checks for user input without a need for
7140  * this hack.
7141  */
7142
7143 void
7144 Attention(int pid)
7145 {
7146 #if 0
7147     if (localPlayer.appData.noShogiProgram || (pid == 0))
7148         return;
7149
7150     switch (gameMode)
7151     {
7152     case MachinePlaysBlack:
7153     case MachinePlaysWhite:
7154     case TwoMachinesPlay:
7155         if ((forwardMostMove > backwardMostMove + 1) && maybeThinking)
7156         {
7157             if (xshogiDebug || localPlayer.appData.debugMode)
7158             {
7159                 fprintf(stderr, "Sending SIGINT to %s\n",
7160                         ((pid == firstProgramPID) ? "first" : "second"));
7161             }
7162
7163             (void)kill(pid, SIGINT); /* stop it thinking */
7164         }
7165         break;
7166
7167     default:
7168         break;  /* CHECKME: is this OK? */
7169     }
7170 #endif /* !defined(FIONREAD) */
7171 }
7172
7173
7174
7175
7176 void
7177 CheckFlags(void)
7178 {
7179     if (blackTimeRemaining <= 0)
7180     {
7181         if (!blackFlag)
7182         {
7183             blackFlag = True;
7184
7185             if (whiteFlag)
7186                 DisplayName("  Both flags have fallen");
7187             else
7188                 DisplayName("  Black's flag has fallen");
7189         }
7190     }
7191
7192     if (whiteTimeRemaining <= 0)
7193     {
7194         if (!whiteFlag)
7195         {
7196             whiteFlag = True;
7197
7198             if (blackFlag)
7199                 DisplayName("  Both flags have fallen");
7200             else
7201                 DisplayName("  White's flag has fallen");
7202         }
7203     }
7204 }
7205
7206
7207
7208
7209 void
7210 CheckTimeControl(void)
7211 {
7212     if (!localPlayer.appData.clockMode)
7213         return;
7214
7215     if (forwardMostMove == 0)
7216         return;
7217
7218     /*
7219      * Add time to clocks when time control is achieved.
7220      */
7221
7222     if ((forwardMostMove % (localPlayer.appData.movesPerSession * 2)) == 0)
7223     {
7224         blackTimeRemaining += timeControl;
7225         whiteTimeRemaining += timeControl;
7226     }
7227 }
7228
7229
7230
7231
7232 void
7233 DisplayLabels(void)
7234 {
7235     DisplayTimerLabel(localPlayer.blackTimerWidget, "Black",
7236                       blackTimeRemaining);
7237     DisplayTimerLabel(localPlayer.whiteTimerWidget, "White",
7238                       whiteTimeRemaining);
7239
7240     if (updateRemotePlayer)
7241     {
7242         DisplayTimerLabel(remotePlayer.blackTimerWidget, "Black",
7243                           blackTimeRemaining);
7244         DisplayTimerLabel(remotePlayer.whiteTimerWidget, "White",
7245                           whiteTimeRemaining);
7246     }
7247 }
7248
7249
7250
7251
7252 #ifdef HAVE_GETTIMEOFDAY
7253 static struct timeval tickStartTV;
7254 static int tickLength;
7255
7256 int
7257 PartialTickLength(void)
7258 {
7259     struct timeval tv;
7260     int ptl;
7261
7262     gettimeofday(&tv, NULL);
7263     ptl = ((tv.tv_sec - tickStartTV.tv_sec) * 1000000 +
7264            (tv.tv_usec - tickStartTV.tv_usec) + 500) / 1000;
7265
7266     if (ptl > tickLength)
7267         ptl = tickLength;
7268
7269     return ptl;
7270 }
7271 #else /* !HAVE_GETTIMEOFDAY */
7272 #define tickLength 1000
7273 #endif /* HAVE_GETTIMEOFDAY */
7274
7275
7276
7277
7278 /*
7279  * DisplayClocks manages the game clocks.
7280  *
7281  * In tournament play, white starts the clock and then black makes a move.
7282  * We give the human user a slight advantage if he is playing black---the
7283  * clocks don't run until he makes his first move, so it takes zero time.
7284  * Also, DisplayClocks doesn't account for network lag so it could get out
7285  * of sync with GNU Shogi's clock -- but then, referees are always right.
7286  */
7287
7288 void
7289 DisplayClocks(int clock_mode)
7290 {
7291     long timeRemaining;
7292
7293     switch (clock_mode)
7294     {
7295     case ResetTimers:
7296         /* Stop clocks and reset to a fresh time control */
7297         if (timerXID != 0)
7298         {
7299             XtRemoveTimeOut(timerXID);
7300             timerXID = 0;
7301         }
7302
7303         blackTimeRemaining = timeControl;
7304         whiteTimeRemaining = timeControl;
7305
7306         if (blackFlag || whiteFlag)
7307         {
7308             DisplayName("");
7309             blackFlag = whiteFlag = False;
7310         }
7311
7312         DisplayLabels();
7313         break;
7314
7315     case DecrementTimers:
7316         /* Decrement running clock to next 1-second boundary */
7317         if (gameMode == PauseGame)
7318             return;
7319
7320         timerXID = 0;
7321
7322         if (!localPlayer.appData.clockMode)
7323             return;
7324
7325         if (BlackOnMove(forwardMostMove))
7326         {
7327             timeRemaining = (blackTimeRemaining -= tickLength);
7328         }
7329         else
7330         {
7331             timeRemaining = (whiteTimeRemaining -= tickLength);
7332         }
7333
7334         DisplayLabels();
7335         CheckFlags();
7336
7337 #ifdef HAVE_GETTIMEOFDAY
7338         tickLength = (((timeRemaining <= 1000) && (timeRemaining > 0))
7339                       ? 100 : 1000);
7340         gettimeofday(&tickStartTV, NULL);
7341 #endif /* HAVE_GETTIMEOFDAY */
7342
7343         timerXID =
7344             XtAppAddTimeOut(appContext, tickLength,
7345                             (XtTimerCallbackProc) DisplayClocks,
7346                             (XtPointer) DecrementTimers);
7347         break;
7348
7349     case SwitchTimers:
7350         /* A player has just moved, so stop the previously running
7351            clock and start the other one. */
7352
7353         if (timerXID != 0)
7354         {
7355             XtRemoveTimeOut(timerXID);
7356             timerXID = 0;
7357
7358 #ifdef HAVE_GETTIMEOFDAY
7359             if (localPlayer.appData.clockMode)
7360             {
7361                 if (BlackOnMove(forwardMostMove))
7362                     whiteTimeRemaining -= PartialTickLength();
7363                 else
7364                     blackTimeRemaining -= PartialTickLength();
7365                 CheckFlags();
7366             }
7367 #endif /* HAVE_GETTIMEOFDAY */
7368         }
7369
7370         CheckTimeControl();
7371         DisplayLabels();
7372
7373         if (!localPlayer.appData.clockMode)
7374             return;
7375
7376         if ((gameMode == PauseGame)
7377             && ((pausePreviousMode == MachinePlaysBlack)
7378                 || (pausePreviousMode == MachinePlaysWhite)))
7379         {
7380             return;
7381         }
7382
7383         timeRemaining = (BlackOnMove(forwardMostMove)
7384                          ? blackTimeRemaining : whiteTimeRemaining);
7385
7386 #ifdef HAVE_GETTIMEOFDAY
7387         tickLength = (((timeRemaining <= 1000) && (timeRemaining > 0))
7388                       ? (((timeRemaining - 1) % 100) + 1)
7389                       : (((timeRemaining - 1) % 1000) + 1));
7390
7391         if (tickLength <= 0)
7392             tickLength += 1000;
7393
7394         gettimeofday(&tickStartTV, NULL);
7395
7396 #endif /* HAVE_GETTIMEOFDAY */
7397         timerXID =
7398             XtAppAddTimeOut(appContext, tickLength,
7399                             (XtTimerCallbackProc) DisplayClocks,
7400                             (XtPointer) DecrementTimers);
7401         break;
7402
7403     case ReDisplayTimers:
7404         /* Display current clock values */
7405         DisplayLabels();
7406         break;
7407
7408     case StopTimers:
7409         /* Stop both clocks */
7410         if (timerXID == 0)
7411             return;
7412
7413         XtRemoveTimeOut(timerXID);
7414         timerXID = 0;
7415
7416         if (!localPlayer.appData.clockMode)
7417             return;
7418
7419 #ifdef HAVE_GETTIMEOFDAY
7420         if (BlackOnMove(forwardMostMove))
7421             blackTimeRemaining -= PartialTickLength();
7422         else
7423             whiteTimeRemaining -= PartialTickLength();
7424         CheckFlags();
7425         DisplayLabels();
7426 #endif /* HAVE_GETTIMEOFDAY */
7427         break;
7428
7429     case StartTimers:
7430         /* Start clock of player on move, if not already running. */
7431         if (timerXID != 0)
7432             return;
7433
7434         DisplayLabels();
7435
7436         if (!localPlayer.appData.clockMode)
7437             return;
7438
7439         timeRemaining = (BlackOnMove(forwardMostMove)
7440                          ? blackTimeRemaining : whiteTimeRemaining);
7441
7442         if (timeRemaining == 0)
7443             return;
7444
7445 #ifdef HAVE_GETTIMEOFDAY
7446         tickLength = (((timeRemaining <= 1000) && (timeRemaining > 0))
7447                       ? (((timeRemaining - 1) % 100) + 1)
7448                       : (((timeRemaining - 1) % 1000) + 1));
7449
7450         if (tickLength <= 0)
7451             tickLength += 1000;
7452
7453         gettimeofday(&tickStartTV, NULL);
7454 #endif /* HAVE_GETTIMEOFDAY */
7455
7456         timerXID =
7457             XtAppAddTimeOut(appContext, tickLength,
7458                             (XtTimerCallbackProc) DisplayClocks,
7459                             (XtPointer)DecrementTimers);
7460         break;
7461     }
7462 }
7463
7464
7465
7466
7467 void
7468 DisplayTimerLabel(Widget w, char *color, long int timer)
7469 {
7470     char buf[MSG_SIZ];
7471     Arg args[3];
7472     struct DisplayData *player;
7473
7474     player = (((w == localPlayer.blackTimerWidget)
7475                || (w == localPlayer.whiteTimerWidget))
7476               ? &localPlayer : &remotePlayer);
7477
7478     if (localPlayer.appData.clockMode)
7479     {
7480         sprintf(buf, "%s: %s", color, TimeString(timer));
7481         XtSetArg(args[0], XtNlabel, buf);
7482     }
7483     else
7484     {
7485         XtSetArg(args[0], XtNlabel, color);
7486     }
7487
7488     if (((color[0] == 'W') && BlackOnMove(forwardMostMove))
7489         || ((color[0] == 'B') && !BlackOnMove(forwardMostMove)))
7490     {
7491         XtSetArg(args[1], XtNbackground, player->timerForegroundPixel);
7492         XtSetArg(args[2], XtNforeground, player->timerBackgroundPixel);
7493     }
7494     else
7495     {
7496         XtSetArg(args[1], XtNbackground, player->timerBackgroundPixel);
7497         XtSetArg(args[2], XtNforeground, player->timerForegroundPixel);
7498     }
7499
7500     XtSetValues(w, args, 3);
7501 }
7502
7503
7504
7505
7506 char *
7507 TimeString(long tm)
7508 {
7509     int second, minute, hour, day;
7510     char *sign = "";
7511     static char buf[32];
7512
7513     if ((tm > 0) && (tm <= 900))
7514     {
7515         /* convert milliseconds to tenths, rounding up */
7516         sprintf(buf, " 0.%1ld ", (tm + 99) / 100);
7517         return buf;
7518     }
7519
7520     /* convert milliseconds to seconds, rounding up */
7521     tm = (tm + 999) / 1000;
7522
7523     if (tm < 0)
7524     {
7525         sign = "-";
7526         tm = -tm;
7527     }
7528
7529     if (tm >= (60 * 60 * 24))
7530     {
7531         day = (int)(tm / (60 * 60 * 24));
7532         tm -= day * 60 * 60 * 24;
7533     }
7534     else
7535     {
7536         day = 0;
7537     }
7538
7539     if (tm >= (60 * 60))
7540     {
7541         hour = (int)(tm / (60 * 60));
7542         tm -= hour * 60 * 60;
7543     }
7544     else
7545     {
7546         hour = 0;
7547     }
7548
7549     if (tm >= 60)
7550     {
7551         minute = (int)(tm / 60);
7552         tm -= minute * 60;
7553     }
7554     else
7555     {
7556         minute = 0;
7557     }
7558
7559     second = tm % 60;
7560
7561     if (day > 0)
7562     {
7563         sprintf(buf, " %s%d:%02d:%02d:%02d ",
7564                 sign, day, hour, minute, second);
7565     }
7566     else if (hour > 0)
7567     {
7568         sprintf(buf, " %s%d:%02d:%02d ",
7569                 sign, hour, minute, second);
7570     }
7571     else
7572     {
7573         sprintf(buf, " %s%2d:%02d ",
7574                 sign, minute, second);
7575     }
7576
7577     return buf;
7578 }
7579
7580
7581
7582
7583 void
7584 Usage(void)
7585 {
7586     fprintf(stderr, "Usage: %s\n", programName);
7587     fprintf(stderr, "\tstandard Xt options\n");
7588     fprintf(stderr, "\t-iconic\n");
7589     fprintf(stderr, "\t-tc or -timeControl minutes[:seconds]\n");
7590     fprintf(stderr, "\t-gi or -gameIn (True | False)\n");
7591     fprintf(stderr, "\t-mps or -movesPerSession moves\n");
7592     fprintf(stderr, "\t-st or -searchTime minutes[:seconds]\n");
7593     fprintf(stderr, "\t-sd or -searchDepth number\n");
7594     fprintf(stderr, "\t-clock or -clockMode (True | False)\n");
7595     fprintf(stderr, "\t-td or -timeDelay seconds\n");
7596
7597     fprintf(stderr, "\t-nsp or -noShogiProgram (True | False)\n");
7598     fprintf(stderr, "\t-fsp or -firstShogiProgram program_name\n");
7599     fprintf(stderr, "\t-ssp or -secondShogiProgram program_name\n");
7600     fprintf(stderr, "\t-fh or -firstHost host_name\n");
7601     fprintf(stderr, "\t-sh or -secondHost host_name\n");
7602     fprintf(stderr, "\t-rsh or -remoteShell shell_name\n");
7603     fprintf(stderr,
7604             "\t-mm or -matchMode (False | Init | Position | Opening)\n");
7605     fprintf(stderr, "\t-lgf or -loadGameFile file_name\n");
7606     fprintf(stderr, "\t-lpf or -loadPositionFile file_name\n");
7607     fprintf(stderr, "\t-sgf or -saveGameFile file_name\n");
7608     fprintf(stderr, "\t-spf or -savePositionFile file_name\n");
7609     fprintf(stderr, "\t-size or -boardSize (Large | Medium | Small)\n");
7610     fprintf(stderr, "\t-coords or -showCoords (True | False)\n");
7611     fprintf(stderr, "\t-mono or -monoMode (True | False)\n");
7612     fprintf(stderr, "\t-pc or -pieceColor color\n");
7613     fprintf(stderr, "\t-sc  or -squareColor color\n");
7614     fprintf(stderr, "\t-wps or -westernPieceSet (True | False)\n");
7615     fprintf(stderr, "\t-debug or -debugMode (True | False)\n");
7616     exit(2);
7617 }
7618
7619
7620
7621 void
7622 CatchPipeSignal(int dummy)
7623 {
7624     char message[MSG_SIZ];
7625
7626     sprintf(message,
7627             "%s shogi program (%s) exited unexpectedly",
7628             ((lastMsgFP == toFirstProgFP) ? "first" : "second"),
7629             ((lastMsgFP == toFirstProgFP)
7630              ? localPlayer.appData.firstShogiProgram
7631              : localPlayer.appData.secondShogiProgram));
7632     fprintf(stderr, "%s: %s\n", programName, message);
7633     ShutdownShogiPrograms(message);
7634     return;
7635 }
7636