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