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