fixed some outstanding pixmaps
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts. 
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #include "config.h"
53
54 #include <stdio.h>
55 #include <ctype.h>
56 #include <signal.h>
57 #include <errno.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <pwd.h>
61
62 #if !OMIT_SOCKETS
63 # if HAVE_SYS_SOCKET_H
64 #  include <sys/socket.h>
65 #  include <netinet/in.h>
66 #  include <netdb.h>
67 # else /* not HAVE_SYS_SOCKET_H */
68 #  if HAVE_LAN_SOCKET_H
69 #   include <lan/socket.h>
70 #   include <lan/in.h>
71 #   include <lan/netdb.h>
72 #  else /* not HAVE_LAN_SOCKET_H */
73 #   define OMIT_SOCKETS 1
74 #  endif /* not HAVE_LAN_SOCKET_H */
75 # endif /* not HAVE_SYS_SOCKET_H */
76 #endif /* !OMIT_SOCKETS */
77
78 #if STDC_HEADERS
79 # include <stdlib.h>
80 # include <string.h>
81 #else /* not STDC_HEADERS */
82 extern char *getenv();
83 # if HAVE_STRING_H
84 #  include <string.h>
85 # else /* not HAVE_STRING_H */
86 #  include <strings.h>
87 # endif /* not HAVE_STRING_H */
88 #endif /* not STDC_HEADERS */
89
90 #if HAVE_SYS_FCNTL_H
91 # include <sys/fcntl.h>
92 #else /* not HAVE_SYS_FCNTL_H */
93 # if HAVE_FCNTL_H
94 #  include <fcntl.h>
95 # endif /* HAVE_FCNTL_H */
96 #endif /* not HAVE_SYS_FCNTL_H */
97
98 #if HAVE_SYS_SYSTEMINFO_H
99 # include <sys/systeminfo.h>
100 #endif /* HAVE_SYS_SYSTEMINFO_H */
101
102 #if TIME_WITH_SYS_TIME
103 # include <sys/time.h>
104 # include <time.h>
105 #else
106 # if HAVE_SYS_TIME_H
107 #  include <sys/time.h>
108 # else
109 #  include <time.h>
110 # endif
111 #endif
112
113 #if HAVE_UNISTD_H
114 # include <unistd.h>
115 #endif
116
117 #if HAVE_SYS_WAIT_H
118 # include <sys/wait.h>
119 #endif
120
121 #if HAVE_DIRENT_H
122 # include <dirent.h>
123 # define NAMLEN(dirent) strlen((dirent)->d_name)
124 # define HAVE_DIR_STRUCT
125 #else
126 # define dirent direct
127 # define NAMLEN(dirent) (dirent)->d_namlen
128 # if HAVE_SYS_NDIR_H
129 #  include <sys/ndir.h>
130 #  define HAVE_DIR_STRUCT
131 # endif
132 # if HAVE_SYS_DIR_H
133 #  include <sys/dir.h>
134 #  define HAVE_DIR_STRUCT
135 # endif
136 # if HAVE_NDIR_H
137 #  include <ndir.h>
138 #  define HAVE_DIR_STRUCT
139 # endif
140 #endif
141
142 #include <X11/Intrinsic.h>
143 #include <X11/StringDefs.h>
144 #include <X11/Shell.h>
145 #include <X11/cursorfont.h>
146 #include <X11/Xatom.h>
147 #include <X11/Xmu/Atoms.h>
148 #if USE_XAW3D
149 #include <X11/Xaw3d/Dialog.h>
150 #include <X11/Xaw3d/Form.h>
151 #include <X11/Xaw3d/List.h>
152 #include <X11/Xaw3d/Label.h>
153 #include <X11/Xaw3d/SimpleMenu.h>
154 #include <X11/Xaw3d/SmeBSB.h>
155 #include <X11/Xaw3d/SmeLine.h>
156 #include <X11/Xaw3d/Box.h>
157 #include <X11/Xaw3d/MenuButton.h>
158 #include <X11/Xaw3d/Text.h>
159 #include <X11/Xaw3d/AsciiText.h>
160 #else
161 #include <X11/Xaw/Dialog.h>
162 #include <X11/Xaw/Form.h>
163 #include <X11/Xaw/List.h>
164 #include <X11/Xaw/Label.h>
165 #include <X11/Xaw/SimpleMenu.h>
166 #include <X11/Xaw/SmeBSB.h>
167 #include <X11/Xaw/SmeLine.h>
168 #include <X11/Xaw/Box.h>
169 #include <X11/Xaw/MenuButton.h>
170 #include <X11/Xaw/Text.h>
171 #include <X11/Xaw/AsciiText.h>
172 #endif
173
174 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
175 #include "common.h"
176
177 #if HAVE_LIBXPM
178 #include <X11/xpm.h>
179 #include "pixmaps/pixmaps.h"
180 #define IMAGE_EXT "xpm"
181 #else
182 #define IMAGE_EXT "xim"
183 #include "bitmaps/bitmaps.h"
184 #endif
185
186 #include "bitmaps/icon_white.bm"
187 #include "bitmaps/icon_black.bm"
188 #include "bitmaps/checkmark.bm"
189
190 #include "frontend.h"
191 #include "backend.h"
192 #include "moves.h"
193 #include "xboard.h"
194 #include "childio.h"
195 #include "xgamelist.h"
196 #include "xhistory.h"
197 #include "xedittags.h"
198 #include "gettext.h"
199
200 // must be moved to xengineoutput.h
201
202 void EngineOutputProc P((Widget w, XEvent *event,
203  String *prms, Cardinal *nprms));
204
205
206 #ifdef __EMX__
207 #ifndef HAVE_USLEEP
208 #define HAVE_USLEEP
209 #endif
210 #define usleep(t)   _sleep2(((t)+500)/1000)
211 #endif
212
213 #ifdef ENABLE_NLS
214 # define  _(s) gettext (s)
215 # define N_(s) gettext_noop (s)
216 #else
217 # define  _(s) (s)
218 # define N_(s)  s
219 #endif
220
221 typedef struct {
222     String string;
223     XtActionProc proc;
224 } MenuItem;
225
226 typedef struct {
227     String name;
228     MenuItem *mi;
229 } Menu;
230
231 int main P((int argc, char **argv));
232 RETSIGTYPE CmailSigHandler P((int sig));
233 RETSIGTYPE IntSigHandler P((int sig));
234 RETSIGTYPE TermSizeSigHandler P((int sig));
235 void CreateGCs P((void));
236 void CreateXIMPieces P((void));
237 void CreateXPMPieces P((void));
238 void CreatePieces P((void));
239 void CreatePieceMenus P((void));
240 Widget CreateMenuBar P((Menu *mb));
241 Widget CreateButtonBar P ((MenuItem *mi));
242 char *FindFont P((char *pattern, int targetPxlSize));
243 void PieceMenuPopup P((Widget w, XEvent *event,
244                        String *params, Cardinal *num_params));
245 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
246 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
247 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
248                    u_int wreq, u_int hreq));
249 void CreateGrid P((void));
250 int EventToSquare P((int x, int limit));
251 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
252 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
253 void HandleUserMove P((Widget w, XEvent *event,
254                      String *prms, Cardinal *nprms));
255 void AnimateUserMove P((Widget w, XEvent * event,
256                      String * params, Cardinal * nParams));
257 void WhiteClock P((Widget w, XEvent *event,
258                    String *prms, Cardinal *nprms));
259 void BlackClock P((Widget w, XEvent *event,
260                    String *prms, Cardinal *nprms));
261 void DrawPositionProc P((Widget w, XEvent *event,
262                      String *prms, Cardinal *nprms));
263 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
264                      Board board));
265 void CommentPopUp P((char *title, char *label));
266 void CommentPopDown P((void));
267 void CommentCallback P((Widget w, XtPointer client_data,
268                         XtPointer call_data));
269 void ICSInputBoxPopUp P((void));
270 void ICSInputBoxPopDown P((void));
271 void FileNamePopUp P((char *label, char *def,
272                       FileProc proc, char *openMode));
273 void FileNamePopDown P((void));
274 void FileNameCallback P((Widget w, XtPointer client_data,
275                          XtPointer call_data));
276 void FileNameAction P((Widget w, XEvent *event,
277                        String *prms, Cardinal *nprms));
278 void AskQuestionReplyAction P((Widget w, XEvent *event,
279                           String *prms, Cardinal *nprms));
280 void AskQuestionProc P((Widget w, XEvent *event,
281                           String *prms, Cardinal *nprms));
282 void AskQuestionPopDown P((void));
283 void PromotionPopDown P((void));
284 void PromotionCallback P((Widget w, XtPointer client_data,
285                           XtPointer call_data));
286 void EditCommentPopDown P((void));
287 void EditCommentCallback P((Widget w, XtPointer client_data,
288                             XtPointer call_data));
289 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
290 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
293                          Cardinal *nprms));
294 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
295                          Cardinal *nprms));
296 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
297                        Cardinal *nprms));
298 void LoadPositionProc P((Widget w, XEvent *event,
299                          String *prms, Cardinal *nprms));
300 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
301                          Cardinal *nprms));
302 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
303                          Cardinal *nprms));
304 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
305                        Cardinal *nprms));
306 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
307                          Cardinal *nprms));
308 void PastePositionProc P((Widget w, XEvent *event, String *prms,
309                           Cardinal *nprms));
310 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
311 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
312 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
313 void SavePositionProc P((Widget w, XEvent *event,
314                          String *prms, Cardinal *nprms));
315 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
316 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
317                             Cardinal *nprms));
318 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
321                          Cardinal *nprms));
322 void MachineWhiteProc P((Widget w, XEvent *event,
323                          String *prms, Cardinal *nprms));
324 void AnalyzeModeProc P((Widget w, XEvent *event,
325                          String *prms, Cardinal *nprms));
326 void AnalyzeFileProc P((Widget w, XEvent *event,
327                          String *prms, Cardinal *nprms));
328 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
329                         Cardinal *nprms));
330 void IcsClientProc P((Widget w, XEvent *event, String *prms,
331                       Cardinal *nprms));
332 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
333 void EditPositionProc P((Widget w, XEvent *event,
334                          String *prms, Cardinal *nprms));
335 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void EditCommentProc P((Widget w, XEvent *event,
337                         String *prms, Cardinal *nprms));
338 void IcsInputBoxProc P((Widget w, XEvent *event,
339                         String *prms, Cardinal *nprms));
340 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
343 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
345 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
346 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
349 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
350 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
351 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
352 void StopObservingProc P((Widget w, XEvent *event, String *prms,
353                           Cardinal *nprms));
354 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
355                           Cardinal *nprms));
356 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
362                          Cardinal *nprms));
363 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
364                         Cardinal *nprms));
365 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
367                         Cardinal *nprms));
368 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
369                          Cardinal *nprms));
370 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
371                          Cardinal *nprms));
372 void AutocommProc P((Widget w, XEvent *event, String *prms,
373                      Cardinal *nprms));
374 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void AutobsProc P((Widget w, XEvent *event, String *prms,
377                         Cardinal *nprms));
378 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
382                        Cardinal *nprms));
383 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
385                         Cardinal *nprms));
386 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
387                               Cardinal *nprms));
388 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
389                               Cardinal *nprms));
390 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
393                          Cardinal *nprms));
394 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
395                          Cardinal *nprms));
396 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
397                            Cardinal *nprms));
398 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
399                         Cardinal *nprms));
400 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
401                              Cardinal *nprms));
402 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
403 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
404 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
405                        Cardinal *nprms));
406 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
407                          Cardinal *nprms));
408 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
409                          Cardinal *nprms));
410 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
411                           Cardinal *nprms));
412 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
413 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
414 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
415 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
416 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
417 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
418 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
419 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
420 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
421 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 void DisplayMove P((int moveNumber));
424 void DisplayTitle P((char *title));
425 void ICSInitScript P((void));
426 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
427 void ErrorPopUp P((char *title, char *text, int modal));
428 void ErrorPopDown P((void));
429 static char *ExpandPathName P((char *path));
430 static void CreateAnimVars P((void));
431 static void DragPieceMove P((int x, int y));
432 static void DrawDragPiece P((void));
433 char *ModeToWidgetName P((GameMode mode));
434 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void ShufflePopDown P(());
442 void EnginePopDown P(());
443 void UciPopDown P(());
444 void TimeControlPopDown P(());
445 void NewVariantPopDown P(());
446 void SettingsPopDown P(());
447 void update_ics_width P(());
448 int get_term_width P(());
449 /*
450 * XBoard depends on Xt R4 or higher
451 */
452 int xtVersion = XtSpecificationRelease;
453
454 int xScreen;
455 Display *xDisplay;
456 Window xBoardWindow;
457 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
458   jailSquareColor, highlightSquareColor, premoveHighlightColor;
459 Pixel lowTimeWarningColor;
460 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
461   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
462   wjPieceGC, bjPieceGC, prelineGC, countGC;
463 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
464 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
465   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
466   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
467   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
468   ICSInputShell, fileNameShell, askQuestionShell;
469 Widget historyShell, evalGraphShell, gameListShell;
470 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
471 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
472 Font clockFontID, coordFontID, countFontID;
473 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
474 XtAppContext appContext;
475 char *layoutName;
476 char *oldICSInteractionTitle;
477
478 FileProc fileProc;
479 char *fileOpenMode;
480 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
481
482 Position commentX = -1, commentY = -1;
483 Dimension commentW, commentH;
484 typedef unsigned int BoardSize;
485 BoardSize boardSize;
486 Boolean chessProgram;
487
488 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
489 int squareSize, smallLayout = 0, tinyLayout = 0,
490   marginW, marginH, // [HGM] for run-time resizing
491   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
492   ICSInputBoxUp = False, askQuestionUp = False,
493   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
494   editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
495 Pixel timerForegroundPixel, timerBackgroundPixel;
496 Pixel buttonForegroundPixel, buttonBackgroundPixel;
497 char *chessDir, *programName, *programVersion,
498   *gameCopyFilename, *gamePasteFilename;
499 Boolean alwaysOnTop = False;
500 Boolean saveSettingsOnExit;
501 char *settingsFileName;
502 char *icsTextMenuString;
503 char *icsNames;
504 char *firstChessProgramNames;
505 char *secondChessProgramNames;
506
507 WindowPlacement wpMain;
508 WindowPlacement wpConsole;
509 WindowPlacement wpComment;
510 WindowPlacement wpMoveHistory;
511 WindowPlacement wpEvalGraph;
512 WindowPlacement wpEngineOutput;
513 WindowPlacement wpGameList;
514 WindowPlacement wpTags;
515
516 #define SOLID 0
517 #define OUTLINE 1
518 Pixmap pieceBitmap[2][(int)BlackPawn];
519 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
520 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
521 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
522 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
523 int useImages, useImageSqs;
524 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
525 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
526 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
527 XImage *ximLightSquare, *ximDarkSquare;
528 XImage *xim_Cross;
529
530 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
531 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
532
533 #define White(piece) ((int)(piece) < (int)BlackPawn)
534
535 /* Variables for doing smooth animation. This whole thing
536    would be much easier if the board was double-buffered,
537    but that would require a fairly major rewrite.       */
538
539 typedef struct {
540         Pixmap  saveBuf;
541         Pixmap  newBuf;
542         GC      blitGC, pieceGC, outlineGC;
543         XPoint  startSquare, prevFrame, mouseDelta;
544         int     startColor;
545         int     dragPiece;
546         Boolean dragActive;
547         int     startBoardX, startBoardY;
548     } AnimState;
549
550 /* There can be two pieces being animated at once: a player
551    can begin dragging a piece before the remote opponent has moved. */
552
553 static AnimState game, player;
554
555 /* Bitmaps for use as masks when drawing XPM pieces.
556    Need one for each black and white piece.             */
557 static Pixmap xpmMask[BlackKing + 1];
558
559 /* This magic number is the number of intermediate frames used
560    in each half of the animation. For short moves it's reduced
561    by 1. The total number of frames will be factor * 2 + 1.  */
562 #define kFactor    4
563
564 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
565
566 MenuItem fileMenu[] = {
567     {N_("New Game"), ResetProc},
568     {N_("New Shuffle Game ..."), ShuffleMenuProc},
569     {N_("New Variant ..."), NewVariantProc},      // [HGM] variant: not functional yet
570     {"----", NothingProc},
571     {N_("Load Game"), LoadGameProc},
572     {N_("Load Next Game"), LoadNextGameProc},
573     {N_("Load Previous Game"), LoadPrevGameProc},
574     {N_("Reload Same Game"), ReloadGameProc},
575     {N_("Save Game"), SaveGameProc},
576     {"----", NothingProc},
577     {N_("Copy Game"), CopyGameProc},
578     {N_("Paste Game"), PasteGameProc},
579     {"----", NothingProc},
580     {N_("Load Position"), LoadPositionProc},
581     {N_("Load Next Position"), LoadNextPositionProc},
582     {N_("Load Previous Position"), LoadPrevPositionProc},
583     {N_("Reload Same Position"), ReloadPositionProc},
584     {N_("Save Position"), SavePositionProc},
585     {"----", NothingProc},
586     {N_("Copy Position"), CopyPositionProc},
587     {N_("Paste Position"), PastePositionProc},
588     {"----", NothingProc},
589     {N_("Mail Move"), MailMoveProc},
590     {N_("Reload CMail Message"), ReloadCmailMsgProc},
591     {"----", NothingProc},
592     {N_("Exit"), QuitProc},
593     {NULL, NULL}
594 };
595
596 MenuItem modeMenu[] = {
597     {N_("Machine White"), MachineWhiteProc},
598     {N_("Machine Black"), MachineBlackProc},
599     {N_("Two Machines"), TwoMachinesProc},
600     {N_("Analysis Mode"), AnalyzeModeProc},
601     {N_("Analyze File"), AnalyzeFileProc },
602     {N_("ICS Client"), IcsClientProc},
603     {N_("Edit Game"), EditGameProc},
604     {N_("Edit Position"), EditPositionProc},
605     {N_("Training"), TrainingProc},
606     {"----", NothingProc},
607     {N_("Show Engine Output"), EngineOutputProc},
608     {N_("Show Evaluation Graph"), NothingProc}, // [HGM] evalgr: not functional yet
609     {N_("Show Game List"), ShowGameListProc},
610     {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
611     {"----", NothingProc},
612     {N_("Edit Tags"), EditTagsProc},
613     {N_("Edit Comment"), EditCommentProc},
614     {N_("ICS Input Box"), IcsInputBoxProc},
615     {N_("Pause"), PauseProc},
616     {NULL, NULL}
617 };
618
619 MenuItem actionMenu[] = {
620     {N_("Accept"), AcceptProc},
621     {N_("Decline"), DeclineProc},
622     {N_("Rematch"), RematchProc},
623     {"----", NothingProc},
624     {N_("Call Flag"), CallFlagProc},
625     {N_("Draw"), DrawProc},
626     {N_("Adjourn"), AdjournProc},
627     {N_("Abort"), AbortProc},
628     {N_("Resign"), ResignProc},
629     {"----", NothingProc},
630     {N_("Stop Observing"), StopObservingProc},
631     {N_("Stop Examining"), StopExaminingProc},
632     {"----", NothingProc},
633     {N_("Adjudicate to White"), AdjuWhiteProc},
634     {N_("Adjudicate to Black"), AdjuBlackProc},
635     {N_("Adjudicate Draw"), AdjuDrawProc},
636     {NULL, NULL}
637 };
638
639 MenuItem stepMenu[] = {
640     {N_("Backward"), BackwardProc},
641     {N_("Forward"), ForwardProc},
642     {N_("Back to Start"), ToStartProc},
643     {N_("Forward to End"), ToEndProc},
644     {N_("Revert"), RevertProc},
645     {N_("Truncate Game"), TruncateGameProc},
646     {"----", NothingProc},
647     {N_("Move Now"), MoveNowProc},
648     {N_("Retract Move"), RetractMoveProc},
649     {NULL, NULL}
650 };
651
652 MenuItem optionsMenu[] = {
653     {N_("Flip View"), FlipViewProc},
654     {"----", NothingProc},
655     {N_("Adjudications ..."), EngineMenuProc},
656     {N_("General Settings ..."), UciMenuProc},
657     {N_("Engine #1 Settings ..."), FirstSettingsProc},
658     {N_("Engine #2 Settings ..."), SecondSettingsProc},
659     {N_("Time Control ..."), TimeControlProc},
660     {"----", NothingProc},
661     {N_("Always Queen"), AlwaysQueenProc},
662     {N_("Animate Dragging"), AnimateDraggingProc},
663     {N_("Animate Moving"), AnimateMovingProc},
664     {N_("Auto Comment"), AutocommProc},
665     {N_("Auto Flag"), AutoflagProc},
666     {N_("Auto Flip View"), AutoflipProc},
667     {N_("Auto Observe"), AutobsProc},
668     {N_("Auto Raise Board"), AutoraiseProc},
669     {N_("Auto Save"), AutosaveProc},
670     {N_("Blindfold"), BlindfoldProc},
671     {N_("Flash Moves"), FlashMovesProc},
672     {N_("Get Move List"), GetMoveListProc},
673 #if HIGHDRAG
674     {N_("Highlight Dragging"), HighlightDraggingProc},
675 #endif
676     {N_("Highlight Last Move"), HighlightLastMoveProc},
677     {N_("Move Sound"), MoveSoundProc},
678     {N_("ICS Alarm"), IcsAlarmProc},
679     {N_("Old Save Style"), OldSaveStyleProc},
680     {N_("Periodic Updates"), PeriodicUpdatesProc},
681     {N_("Ponder Next Move"), PonderNextMoveProc},
682     {N_("Popup Exit Message"), PopupExitMessageProc},
683     {N_("Popup Move Errors"), PopupMoveErrorsProc},
684     {N_("Premove"), PremoveProc},
685     {N_("Quiet Play"), QuietPlayProc},
686     {N_("Show Coords"), ShowCoordsProc},
687     {N_("Hide Thinking"), HideThinkingProc},
688     {N_("Test Legality"), TestLegalityProc},
689     {"----", NothingProc},
690     {N_("Save Settings Now"), SaveSettingsProc},
691     {N_("Save Settings on Exit"), SaveOnExitProc},
692     {NULL, NULL}
693 };
694
695 MenuItem helpMenu[] = {
696     {N_("Info XBoard"), InfoProc},
697     {N_("Man XBoard"), ManProc},
698     {"----", NothingProc},
699     {N_("Hint"), HintProc},
700     {N_("Book"), BookProc},
701     {"----", NothingProc},
702     {N_("About XBoard"), AboutProc},
703     {NULL, NULL}
704 };
705
706 Menu menuBar[] = {
707     {N_("File"), fileMenu},
708     {N_("Mode"), modeMenu},
709     {N_("Action"), actionMenu},
710     {N_("Step"), stepMenu},
711     {N_("Options"), optionsMenu},
712     {N_("Help"), helpMenu},
713     {NULL, NULL}
714 };
715
716 #define PAUSE_BUTTON N_("P")
717 MenuItem buttonBar[] = {
718     {"<<", ToStartProc},
719     {"<", BackwardProc},
720     {PAUSE_BUTTON, PauseProc},
721     {">", ForwardProc},
722     {">>", ToEndProc},
723     {NULL, NULL}
724 };
725
726 #define PIECE_MENU_SIZE 18
727 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
728     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
729       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
730       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
731       N_("Empty square"), N_("Clear board") },
732     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
733       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
734       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
735       N_("Empty square"), N_("Clear board") }
736 };
737 /* must be in same order as PieceMenuStrings! */
738 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
739     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
740         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
741         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0, 
742         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
743     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
744         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
745         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0, 
746         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
747 };
748
749 #define DROP_MENU_SIZE 6
750 String dropMenuStrings[DROP_MENU_SIZE] = {
751     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
752   };
753 /* must be in same order as PieceMenuStrings! */
754 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
755     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
756     WhiteRook, WhiteQueen
757 };
758
759 typedef struct {
760     char piece;
761     char* widget;
762 } DropMenuEnables;
763
764 DropMenuEnables dmEnables[] = {
765     { 'P', "Pawn" },
766     { 'N', "Knight" },
767     { 'B', "Bishop" },
768     { 'R', "Rook" },
769     { 'Q', "Queen" }
770 };
771
772 Arg shellArgs[] = {
773     { XtNwidth, 0 },
774     { XtNheight, 0 },
775     { XtNminWidth, 0 },
776     { XtNminHeight, 0 },
777     { XtNmaxWidth, 0 },
778     { XtNmaxHeight, 0 }
779 };
780
781 Arg layoutArgs[] = {
782     { XtNborderWidth, 0 },
783     { XtNdefaultDistance, 0 },
784 };
785
786 Arg formArgs[] = {
787     { XtNborderWidth, 0 },
788     { XtNresizable, (XtArgVal) True },
789 };
790
791 Arg boardArgs[] = {
792     { XtNborderWidth, 0 },
793     { XtNwidth, 0 },
794     { XtNheight, 0 }
795 };
796
797 Arg titleArgs[] = {
798     { XtNjustify, (XtArgVal) XtJustifyRight },
799     { XtNlabel, (XtArgVal) "..." },
800     { XtNresizable, (XtArgVal) True },
801     { XtNresize, (XtArgVal) False }
802 };
803
804 Arg messageArgs[] = {
805     { XtNjustify, (XtArgVal) XtJustifyLeft },
806     { XtNlabel, (XtArgVal) "..." },
807     { XtNresizable, (XtArgVal) True },
808     { XtNresize, (XtArgVal) False }
809 };
810
811 Arg timerArgs[] = {
812     { XtNborderWidth, 0 },
813     { XtNjustify, (XtArgVal) XtJustifyLeft }
814 };
815
816 XtResource clientResources[] = {
817     { "flashCount", "flashCount", XtRInt, sizeof(int),
818         XtOffset(AppDataPtr, flashCount), XtRImmediate,
819         (XtPointer) FLASH_COUNT  },
820 };
821
822 XrmOptionDescRec shellOptions[] = {
823     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
824     { "-flash", "flashCount", XrmoptionNoArg, "3" },
825     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
826 };
827
828 XtActionsRec boardActions[] = {
829     { "DrawPosition", DrawPositionProc },
830     { "HandleUserMove", HandleUserMove },
831     { "AnimateUserMove", AnimateUserMove },
832     { "FileNameAction", FileNameAction },
833     { "AskQuestionProc", AskQuestionProc },
834     { "AskQuestionReplyAction", AskQuestionReplyAction },
835     { "PieceMenuPopup", PieceMenuPopup },
836     { "WhiteClock", WhiteClock },
837     { "BlackClock", BlackClock },
838     { "Iconify", Iconify },
839     { "ResetProc", ResetProc },
840     { "LoadGameProc", LoadGameProc },
841     { "LoadNextGameProc", LoadNextGameProc },
842     { "LoadPrevGameProc", LoadPrevGameProc },
843     { "LoadSelectedProc", LoadSelectedProc },
844     { "ReloadGameProc", ReloadGameProc },
845     { "LoadPositionProc", LoadPositionProc },
846     { "LoadNextPositionProc", LoadNextPositionProc },
847     { "LoadPrevPositionProc", LoadPrevPositionProc },
848     { "ReloadPositionProc", ReloadPositionProc },
849     { "CopyPositionProc", CopyPositionProc },
850     { "PastePositionProc", PastePositionProc },
851     { "CopyGameProc", CopyGameProc },
852     { "PasteGameProc", PasteGameProc },
853     { "SaveGameProc", SaveGameProc },
854     { "SavePositionProc", SavePositionProc },
855     { "MailMoveProc", MailMoveProc },
856     { "ReloadCmailMsgProc", ReloadCmailMsgProc },
857     { "QuitProc", QuitProc },
858     { "MachineWhiteProc", MachineWhiteProc },
859     { "MachineBlackProc", MachineBlackProc },
860     { "AnalysisModeProc", AnalyzeModeProc },
861     { "AnalyzeFileProc", AnalyzeFileProc },
862     { "TwoMachinesProc", TwoMachinesProc },
863     { "IcsClientProc", IcsClientProc },
864     { "EditGameProc", EditGameProc },
865     { "EditPositionProc", EditPositionProc },
866     { "TrainingProc", EditPositionProc },
867     { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
868     { "ShowGameListProc", ShowGameListProc },
869     { "ShowMoveListProc", HistoryShowProc},
870     { "EditTagsProc", EditCommentProc },
871     { "EditCommentProc", EditCommentProc },
872     { "IcsAlarmProc", IcsAlarmProc },
873     { "IcsInputBoxProc", IcsInputBoxProc },
874     { "PauseProc", PauseProc },
875     { "AcceptProc", AcceptProc },
876     { "DeclineProc", DeclineProc },
877     { "RematchProc", RematchProc },
878     { "CallFlagProc", CallFlagProc },
879     { "DrawProc", DrawProc },
880     { "AdjournProc", AdjournProc },
881     { "AbortProc", AbortProc },
882     { "ResignProc", ResignProc },
883     { "AdjuWhiteProc", AdjuWhiteProc },
884     { "AdjuBlackProc", AdjuBlackProc },
885     { "AdjuDrawProc", AdjuDrawProc },
886     { "EnterKeyProc", EnterKeyProc },
887     { "StopObservingProc", StopObservingProc },
888     { "StopExaminingProc", StopExaminingProc },
889     { "BackwardProc", BackwardProc },
890     { "ForwardProc", ForwardProc },
891     { "ToStartProc", ToStartProc },
892     { "ToEndProc", ToEndProc },
893     { "RevertProc", RevertProc },
894     { "TruncateGameProc", TruncateGameProc },
895     { "MoveNowProc", MoveNowProc },
896     { "RetractMoveProc", RetractMoveProc },
897     { "AlwaysQueenProc", AlwaysQueenProc },
898     { "AnimateDraggingProc", AnimateDraggingProc },
899     { "AnimateMovingProc", AnimateMovingProc },
900     { "AutoflagProc", AutoflagProc },
901     { "AutoflipProc", AutoflipProc },
902     { "AutobsProc", AutobsProc },
903     { "AutoraiseProc", AutoraiseProc },
904     { "AutosaveProc", AutosaveProc },
905     { "BlindfoldProc", BlindfoldProc },
906     { "FlashMovesProc", FlashMovesProc },
907     { "FlipViewProc", FlipViewProc },
908     { "GetMoveListProc", GetMoveListProc },
909 #if HIGHDRAG
910     { "HighlightDraggingProc", HighlightDraggingProc },
911 #endif
912     { "HighlightLastMoveProc", HighlightLastMoveProc },
913     { "IcsAlarmProc", IcsAlarmProc },
914     { "MoveSoundProc", MoveSoundProc },
915     { "OldSaveStyleProc", OldSaveStyleProc },
916     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
917     { "PonderNextMoveProc", PonderNextMoveProc },
918     { "PopupExitMessageProc", PopupExitMessageProc },
919     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
920     { "PremoveProc", PremoveProc },
921     { "QuietPlayProc", QuietPlayProc },
922     { "ShowCoordsProc", ShowCoordsProc },
923     { "ShowThinkingProc", ShowThinkingProc },
924     { "HideThinkingProc", HideThinkingProc },
925     { "TestLegalityProc", TestLegalityProc },
926     { "SaveSettingsProc", SaveSettingsProc },
927     { "SaveOnExitProc", SaveOnExitProc },
928     { "InfoProc", InfoProc },
929     { "ManProc", ManProc },
930     { "HintProc", HintProc },
931     { "BookProc", BookProc },
932     { "AboutGameProc", AboutGameProc },
933     { "AboutProc", AboutProc },
934     { "DebugProc", DebugProc },
935     { "NothingProc", NothingProc },
936     { "CommentPopDown", (XtActionProc) CommentPopDown },
937     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
938     { "TagsPopDown", (XtActionProc) TagsPopDown },
939     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
940     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
941     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
942     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
943     { "GameListPopDown", (XtActionProc) GameListPopDown },
944     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
945     { "HistoryPopDown", (XtActionProc) HistoryPopDown },
946     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
947     { "ShufflePopDown", (XtActionProc) ShufflePopDown },
948     { "EnginePopDown", (XtActionProc) EnginePopDown },
949     { "UciPopDown", (XtActionProc) UciPopDown },
950     { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
951     { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
952     { "SettingsPopDown", (XtActionProc) SettingsPopDown },
953 };
954
955 char globalTranslations[] =
956   ":<Key>R: ResignProc() \n \
957    :<Key>r: ResetProc() \n \
958    :<Key>g: LoadGameProc() \n \
959    :<Key>N: LoadNextGameProc() \n \
960    :<Key>P: LoadPrevGameProc() \n \
961    :<Key>Q: QuitProc() \n \
962    :<Key>F: ToEndProc() \n \
963    :<Key>f: ForwardProc() \n \
964    :<Key>B: ToStartProc() \n \
965    :<Key>b: BackwardProc() \n \
966    :<Key>p: PauseProc() \n \
967    :<Key>d: DrawProc() \n \
968    :<Key>t: CallFlagProc() \n \
969    :<Key>i: Iconify() \n \
970    :<Key>c: Iconify() \n \
971    :<Key>v: FlipViewProc() \n \
972    <KeyDown>Control_L: BackwardProc() \n \
973    <KeyUp>Control_L: ForwardProc() \n \
974    <KeyDown>Control_R: BackwardProc() \n \
975    <KeyUp>Control_R: ForwardProc() \n \
976    Shift<Key>1: AskQuestionProc(\"Direct command\",\
977                                 \"Send to chess program:\",,1) \n \
978    Shift<Key>2: AskQuestionProc(\"Direct command\",\
979                                 \"Send to second chess program:\",,2) \n";
980
981 char boardTranslations[] =
982    "<Btn1Down>: HandleUserMove() \n \
983    <Btn1Up>: HandleUserMove() \n \
984    <Btn1Motion>: AnimateUserMove() \n \
985    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
986                  PieceMenuPopup(menuB) \n \
987    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
988                  PieceMenuPopup(menuW) \n \
989    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
990                  PieceMenuPopup(menuW) \n \
991    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
992                  PieceMenuPopup(menuB) \n";
993
994 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
995 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
996
997 char ICSInputTranslations[] =
998     "<Key>Return: EnterKeyProc() \n";
999
1000 String xboardResources[] = {
1001     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1002     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1003     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1004     NULL
1005   };
1006
1007
1008 /* Max possible square size */
1009 #define MAXSQSIZE 256
1010
1011 static int xpm_avail[MAXSQSIZE];
1012
1013 #ifdef HAVE_DIR_STRUCT
1014
1015 /* Extract piece size from filename */
1016 static int
1017 xpm_getsize(name, len, ext)
1018      char *name;
1019      int len;
1020      char *ext;
1021 {
1022     char *p, *d;
1023     char buf[10];
1024
1025     if (len < 4)
1026       return 0;
1027
1028     if ((p=strchr(name, '.')) == NULL ||
1029         StrCaseCmp(p+1, ext) != 0)
1030       return 0;
1031
1032     p = name + 3;
1033     d = buf;
1034
1035     while (*p && isdigit(*p))
1036       *(d++) = *(p++);
1037
1038     *d = 0;
1039     return atoi(buf);
1040 }
1041
1042 /* Setup xpm_avail */
1043 static int
1044 xpm_getavail(dirname, ext)
1045      char *dirname;
1046      char *ext;
1047 {
1048     DIR *dir;
1049     struct dirent *ent;
1050     int  i;
1051
1052     for (i=0; i<MAXSQSIZE; ++i)
1053       xpm_avail[i] = 0;
1054
1055     if (appData.debugMode)
1056       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1057
1058     dir = opendir(dirname);
1059     if (!dir)
1060       {
1061           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1062                   programName, dirname);
1063           exit(1);
1064       }
1065
1066     while ((ent=readdir(dir)) != NULL) {
1067         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1068         if (i > 0 && i < MAXSQSIZE)
1069           xpm_avail[i] = 1;
1070     }
1071
1072     closedir(dir);
1073
1074     return 0;
1075 }
1076
1077 void
1078 xpm_print_avail(fp, ext)
1079      FILE *fp;
1080      char *ext;
1081 {
1082     int i;
1083
1084     fprintf(fp, _("Available `%s' sizes:\n"), ext);
1085     for (i=1; i<MAXSQSIZE; ++i) {
1086         if (xpm_avail[i])
1087           printf("%d\n", i);
1088     }
1089 }
1090
1091 /* Return XPM piecesize closest to size */
1092 int
1093 xpm_closest_to(dirname, size, ext)
1094      char *dirname;
1095      int size;
1096      char *ext;
1097 {
1098     int i;
1099     int sm_diff = MAXSQSIZE;
1100     int sm_index = 0;
1101     int diff;
1102
1103     xpm_getavail(dirname, ext);
1104
1105     if (appData.debugMode)
1106       xpm_print_avail(stderr, ext);
1107
1108     for (i=1; i<MAXSQSIZE; ++i) {
1109         if (xpm_avail[i]) {
1110             diff = size - i;
1111             diff = (diff<0) ? -diff : diff;
1112             if (diff < sm_diff) {
1113                 sm_diff = diff;
1114                 sm_index = i;
1115             }
1116         }
1117     }
1118
1119     if (!sm_index) {
1120         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1121         exit(1);
1122     }
1123
1124     return sm_index;
1125 }
1126 #else   /* !HAVE_DIR_STRUCT */
1127 /* If we are on a system without a DIR struct, we can't
1128    read the directory, so we can't collect a list of
1129    filenames, etc., so we can't do any size-fitting. */
1130 int
1131 xpm_closest_to(dirname, size, ext)
1132      char *dirname;
1133      int size;
1134      char *ext;
1135 {
1136     fprintf(stderr, _("\
1137 Warning: No DIR structure found on this system --\n\
1138          Unable to autosize for XPM/XIM pieces.\n\
1139    Please report this error to frankm@hiwaay.net.\n\
1140    Include system type & operating system in message.\n"));
1141     return size;
1142 }
1143 #endif /* HAVE_DIR_STRUCT */
1144
1145 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1146                              "magenta", "cyan", "white" };
1147 typedef struct {
1148     int attr, bg, fg;
1149 } TextColors;
1150 TextColors textColors[(int)NColorClasses];
1151
1152 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1153 static int
1154 parse_color(str, which)
1155      char *str;
1156      int which;
1157 {
1158     char *p, buf[100], *d;
1159     int i;
1160
1161     if (strlen(str) > 99)       /* watch bounds on buf */
1162       return -1;
1163
1164     p = str;
1165     d = buf;
1166     for (i=0; i<which; ++i) {
1167         p = strchr(p, ',');
1168         if (!p)
1169           return -1;
1170         ++p;
1171     }
1172
1173     /* Could be looking at something like:
1174        black, , 1
1175        .. in which case we want to stop on a comma also */
1176     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1177       ++p;
1178
1179     if (*p == ',') {
1180         return -1;              /* Use default for empty field */
1181     }
1182
1183     if (which == 2 || isdigit(*p))
1184       return atoi(p);
1185
1186     while (*p && isalpha(*p))
1187       *(d++) = *(p++);
1188
1189     *d = 0;
1190
1191     for (i=0; i<8; ++i) {
1192         if (!StrCaseCmp(buf, cnames[i]))
1193           return which? (i+40) : (i+30);
1194     }
1195     if (!StrCaseCmp(buf, "default")) return -1;
1196
1197     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1198     return -2;
1199 }
1200
1201 static int
1202 parse_cpair(cc, str)
1203      ColorClass cc;
1204      char *str;
1205 {
1206     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1207         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1208                 programName, str);
1209         return -1;
1210     }
1211
1212     /* bg and attr are optional */
1213     textColors[(int)cc].bg = parse_color(str, 1);
1214     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1215         textColors[(int)cc].attr = 0;
1216     }
1217     return 0;
1218 }
1219
1220
1221 /* Arrange to catch delete-window events */
1222 Atom wm_delete_window;
1223 void
1224 CatchDeleteWindow(Widget w, String procname)
1225 {
1226   char buf[MSG_SIZ];
1227   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1228   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1229   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1230 }
1231
1232 void
1233 BoardToTop()
1234 {
1235   Arg args[16];
1236   XtSetArg(args[0], XtNiconic, False);
1237   XtSetValues(shellWidget, args, 1);
1238
1239   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1240 }
1241
1242 //---------------------------------------------------------------------------------------------------------
1243 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1244 #define XBOARD True
1245 #define JAWS_ARGS
1246 #define CW_USEDEFAULT (1<<31)
1247 #define ICS_TEXT_MENU_SIZE 90
1248 #define SetCurrentDirectory chdir
1249 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1250 #define OPTCHAR "-"
1251 #define SEPCHAR " "
1252
1253 // these two must some day move to frontend.h, when they are implemented
1254 Boolean EvalGraphIsUp();
1255 Boolean MoveHistoryIsUp();
1256 Boolean GameListIsUp();
1257
1258 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1259 #include "args.h"
1260
1261 // front-end part of option handling
1262
1263 // [HGM] This platform-dependent table provides the location for storing the color info
1264 void *
1265 colorVariable[] = {
1266   &appData.whitePieceColor, 
1267   &appData.blackPieceColor, 
1268   &appData.lightSquareColor,
1269   &appData.darkSquareColor, 
1270   &appData.highlightSquareColor,
1271   &appData.premoveHighlightColor,
1272   NULL,
1273   NULL,
1274   NULL,
1275   NULL,
1276   NULL,
1277   NULL,
1278   NULL,
1279   NULL,
1280   NULL
1281 };
1282
1283 void
1284 ParseFont(char *name, int number)
1285 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1286   switch(number) {
1287     case 0: // CLOCK_FONT
1288         appData.clockFont = strdup(name);
1289       break;
1290     case 1: // MESSAGE_FONT
1291         appData.font = strdup(name);
1292       break;
1293     case 2: // COORD_FONT
1294         appData.coordFont = strdup(name);
1295       break;
1296     default:
1297       return;
1298   }
1299 }
1300
1301 void
1302 SetFontDefaults()
1303 { // only 2 fonts currently
1304   appData.clockFont = CLOCK_FONT_NAME;
1305   appData.coordFont = COORD_FONT_NAME;
1306   appData.font  =   DEFAULT_FONT_NAME;
1307 }
1308
1309 void
1310 CreateFonts()
1311 { // no-op, until we identify the code for this already in XBoard and move it here
1312 }
1313
1314 void
1315 ParseColor(int n, char *name)
1316 { // in XBoard, just copy the color-name string
1317   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1318 }
1319
1320 void
1321 ParseTextAttribs(ColorClass cc, char *s)
1322 {   
1323     (&appData.colorShout)[cc] = strdup(s);
1324 }
1325
1326 void
1327 ParseBoardSize(void *addr, char *name)
1328 {
1329     appData.boardSize = strdup(name);
1330 }
1331
1332 void
1333 LoadAllSounds()
1334 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1335 }
1336
1337 void
1338 SetCommPortDefaults()
1339 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1340 }
1341
1342 // [HGM] args: these three cases taken out to stay in front-end
1343 void
1344 SaveFontArg(FILE *f, ArgDescriptor *ad)
1345 {
1346   char *name;
1347   switch((int)ad->argLoc) {
1348     case 0: // CLOCK_FONT
1349         name = appData.clockFont;
1350       break;
1351     case 1: // MESSAGE_FONT
1352         name = appData.font;
1353       break;
1354     case 2: // COORD_FONT
1355         name = appData.coordFont;
1356       break;
1357     default:
1358       return;
1359   }
1360   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, name);
1361 }
1362
1363 void
1364 ExportSounds()
1365 { // nothing to do, as the sounds are at all times represented by their text-string names already
1366 }
1367
1368 void
1369 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1370 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1371         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1372 }
1373
1374 void
1375 SaveColor(FILE *f, ArgDescriptor *ad)
1376 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1377         if(colorVariable[(int)ad->argLoc])
1378         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1379 }
1380
1381 void
1382 SaveBoardSize(FILE *f, char *name, void *addr)
1383 { // wrapper to shield back-end from BoardSize & sizeInfo
1384   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1385 }
1386
1387 void
1388 ParseCommPortSettings(char *s)
1389 { // no such option in XBoard (yet)
1390 }
1391
1392 extern Widget engineOutputShell;
1393 extern Widget tagsShell, editTagsShell;
1394 void
1395 GetActualPlacement(Widget wg, WindowPlacement *wp)
1396 {
1397   Arg args[16];
1398   Dimension w, h;
1399   Position x, y;
1400   int i;
1401
1402   if(!wg) return;
1403   
1404     i = 0;
1405     XtSetArg(args[i], XtNx, &x); i++;
1406     XtSetArg(args[i], XtNy, &y); i++;
1407     XtSetArg(args[i], XtNwidth, &w); i++;
1408     XtSetArg(args[i], XtNheight, &h); i++;
1409     XtGetValues(wg, args, i);
1410     wp->x = x - 4;
1411     wp->y = y - 23;
1412     wp->height = h;
1413     wp->width = w;
1414 }
1415
1416 void
1417 GetWindowCoords()
1418 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1419   // In XBoard this will have to wait until awareness of window parameters is implemented
1420   GetActualPlacement(shellWidget, &wpMain);
1421   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1422   if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1423   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1424   if(commentShell) GetActualPlacement(commentShell, &wpComment);
1425   else             GetActualPlacement(editShell,    &wpComment);
1426   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1427   else      GetActualPlacement(editTagsShell, &wpTags);
1428 #if 0
1429   GetActualPlacement(hwndConsole, &wpConsole);
1430   GetActualPlacement(evalGraphDialog, &wpEvalGraph);
1431 #endif
1432 }
1433
1434 void
1435 PrintCommPortSettings(FILE *f, char *name)
1436 { // This option does not exist in XBoard
1437 }
1438
1439 int
1440 MySearchPath(char *installDir, char *name, char *fullname)
1441 { // just append installDir and name. Perhaps ExpandPath should be used here?
1442   name = ExpandPathName(name);
1443   if(name && name[0] == '/') strcpy(fullname, name); else {
1444     sprintf(fullname, "%s%c%s", installDir, '/', name);
1445   }
1446   return 1;
1447 }
1448
1449 int
1450 MyGetFullPathName(char *name, char *fullname)
1451 { // should use ExpandPath?
1452   name = ExpandPathName(name);
1453   strcpy(fullname, name);
1454   return 1;
1455 }
1456
1457 void
1458 EnsureOnScreen(int *x, int *y, int minX, int minY)
1459 {
1460   return;
1461 }
1462
1463 Boolean
1464 EvalGraphIsUp()
1465 {
1466   return False;
1467 }
1468
1469 int
1470 MainWindowUp()
1471 { // [HGM] args: allows testing if main window is realized from back-end
1472   return xBoardWindow != 0;
1473 }
1474
1475 void
1476 PopUpStartupDialog()
1477 {  // start menu not implemented in XBoard
1478 }
1479 char *
1480 ConvertToLine(int argc, char **argv)
1481 {
1482   static char line[128*1024], buf[1024];
1483   int i;
1484
1485   line[0] = NULLCHAR;
1486   for(i=1; i<argc; i++) {
1487     if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1488         && argv[i][0] != '{' )
1489          sprintf(buf, "{%s} ", argv[i]);
1490     else sprintf(buf, "%s ", argv[i]);
1491     strcat(line, buf);
1492   }
1493     line[strlen(line)-1] = NULLCHAR;
1494   return line;
1495 }
1496
1497 //--------------------------------------------------------------------------------------------
1498
1499 #ifdef IDSIZES
1500   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1501 #else
1502 #define BoardSize int
1503 void InitDrawingSizes(BoardSize boardSize, int flags)
1504 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1505     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1506     Arg args[16];
1507     XtGeometryResult gres;
1508     int i;
1509
1510     if(!formWidget) return;
1511
1512     /*
1513      * Enable shell resizing.
1514      */
1515     shellArgs[0].value = (XtArgVal) &w;
1516     shellArgs[1].value = (XtArgVal) &h;
1517     XtGetValues(shellWidget, shellArgs, 2);
1518
1519     shellArgs[4].value = 2*w; shellArgs[2].value = 10;
1520     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1521     XtSetValues(shellWidget, &shellArgs[2], 4);
1522
1523     XtSetArg(args[0], XtNdefaultDistance, &sep);
1524     XtGetValues(formWidget, args, 1);
1525
1526     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1527     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1528     CreateGrid();
1529
1530     XtSetArg(args[0], XtNwidth, boardWidth);
1531     XtSetArg(args[1], XtNheight, boardHeight);
1532     XtSetValues(boardWidget, args, 2);
1533
1534     timerWidth = (boardWidth - sep) / 2;
1535     XtSetArg(args[0], XtNwidth, timerWidth);
1536     XtSetValues(whiteTimerWidget, args, 1);
1537     XtSetValues(blackTimerWidget, args, 1);
1538
1539     XawFormDoLayout(formWidget, False);
1540
1541     if (appData.titleInWindow) {
1542         i = 0;
1543         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1544         XtSetArg(args[i], XtNheight, &h);  i++;
1545         XtGetValues(titleWidget, args, i);
1546         if (smallLayout) {
1547             w = boardWidth - 2*bor;
1548         } else {
1549             XtSetArg(args[0], XtNwidth, &w);
1550             XtGetValues(menuBarWidget, args, 1);
1551             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1552         }
1553
1554         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1555         if (gres != XtGeometryYes && appData.debugMode) {
1556             fprintf(stderr,
1557                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1558                     programName, gres, w, h, wr, hr);
1559         }
1560     }
1561
1562     XawFormDoLayout(formWidget, True);
1563
1564     /*
1565      * Inhibit shell resizing.
1566      */
1567     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW;
1568     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1569     shellArgs[4].value = shellArgs[2].value = w;
1570     shellArgs[5].value = shellArgs[3].value = h;
1571     XtSetValues(shellWidget, &shellArgs[0], 6);
1572
1573     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1574     // (only for xpm)
1575     if(useImages) {
1576       for(i=0; i<4; i++) {
1577         int p;
1578         for(p=0; p<=(int)WhiteKing; p++)
1579            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1580         if(gameInfo.variant == VariantShogi) {
1581            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1582            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1583            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1584            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1585            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1586         }
1587 #ifdef GOTHIC
1588         if(gameInfo.variant == VariantGothic) {
1589            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1590         }
1591 #endif
1592 #if !HAVE_LIBXPM
1593         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1594         for(p=0; p<=(int)WhiteKing; p++)
1595            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1596         if(gameInfo.variant == VariantShogi) {
1597            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1598            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1599            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1600            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1601            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1602         }
1603 #ifdef GOTHIC
1604         if(gameInfo.variant == VariantGothic) {
1605            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1606         }
1607 #endif
1608 #endif
1609       }
1610     } else {
1611       for(i=0; i<2; i++) {
1612         int p;
1613         for(p=0; p<=(int)WhiteKing; p++)
1614            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1615         if(gameInfo.variant == VariantShogi) {
1616            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1617            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1618            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1619            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1620            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1621         }
1622 #ifdef GOTHIC
1623         if(gameInfo.variant == VariantGothic) {
1624            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1625         }
1626 #endif
1627       }
1628     }
1629 #if HAVE_LIBXPM
1630     CreateAnimVars();
1631 #endif
1632 }
1633 #endif
1634
1635 void EscapeExpand(char *p, char *q)
1636 {       // [HGM] initstring: routine to shape up string arguments
1637         while(*p++ = *q++) if(p[-1] == '\\')
1638             switch(*q++) {
1639                 case 'n': p[-1] = '\n'; break;
1640                 case 'r': p[-1] = '\r'; break;
1641                 case 't': p[-1] = '\t'; break;
1642                 case '\\': p[-1] = '\\'; break;
1643                 case 0: *p = 0; return;
1644                 default: p[-1] = q[-1]; break;
1645             }
1646 }
1647
1648 int
1649 main(argc, argv)
1650      int argc;
1651      char **argv;
1652 {
1653     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1654     XSetWindowAttributes window_attributes;
1655     Arg args[16];
1656     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1657     XrmValue vFrom, vTo;
1658     XtGeometryResult gres;
1659     char *p;
1660     XrmDatabase xdb;
1661     int forceMono = False;
1662 //define INDIRECTION
1663 #ifdef INDIRECTION
1664     // [HGM] before anything else, expand any indirection files amongst options
1665     char *argvCopy[1000]; // 1000 seems enough
1666     char newArgs[10000];  // holds actual characters
1667     int k = 0;
1668
1669     srandom(time(0)); // [HGM] book: make random truly random
1670
1671     j = 0;
1672     for(i=0; i<argc; i++) {
1673         if(j >= 1000-2) { printf(_("too many arguments\n")); exit(-1); }
1674 //fprintf(stderr, "arg %s\n", argv[i]);
1675         if(argv[i][0] != '@') argvCopy[j++] = argv[i]; else {
1676             char c;
1677             FILE *f = fopen(argv[i]+1, "rb");
1678             if(f == NULL) { fprintf(stderr, _("ignore %s\n"), argv[i]); continue; } // do not expand non-existing
1679             argvCopy[j++] = newArgs + k; // get ready for first argument from file
1680             while((c = fgetc(f)) != EOF) { // each line of file inserts 1 argument in the list
1681                 if(c == '\n') {
1682                     if(j >= 1000-2) { printf(_("too many arguments\n")); exit(-1); }
1683                     newArgs[k++] = 0;  // terminate current arg
1684                     if(k >= 10000-1) { printf(_("too long arguments\n")); exit(-1); }
1685                     argvCopy[j++] = newArgs + k; // get ready for next
1686                 } else {
1687                     if(k >= 10000-1) { printf(_("too long arguments\n")); exit(-1); }
1688                     newArgs[k++] = c;
1689                 }
1690             }
1691             newArgs[k] = 0;
1692             j--;
1693             fclose(f);
1694         }
1695     }
1696     argvCopy[j] = NULL;
1697     argv = argvCopy;
1698     argc = j;
1699 #endif
1700
1701     setbuf(stdout, NULL);
1702     setbuf(stderr, NULL);
1703     debugFP = stderr;
1704
1705     programName = strrchr(argv[0], '/');
1706     if (programName == NULL)
1707       programName = argv[0];
1708     else
1709       programName++;
1710
1711 #ifdef ENABLE_NLS
1712     XtSetLanguageProc(NULL, NULL, NULL);
1713     bindtextdomain(PACKAGE, LOCALEDIR);
1714     textdomain(PACKAGE);
1715 #endif
1716
1717     shellWidget =
1718       XtAppInitialize(&appContext, "XBoard", shellOptions,
1719                       XtNumber(shellOptions),
1720                       &argc, argv, xboardResources, NULL, 0);
1721     appData.boardSize = "";
1722     InitAppData(ConvertToLine(argc, argv));
1723     p = getenv("HOME");
1724     if (p == NULL) p = "/tmp";
1725     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1726     gameCopyFilename = (char*) malloc(i);
1727     gamePasteFilename = (char*) malloc(i);
1728     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1729     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1730
1731     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1732                               clientResources, XtNumber(clientResources),
1733                               NULL, 0);
1734
1735     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1736         static char buf[MSG_SIZ];
1737         EscapeExpand(buf, appData.initString);
1738         appData.initString = strdup(buf);
1739         EscapeExpand(buf, appData.secondInitString);
1740         appData.secondInitString = strdup(buf);
1741         EscapeExpand(buf, appData.firstComputerString);
1742         appData.firstComputerString = strdup(buf);
1743         EscapeExpand(buf, appData.secondComputerString);
1744         appData.secondComputerString = strdup(buf);
1745     }
1746
1747     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1748         chessDir = ".";
1749     } else {
1750         if (chdir(chessDir) != 0) {
1751             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1752             perror(chessDir);
1753             exit(1);
1754         }
1755     }
1756
1757     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1758         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1759         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1760            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1761            exit(errno);
1762         }
1763         setbuf(debugFP, NULL);
1764     }
1765
1766     /* [HGM,HR] make sure board size is acceptable */
1767     if(appData.NrFiles > BOARD_FILES ||
1768        appData.NrRanks > BOARD_RANKS   )
1769          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1770
1771 #if !HIGHDRAG
1772     /* This feature does not work; animation needs a rewrite */
1773     appData.highlightDragging = FALSE;
1774 #endif
1775     InitBackEnd1();
1776
1777     xDisplay = XtDisplay(shellWidget);
1778     xScreen = DefaultScreen(xDisplay);
1779     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1780
1781         gameInfo.variant = StringToVariant(appData.variant);
1782         InitPosition(FALSE);
1783
1784 #ifdef IDSIZE
1785     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1786 #else
1787     if (isdigit(appData.boardSize[0])) {
1788         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1789                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1790                    &fontPxlSize, &smallLayout, &tinyLayout);
1791         if (i == 0) {
1792             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1793                     programName, appData.boardSize);
1794             exit(2);
1795         }
1796         if (i < 7) {
1797             /* Find some defaults; use the nearest known size */
1798             SizeDefaults *szd, *nearest;
1799             int distance = 99999;
1800             nearest = szd = sizeDefaults;
1801             while (szd->name != NULL) {
1802                 if (abs(szd->squareSize - squareSize) < distance) {
1803                     nearest = szd;
1804                     distance = abs(szd->squareSize - squareSize);
1805                     if (distance == 0) break;
1806                 }
1807                 szd++;
1808             }
1809             if (i < 2) lineGap = nearest->lineGap;
1810             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1811             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1812             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1813             if (i < 6) smallLayout = nearest->smallLayout;
1814             if (i < 7) tinyLayout = nearest->tinyLayout;
1815         }
1816     } else {
1817         SizeDefaults *szd = sizeDefaults;
1818         if (*appData.boardSize == NULLCHAR) {
1819             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1820                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1821               szd++;
1822             }
1823             if (szd->name == NULL) szd--;
1824             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1825         } else {
1826             while (szd->name != NULL &&
1827                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1828             if (szd->name == NULL) {
1829                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1830                         programName, appData.boardSize);
1831                 exit(2);
1832             }
1833         }
1834         squareSize = szd->squareSize;
1835         lineGap = szd->lineGap;
1836         clockFontPxlSize = szd->clockFontPxlSize;
1837         coordFontPxlSize = szd->coordFontPxlSize;
1838         fontPxlSize = szd->fontPxlSize;
1839         smallLayout = szd->smallLayout;
1840         tinyLayout = szd->tinyLayout;
1841     }
1842
1843     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1844     if (strlen(appData.pixmapDirectory) > 0) {
1845         p = ExpandPathName(appData.pixmapDirectory);
1846         if (!p) {
1847             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1848                    appData.pixmapDirectory);
1849             exit(1);
1850         }
1851         if (appData.debugMode) {
1852           fprintf(stderr, _("\
1853 XBoard square size (hint): %d\n\
1854 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1855         }
1856         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1857         if (appData.debugMode) {
1858             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1859         }
1860     }
1861
1862     /* [HR] height treated separately (hacked) */
1863     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1864     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1865     if (appData.showJail == 1) {
1866         /* Jail on top and bottom */
1867         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1868         XtSetArg(boardArgs[2], XtNheight,
1869                  boardHeight + 2*(lineGap + squareSize));
1870     } else if (appData.showJail == 2) {
1871         /* Jail on sides */
1872         XtSetArg(boardArgs[1], XtNwidth,
1873                  boardWidth + 2*(lineGap + squareSize));
1874         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1875     } else {
1876         /* No jail */
1877         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1878         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1879     }
1880
1881     /*
1882      * Determine what fonts to use.
1883      */
1884     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1885     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1886     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1887     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1888     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1889     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1890     appData.font = FindFont(appData.font, fontPxlSize);
1891     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1892     countFontStruct = XQueryFont(xDisplay, countFontID);
1893 //    appData.font = FindFont(appData.font, fontPxlSize);
1894
1895     xdb = XtDatabase(xDisplay);
1896     XrmPutStringResource(&xdb, "*font", appData.font);
1897
1898     /*
1899      * Detect if there are not enough colors available and adapt.
1900      */
1901     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1902       appData.monoMode = True;
1903     }
1904
1905     if (!appData.monoMode) {
1906         vFrom.addr = (caddr_t) appData.lightSquareColor;
1907         vFrom.size = strlen(appData.lightSquareColor);
1908         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1909         if (vTo.addr == NULL) {
1910           appData.monoMode = True;
1911           forceMono = True;
1912         } else {
1913           lightSquareColor = *(Pixel *) vTo.addr;
1914         }
1915     }
1916     if (!appData.monoMode) {
1917         vFrom.addr = (caddr_t) appData.darkSquareColor;
1918         vFrom.size = strlen(appData.darkSquareColor);
1919         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1920         if (vTo.addr == NULL) {
1921           appData.monoMode = True;
1922           forceMono = True;
1923         } else {
1924           darkSquareColor = *(Pixel *) vTo.addr;
1925         }
1926     }
1927     if (!appData.monoMode) {
1928         vFrom.addr = (caddr_t) appData.whitePieceColor;
1929         vFrom.size = strlen(appData.whitePieceColor);
1930         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1931         if (vTo.addr == NULL) {
1932           appData.monoMode = True;
1933           forceMono = True;
1934         } else {
1935           whitePieceColor = *(Pixel *) vTo.addr;
1936         }
1937     }
1938     if (!appData.monoMode) {
1939         vFrom.addr = (caddr_t) appData.blackPieceColor;
1940         vFrom.size = strlen(appData.blackPieceColor);
1941         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1942         if (vTo.addr == NULL) {
1943           appData.monoMode = True;
1944           forceMono = True;
1945         } else {
1946           blackPieceColor = *(Pixel *) vTo.addr;
1947         }
1948     }
1949
1950     if (!appData.monoMode) {
1951         vFrom.addr = (caddr_t) appData.highlightSquareColor;
1952         vFrom.size = strlen(appData.highlightSquareColor);
1953         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1954         if (vTo.addr == NULL) {
1955           appData.monoMode = True;
1956           forceMono = True;
1957         } else {
1958           highlightSquareColor = *(Pixel *) vTo.addr;
1959         }
1960     }
1961
1962     if (!appData.monoMode) {
1963         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1964         vFrom.size = strlen(appData.premoveHighlightColor);
1965         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1966         if (vTo.addr == NULL) {
1967           appData.monoMode = True;
1968           forceMono = True;
1969         } else {
1970           premoveHighlightColor = *(Pixel *) vTo.addr;
1971         }
1972     }
1973
1974     if (forceMono) {
1975       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1976               programName);
1977       
1978       if (appData.bitmapDirectory == NULL ||
1979               appData.bitmapDirectory[0] == NULLCHAR)
1980             appData.bitmapDirectory = DEF_BITMAP_DIR;
1981     }
1982
1983     if (appData.lowTimeWarning && !appData.monoMode) {
1984       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1985       vFrom.size = strlen(appData.lowTimeWarningColor);
1986       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1987       if (vTo.addr == NULL) 
1988                 appData.monoMode = True;
1989       else
1990                 lowTimeWarningColor = *(Pixel *) vTo.addr;
1991     }
1992
1993     if (appData.monoMode && appData.debugMode) {
1994         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1995                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1996                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1997     }
1998
1999     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2000         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2001         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2002         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2003         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2004         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2005         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2006         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2007         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2008         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2009       {
2010           if (appData.colorize) {
2011               fprintf(stderr,
2012                       _("%s: can't parse color names; disabling colorization\n"),
2013                       programName);
2014           }
2015           appData.colorize = FALSE;
2016       }
2017     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2018     textColors[ColorNone].attr = 0;
2019
2020     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2021
2022     /*
2023      * widget hierarchy
2024      */
2025     if (tinyLayout) {
2026         layoutName = "tinyLayout";
2027     } else if (smallLayout) {
2028         layoutName = "smallLayout";
2029     } else {
2030         layoutName = "normalLayout";
2031     }
2032     /* Outer layoutWidget is there only to provide a name for use in
2033        resources that depend on the layout style */
2034     layoutWidget =
2035       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2036                             layoutArgs, XtNumber(layoutArgs));
2037     formWidget =
2038       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2039                             formArgs, XtNumber(formArgs));
2040     XtSetArg(args[0], XtNdefaultDistance, &sep);
2041     XtGetValues(formWidget, args, 1);
2042
2043     j = 0;
2044     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2045     XtSetArg(args[0], XtNtop,    XtChainTop);
2046     XtSetArg(args[1], XtNbottom, XtChainTop);
2047     XtSetArg(args[2], XtNright,  XtChainLeft);
2048     XtSetValues(menuBarWidget, args, 3);
2049
2050     widgetList[j++] = whiteTimerWidget =
2051       XtCreateWidget("whiteTime", labelWidgetClass,
2052                      formWidget, timerArgs, XtNumber(timerArgs));
2053     XtSetArg(args[0], XtNfont, clockFontStruct);
2054     XtSetArg(args[1], XtNtop,    XtChainTop);
2055     XtSetArg(args[2], XtNbottom, XtChainTop);
2056     XtSetValues(whiteTimerWidget, args, 3);
2057
2058     widgetList[j++] = blackTimerWidget =
2059       XtCreateWidget("blackTime", labelWidgetClass,
2060                      formWidget, timerArgs, XtNumber(timerArgs));
2061     XtSetArg(args[0], XtNfont, clockFontStruct);
2062     XtSetArg(args[1], XtNtop,    XtChainTop);
2063     XtSetArg(args[2], XtNbottom, XtChainTop);
2064     XtSetValues(blackTimerWidget, args, 3);
2065
2066     if (appData.titleInWindow) {
2067         widgetList[j++] = titleWidget =
2068           XtCreateWidget("title", labelWidgetClass, formWidget,
2069                          titleArgs, XtNumber(titleArgs));
2070         XtSetArg(args[0], XtNtop,    XtChainTop);
2071         XtSetArg(args[1], XtNbottom, XtChainTop);
2072         XtSetValues(titleWidget, args, 2);
2073     }
2074
2075     if (appData.showButtonBar) {
2076       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2077       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2078       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2079       XtSetArg(args[2], XtNtop,    XtChainTop);
2080       XtSetArg(args[3], XtNbottom, XtChainTop);
2081       XtSetValues(buttonBarWidget, args, 4);
2082     }
2083
2084     widgetList[j++] = messageWidget =
2085       XtCreateWidget("message", labelWidgetClass, formWidget,
2086                      messageArgs, XtNumber(messageArgs));
2087     XtSetArg(args[0], XtNtop,    XtChainTop);
2088     XtSetArg(args[1], XtNbottom, XtChainTop);
2089     XtSetValues(messageWidget, args, 2);
2090
2091     widgetList[j++] = boardWidget =
2092       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2093                      XtNumber(boardArgs));
2094
2095     XtManageChildren(widgetList, j);
2096
2097     timerWidth = (boardWidth - sep) / 2;
2098     XtSetArg(args[0], XtNwidth, timerWidth);
2099     XtSetValues(whiteTimerWidget, args, 1);
2100     XtSetValues(blackTimerWidget, args, 1);
2101
2102     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2103     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2104     XtGetValues(whiteTimerWidget, args, 2);
2105
2106     if (appData.showButtonBar) {
2107       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2108       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2109       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2110     }
2111
2112     /*
2113      * formWidget uses these constraints but they are stored
2114      * in the children.
2115      */
2116     i = 0;
2117     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2118     XtSetValues(menuBarWidget, args, i);
2119     if (appData.titleInWindow) {
2120         if (smallLayout) {
2121             i = 0;
2122             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2123             XtSetValues(whiteTimerWidget, args, i);
2124             i = 0;
2125             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2126             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2127             XtSetValues(blackTimerWidget, args, i);
2128             i = 0;
2129             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2130             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2131             XtSetValues(titleWidget, args, i);
2132             i = 0;
2133             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2134             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2135             XtSetValues(messageWidget, args, i);
2136             if (appData.showButtonBar) {
2137               i = 0;
2138               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2139               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2140               XtSetValues(buttonBarWidget, args, i);
2141             }
2142         } else {
2143             i = 0;
2144             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2145             XtSetValues(whiteTimerWidget, args, i);
2146             i = 0;
2147             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2148             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2149             XtSetValues(blackTimerWidget, args, i);
2150             i = 0;
2151             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2152             XtSetValues(titleWidget, args, i);
2153             i = 0;
2154             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2155             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2156             XtSetValues(messageWidget, args, i);
2157             if (appData.showButtonBar) {
2158               i = 0;
2159               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2160               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2161               XtSetValues(buttonBarWidget, args, i);
2162             }
2163         }
2164     } else {
2165         i = 0;
2166         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2167         XtSetValues(whiteTimerWidget, args, i);
2168         i = 0;
2169         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2170         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2171         XtSetValues(blackTimerWidget, args, i);
2172         i = 0;
2173         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2174         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2175         XtSetValues(messageWidget, args, i);
2176         if (appData.showButtonBar) {
2177           i = 0;
2178           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2179           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2180           XtSetValues(buttonBarWidget, args, i);
2181         }
2182     }
2183     i = 0;
2184     XtSetArg(args[0], XtNfromVert, messageWidget);
2185     XtSetArg(args[1], XtNtop,    XtChainTop);
2186     XtSetArg(args[2], XtNbottom, XtChainBottom);
2187     XtSetArg(args[3], XtNleft,   XtChainLeft);
2188     XtSetArg(args[4], XtNright,  XtChainRight);
2189     XtSetValues(boardWidget, args, 5);
2190
2191     XtRealizeWidget(shellWidget);
2192
2193     if(wpMain.x > 0) {
2194       XtSetArg(args[0], XtNx, wpMain.x);
2195       XtSetArg(args[1], XtNy, wpMain.y);
2196       XtSetValues(shellWidget, args, 2);
2197     }
2198
2199     /*
2200      * Correct the width of the message and title widgets.
2201      * It is not known why some systems need the extra fudge term.
2202      * The value "2" is probably larger than needed.
2203      */
2204     XawFormDoLayout(formWidget, False);
2205
2206 #define WIDTH_FUDGE 2
2207     i = 0;
2208     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2209     XtSetArg(args[i], XtNheight, &h);  i++;
2210     XtGetValues(messageWidget, args, i);
2211     if (appData.showButtonBar) {
2212       i = 0;
2213       XtSetArg(args[i], XtNwidth, &w);  i++;
2214       XtGetValues(buttonBarWidget, args, i);
2215       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2216     } else {
2217       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2218     }
2219
2220     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2221     if (gres != XtGeometryYes && appData.debugMode) {
2222       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2223               programName, gres, w, h, wr, hr);
2224     }
2225
2226     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2227     /* The size used for the child widget in layout lags one resize behind
2228        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2229     w--;
2230     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2231     if (gres != XtGeometryYes && appData.debugMode) {
2232       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2233               programName, gres, w, h, wr, hr);
2234     }
2235     /* !! end hack */
2236     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2237     XtSetArg(args[1], XtNright, XtChainRight);
2238     XtSetValues(messageWidget, args, 2);
2239
2240     if (appData.titleInWindow) {
2241         i = 0;
2242         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2243         XtSetArg(args[i], XtNheight, &h);  i++;
2244         XtGetValues(titleWidget, args, i);
2245         if (smallLayout) {
2246             w = boardWidth - 2*bor;
2247         } else {
2248             XtSetArg(args[0], XtNwidth, &w);
2249             XtGetValues(menuBarWidget, args, 1);
2250             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2251         }
2252
2253         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2254         if (gres != XtGeometryYes && appData.debugMode) {
2255             fprintf(stderr,
2256                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2257                     programName, gres, w, h, wr, hr);
2258         }
2259     }
2260     XawFormDoLayout(formWidget, True);
2261
2262     xBoardWindow = XtWindow(boardWidget);
2263
2264     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2265     //       not need to go into InitDrawingSizes().
2266 #endif
2267
2268     /*
2269      * Create X checkmark bitmap and initialize option menu checks.
2270      */
2271     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2272                checkmark_bits, checkmark_width, checkmark_height);
2273     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2274     if (appData.alwaysPromoteToQueen) {
2275         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2276                     args, 1);
2277     }
2278     if (appData.animateDragging) {
2279         XtSetValues(XtNameToWidget(menuBarWidget,
2280                                    "menuOptions.Animate Dragging"),
2281                     args, 1);
2282     }
2283     if (appData.animate) {
2284         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2285                     args, 1);
2286     }
2287     if (appData.autoComment) {
2288         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2289                     args, 1);
2290     }
2291     if (appData.autoCallFlag) {
2292         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2293                     args, 1);
2294     }
2295     if (appData.autoFlipView) {
2296         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2297                     args, 1);
2298     }
2299     if (appData.autoObserve) {
2300         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2301                     args, 1);
2302     }
2303     if (appData.autoRaiseBoard) {
2304         XtSetValues(XtNameToWidget(menuBarWidget,
2305                                    "menuOptions.Auto Raise Board"), args, 1);
2306     }
2307     if (appData.autoSaveGames) {
2308         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2309                     args, 1);
2310     }
2311     if (appData.saveGameFile[0] != NULLCHAR) {
2312         /* Can't turn this off from menu */
2313         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2314                     args, 1);
2315         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2316                        False);
2317
2318     }
2319     if (appData.blindfold) {
2320         XtSetValues(XtNameToWidget(menuBarWidget,
2321                                    "menuOptions.Blindfold"), args, 1);
2322     }
2323     if (appData.flashCount > 0) {
2324         XtSetValues(XtNameToWidget(menuBarWidget,
2325                                    "menuOptions.Flash Moves"),
2326                     args, 1);
2327     }
2328     if (appData.getMoveList) {
2329         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2330                     args, 1);
2331     }
2332 #if HIGHDRAG
2333     if (appData.highlightDragging) {
2334         XtSetValues(XtNameToWidget(menuBarWidget,
2335                                    "menuOptions.Highlight Dragging"),
2336                     args, 1);
2337     }
2338 #endif
2339     if (appData.highlightLastMove) {
2340         XtSetValues(XtNameToWidget(menuBarWidget,
2341                                    "menuOptions.Highlight Last Move"),
2342                     args, 1);
2343     }
2344     if (appData.icsAlarm) {
2345         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2346                     args, 1);
2347     }
2348     if (appData.ringBellAfterMoves) {
2349         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2350                     args, 1);
2351     }
2352     if (appData.oldSaveStyle) {
2353         XtSetValues(XtNameToWidget(menuBarWidget,
2354                                    "menuOptions.Old Save Style"), args, 1);
2355     }
2356     if (appData.periodicUpdates) {
2357         XtSetValues(XtNameToWidget(menuBarWidget,
2358                                    "menuOptions.Periodic Updates"), args, 1);
2359     }
2360     if (appData.ponderNextMove) {
2361         XtSetValues(XtNameToWidget(menuBarWidget,
2362                                    "menuOptions.Ponder Next Move"), args, 1);
2363     }
2364     if (appData.popupExitMessage) {
2365         XtSetValues(XtNameToWidget(menuBarWidget,
2366                                    "menuOptions.Popup Exit Message"), args, 1);
2367     }
2368     if (appData.popupMoveErrors) {
2369         XtSetValues(XtNameToWidget(menuBarWidget,
2370                                    "menuOptions.Popup Move Errors"), args, 1);
2371     }
2372     if (appData.premove) {
2373         XtSetValues(XtNameToWidget(menuBarWidget,
2374                                    "menuOptions.Premove"), args, 1);
2375     }
2376     if (appData.quietPlay) {
2377         XtSetValues(XtNameToWidget(menuBarWidget,
2378                                    "menuOptions.Quiet Play"), args, 1);
2379     }
2380     if (appData.showCoords) {
2381         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2382                     args, 1);
2383     }
2384     if (appData.hideThinkingFromHuman) {
2385         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2386                     args, 1);
2387     }
2388     if (appData.testLegality) {
2389         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2390                     args, 1);
2391     }
2392     if (saveSettingsOnExit) {
2393         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2394                     args, 1);
2395     }
2396
2397     /*
2398      * Create an icon.
2399      */
2400     ReadBitmap(&wIconPixmap, "icon_white.bm",
2401                icon_white_bits, icon_white_width, icon_white_height);
2402     ReadBitmap(&bIconPixmap, "icon_black.bm",
2403                icon_black_bits, icon_black_width, icon_black_height);
2404     iconPixmap = wIconPixmap;
2405     i = 0;
2406     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2407     XtSetValues(shellWidget, args, i);
2408
2409     /*
2410      * Create a cursor for the board widget.
2411      */
2412     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2413     XChangeWindowAttributes(xDisplay, xBoardWindow,
2414                             CWCursor, &window_attributes);
2415
2416     /*
2417      * Inhibit shell resizing.
2418      */
2419     shellArgs[0].value = (XtArgVal) &w;
2420     shellArgs[1].value = (XtArgVal) &h;
2421     XtGetValues(shellWidget, shellArgs, 2);
2422     shellArgs[4].value = shellArgs[2].value = w;
2423     shellArgs[5].value = shellArgs[3].value = h;
2424     XtSetValues(shellWidget, &shellArgs[2], 4);
2425     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2426     marginH =  h - boardHeight;
2427
2428     CatchDeleteWindow(shellWidget, "QuitProc");
2429
2430     CreateGCs();
2431     CreateGrid();
2432 #if HAVE_LIBXPM
2433     if (appData.bitmapDirectory[0] != NULLCHAR) {
2434       CreatePieces();
2435     } else {
2436       CreateXPMPieces();
2437     }
2438 #else
2439     CreateXIMPieces();
2440     /* Create regular pieces */
2441     if (!useImages) CreatePieces();
2442 #endif
2443
2444     CreatePieceMenus();
2445
2446     if (appData.animate || appData.animateDragging)
2447       CreateAnimVars();
2448
2449     XtAugmentTranslations(formWidget,
2450                           XtParseTranslationTable(globalTranslations));
2451     XtAugmentTranslations(boardWidget,
2452                           XtParseTranslationTable(boardTranslations));
2453     XtAugmentTranslations(whiteTimerWidget,
2454                           XtParseTranslationTable(whiteTranslations));
2455     XtAugmentTranslations(blackTimerWidget,
2456                           XtParseTranslationTable(blackTranslations));
2457
2458     /* Why is the following needed on some versions of X instead
2459      * of a translation? */
2460     XtAddEventHandler(boardWidget, ExposureMask, False,
2461                       (XtEventHandler) EventProc, NULL);
2462     /* end why */
2463
2464     InitBackEnd2();
2465
2466     if (errorExitStatus == -1) {
2467         if (appData.icsActive) {
2468             /* We now wait until we see "login:" from the ICS before
2469                sending the logon script (problems with timestamp otherwise) */
2470             /*ICSInitScript();*/
2471             if (appData.icsInputBox) ICSInputBoxPopUp();
2472         }
2473
2474     #ifdef SIGWINCH
2475     signal(SIGWINCH, TermSizeSigHandler);
2476     #endif
2477         signal(SIGINT, IntSigHandler);
2478         signal(SIGTERM, IntSigHandler);
2479         if (*appData.cmailGameName != NULLCHAR) {
2480             signal(SIGUSR1, CmailSigHandler);
2481         }
2482     }
2483     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2484     InitPosition(TRUE);
2485
2486     XtAppMainLoop(appContext);
2487     if (appData.debugMode) fclose(debugFP); // [DM] debug
2488     return 0;
2489 }
2490
2491 void
2492 ShutDownFrontEnd()
2493 {
2494     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2495         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2496     }
2497     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2498     unlink(gameCopyFilename);
2499     unlink(gamePasteFilename);
2500 }
2501
2502 RETSIGTYPE TermSizeSigHandler(int sig)
2503 {
2504     update_ics_width();
2505 }
2506
2507 RETSIGTYPE
2508 IntSigHandler(sig)
2509      int sig;
2510 {
2511     ExitEvent(sig);
2512 }
2513
2514 RETSIGTYPE
2515 CmailSigHandler(sig)
2516      int sig;
2517 {
2518     int dummy = 0;
2519     int error;
2520
2521     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2522
2523     /* Activate call-back function CmailSigHandlerCallBack()             */
2524     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2525
2526     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2527 }
2528
2529 void
2530 CmailSigHandlerCallBack(isr, closure, message, count, error)
2531      InputSourceRef isr;
2532      VOIDSTAR closure;
2533      char *message;
2534      int count;
2535      int error;
2536 {
2537     BoardToTop();
2538     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2539 }
2540 /**** end signal code ****/
2541
2542
2543 void
2544 ICSInitScript()
2545 {
2546     FILE *f;
2547     char buf[MSG_SIZ];
2548     char *p;
2549
2550     f = fopen(appData.icsLogon, "r");
2551     if (f == NULL) {
2552         p = getenv("HOME");
2553         if (p != NULL) {
2554             strcpy(buf, p);
2555             strcat(buf, "/");
2556             strcat(buf, appData.icsLogon);
2557             f = fopen(buf, "r");
2558         }
2559     }
2560     if (f != NULL)
2561       ProcessICSInitScript(f);
2562 }
2563
2564 void
2565 ResetFrontEnd()
2566 {
2567     CommentPopDown();
2568     EditCommentPopDown();
2569     TagsPopDown();
2570     return;
2571 }
2572
2573 typedef struct {
2574     char *name;
2575     Boolean value;
2576 } Enables;
2577
2578 void
2579 GreyRevert(grey)
2580      Boolean grey;
2581 {
2582     Widget w;
2583     if (!menuBarWidget) return;
2584     w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2585     if (w == NULL) {
2586       DisplayError("menuStep.Revert", 0);
2587     } else {
2588       XtSetSensitive(w, !grey);
2589     }
2590 }
2591
2592 void
2593 SetMenuEnables(enab)
2594      Enables *enab;
2595 {
2596   Widget w;
2597   if (!menuBarWidget) return;
2598   while (enab->name != NULL) {
2599     w = XtNameToWidget(menuBarWidget, enab->name);
2600     if (w == NULL) {
2601       DisplayError(enab->name, 0);
2602     } else {
2603       XtSetSensitive(w, enab->value);
2604     }
2605     enab++;
2606   }
2607 }
2608
2609 Enables icsEnables[] = {
2610     { "menuFile.Mail Move", False },
2611     { "menuFile.Reload CMail Message", False },
2612     { "menuMode.Machine Black", False },
2613     { "menuMode.Machine White", False },
2614     { "menuMode.Analysis Mode", False },
2615     { "menuMode.Analyze File", False },
2616     { "menuMode.Two Machines", False },
2617 #ifndef ZIPPY
2618     { "menuHelp.Hint", False },
2619     { "menuHelp.Book", False },
2620     { "menuStep.Move Now", False },
2621     { "menuOptions.Periodic Updates", False },
2622     { "menuOptions.Hide Thinking", False },
2623     { "menuOptions.Ponder Next Move", False },
2624 #endif
2625     { NULL, False }
2626 };
2627
2628 Enables ncpEnables[] = {
2629     { "menuFile.Mail Move", False },
2630     { "menuFile.Reload CMail Message", False },
2631     { "menuMode.Machine White", False },
2632     { "menuMode.Machine Black", False },
2633     { "menuMode.Analysis Mode", False },
2634     { "menuMode.Analyze File", False },
2635     { "menuMode.Two Machines", False },
2636     { "menuMode.ICS Client", False },
2637     { "menuMode.ICS Input Box", False },
2638     { "Action", False },
2639     { "menuStep.Revert", False },
2640     { "menuStep.Move Now", False },
2641     { "menuStep.Retract Move", False },
2642     { "menuOptions.Auto Comment", False },
2643     { "menuOptions.Auto Flag", False },
2644     { "menuOptions.Auto Flip View", False },
2645     { "menuOptions.Auto Observe", False },
2646     { "menuOptions.Auto Raise Board", False },
2647     { "menuOptions.Get Move List", False },
2648     { "menuOptions.ICS Alarm", False },
2649     { "menuOptions.Move Sound", False },
2650     { "menuOptions.Quiet Play", False },
2651     { "menuOptions.Hide Thinking", False },
2652     { "menuOptions.Periodic Updates", False },
2653     { "menuOptions.Ponder Next Move", False },
2654     { "menuHelp.Hint", False },
2655     { "menuHelp.Book", False },
2656     { NULL, False }
2657 };
2658
2659 Enables gnuEnables[] = {
2660     { "menuMode.ICS Client", False },
2661     { "menuMode.ICS Input Box", False },
2662     { "menuAction.Accept", False },
2663     { "menuAction.Decline", False },
2664     { "menuAction.Rematch", False },
2665     { "menuAction.Adjourn", False },
2666     { "menuAction.Stop Examining", False },
2667     { "menuAction.Stop Observing", False },
2668     { "menuStep.Revert", False },
2669     { "menuOptions.Auto Comment", False },
2670     { "menuOptions.Auto Observe", False },
2671     { "menuOptions.Auto Raise Board", False },
2672     { "menuOptions.Get Move List", False },
2673     { "menuOptions.Premove", False },
2674     { "menuOptions.Quiet Play", False },
2675
2676     /* The next two options rely on SetCmailMode being called *after*    */
2677     /* SetGNUMode so that when GNU is being used to give hints these     */
2678     /* menu options are still available                                  */
2679
2680     { "menuFile.Mail Move", False },
2681     { "menuFile.Reload CMail Message", False },
2682     { NULL, False }
2683 };
2684
2685 Enables cmailEnables[] = {
2686     { "Action", True },
2687     { "menuAction.Call Flag", False },
2688     { "menuAction.Draw", True },
2689     { "menuAction.Adjourn", False },
2690     { "menuAction.Abort", False },
2691     { "menuAction.Stop Observing", False },
2692     { "menuAction.Stop Examining", False },
2693     { "menuFile.Mail Move", True },
2694     { "menuFile.Reload CMail Message", True },
2695     { NULL, False }
2696 };
2697
2698 Enables trainingOnEnables[] = {
2699   { "menuMode.Edit Comment", False },
2700   { "menuMode.Pause", False },
2701   { "menuStep.Forward", False },
2702   { "menuStep.Backward", False },
2703   { "menuStep.Forward to End", False },
2704   { "menuStep.Back to Start", False },
2705   { "menuStep.Move Now", False },
2706   { "menuStep.Truncate Game", False },
2707   { NULL, False }
2708 };
2709
2710 Enables trainingOffEnables[] = {
2711   { "menuMode.Edit Comment", True },
2712   { "menuMode.Pause", True },
2713   { "menuStep.Forward", True },
2714   { "menuStep.Backward", True },
2715   { "menuStep.Forward to End", True },
2716   { "menuStep.Back to Start", True },
2717   { "menuStep.Move Now", True },
2718   { "menuStep.Truncate Game", True },
2719   { NULL, False }
2720 };
2721
2722 Enables machineThinkingEnables[] = {
2723   { "menuFile.Load Game", False },
2724   { "menuFile.Load Next Game", False },
2725   { "menuFile.Load Previous Game", False },
2726   { "menuFile.Reload Same Game", False },
2727   { "menuFile.Paste Game", False },
2728   { "menuFile.Load Position", False },
2729   { "menuFile.Load Next Position", False },
2730   { "menuFile.Load Previous Position", False },
2731   { "menuFile.Reload Same Position", False },
2732   { "menuFile.Paste Position", False },
2733   { "menuMode.Machine White", False },
2734   { "menuMode.Machine Black", False },
2735   { "menuMode.Two Machines", False },
2736   { "menuStep.Retract Move", False },
2737   { NULL, False }
2738 };
2739
2740 Enables userThinkingEnables[] = {
2741   { "menuFile.Load Game", True },
2742   { "menuFile.Load Next Game", True },
2743   { "menuFile.Load Previous Game", True },
2744   { "menuFile.Reload Same Game", True },
2745   { "menuFile.Paste Game", True },
2746   { "menuFile.Load Position", True },
2747   { "menuFile.Load Next Position", True },
2748   { "menuFile.Load Previous Position", True },
2749   { "menuFile.Reload Same Position", True },
2750   { "menuFile.Paste Position", True },
2751   { "menuMode.Machine White", True },
2752   { "menuMode.Machine Black", True },
2753   { "menuMode.Two Machines", True },
2754   { "menuStep.Retract Move", True },
2755   { NULL, False }
2756 };
2757
2758 void SetICSMode()
2759 {
2760   SetMenuEnables(icsEnables);
2761
2762 #ifdef ZIPPY
2763   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2764      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2765 #endif
2766 }
2767
2768 void
2769 SetNCPMode()
2770 {
2771   SetMenuEnables(ncpEnables);
2772 }
2773
2774 void
2775 SetGNUMode()
2776 {
2777   SetMenuEnables(gnuEnables);
2778 }
2779
2780 void
2781 SetCmailMode()
2782 {
2783   SetMenuEnables(cmailEnables);
2784 }
2785
2786 void
2787 SetTrainingModeOn()
2788 {
2789   SetMenuEnables(trainingOnEnables);
2790   if (appData.showButtonBar) {
2791     XtSetSensitive(buttonBarWidget, False);
2792   }
2793   CommentPopDown();
2794 }
2795
2796 void
2797 SetTrainingModeOff()
2798 {
2799   SetMenuEnables(trainingOffEnables);
2800   if (appData.showButtonBar) {
2801     XtSetSensitive(buttonBarWidget, True);
2802   }
2803 }
2804
2805 void
2806 SetUserThinkingEnables()
2807 {
2808   if (appData.noChessProgram) return;
2809   SetMenuEnables(userThinkingEnables);
2810 }
2811
2812 void
2813 SetMachineThinkingEnables()
2814 {
2815   if (appData.noChessProgram) return;
2816   SetMenuEnables(machineThinkingEnables);
2817   switch (gameMode) {
2818   case MachinePlaysBlack:
2819   case MachinePlaysWhite:
2820   case TwoMachinesPlay:
2821     XtSetSensitive(XtNameToWidget(menuBarWidget,
2822                                   ModeToWidgetName(gameMode)), True);
2823     break;
2824   default:
2825     break;
2826   }
2827 }
2828
2829 #define Abs(n) ((n)<0 ? -(n) : (n))
2830
2831 /*
2832  * Find a font that matches "pattern" that is as close as
2833  * possible to the targetPxlSize.  Prefer fonts that are k
2834  * pixels smaller to fonts that are k pixels larger.  The
2835  * pattern must be in the X Consortium standard format,
2836  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2837  * The return value should be freed with XtFree when no
2838  * longer needed.
2839  */
2840 char *FindFont(pattern, targetPxlSize)
2841      char *pattern;
2842      int targetPxlSize;
2843 {
2844     char **fonts, *p, *best, *scalable, *scalableTail;
2845     int i, j, nfonts, minerr, err, pxlSize;
2846
2847 #ifdef ENABLE_NLS
2848     char **missing_list;
2849     int missing_count;
2850     char *def_string, *base_fnt_lst, strInt[3];
2851     XFontSet fntSet;
2852     XFontStruct **fnt_list;
2853
2854     base_fnt_lst = calloc(1, strlen(pattern) + 3);
2855     sprintf(strInt, "%d", targetPxlSize);
2856     p = strstr(pattern, "--");
2857     strncpy(base_fnt_lst, pattern, p - pattern + 2);
2858     strcat(base_fnt_lst, strInt);
2859     strcat(base_fnt_lst, strchr(p + 2, '-'));
2860
2861     if ((fntSet = XCreateFontSet(xDisplay,
2862                                  base_fnt_lst,
2863                                  &missing_list,
2864                                  &missing_count,
2865                                  &def_string)) == NULL) {
2866
2867        fprintf(stderr, _("Unable to create font set.\n"));
2868        exit (2);
2869     }
2870
2871     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2872 #else
2873     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2874     if (nfonts < 1) {
2875         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2876                 programName, pattern);
2877         exit(2);
2878     }
2879 #endif
2880
2881     best = fonts[0];
2882     scalable = NULL;
2883     minerr = 999999;
2884     for (i=0; i<nfonts; i++) {
2885         j = 0;
2886         p = fonts[i];
2887         if (*p != '-') continue;
2888         while (j < 7) {
2889             if (*p == NULLCHAR) break;
2890             if (*p++ == '-') j++;
2891         }
2892         if (j < 7) continue;
2893         pxlSize = atoi(p);
2894         if (pxlSize == 0) {
2895             scalable = fonts[i];
2896             scalableTail = p;
2897         } else {
2898             err = pxlSize - targetPxlSize;
2899             if (Abs(err) < Abs(minerr) ||
2900                 (minerr > 0 && err < 0 && -err == minerr)) {
2901                 best = fonts[i];
2902                 minerr = err;
2903             }
2904         }
2905     }
2906     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2907         /* If the error is too big and there is a scalable font,
2908            use the scalable font. */
2909         int headlen = scalableTail - scalable;
2910         p = (char *) XtMalloc(strlen(scalable) + 10);
2911         while (isdigit(*scalableTail)) scalableTail++;
2912         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2913     } else {
2914         p = (char *) XtMalloc(strlen(best) + 1);
2915         strcpy(p, best);
2916     }
2917     if (appData.debugMode) {
2918         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2919                 pattern, targetPxlSize, p);
2920     }
2921 #ifdef ENABLE_NLS
2922     if (missing_count > 0)
2923        XFreeStringList(missing_list);
2924     XFreeFontSet(xDisplay, fntSet);
2925 #else
2926      XFreeFontNames(fonts);
2927 #endif
2928     return p;
2929 }
2930
2931 void CreateGCs()
2932 {
2933     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2934       | GCBackground | GCFunction | GCPlaneMask;
2935     XGCValues gc_values;
2936     GC copyInvertedGC;
2937
2938     gc_values.plane_mask = AllPlanes;
2939     gc_values.line_width = lineGap;
2940     gc_values.line_style = LineSolid;
2941     gc_values.function = GXcopy;
2942
2943     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2944     gc_values.background = XBlackPixel(xDisplay, xScreen);
2945     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2946
2947     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2948     gc_values.background = XWhitePixel(xDisplay, xScreen);
2949     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2950     XSetFont(xDisplay, coordGC, coordFontID);
2951
2952     // [HGM] make font for holdings counts (white on black0
2953     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2954     gc_values.background = XBlackPixel(xDisplay, xScreen);
2955     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
2956     XSetFont(xDisplay, countGC, countFontID);
2957
2958     if (appData.monoMode) {
2959         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2960         gc_values.background = XWhitePixel(xDisplay, xScreen);
2961         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2962
2963         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2964         gc_values.background = XBlackPixel(xDisplay, xScreen);
2965         lightSquareGC = wbPieceGC
2966           = XtGetGC(shellWidget, value_mask, &gc_values);
2967
2968         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2969         gc_values.background = XWhitePixel(xDisplay, xScreen);
2970         darkSquareGC = bwPieceGC
2971           = XtGetGC(shellWidget, value_mask, &gc_values);
2972
2973         if (DefaultDepth(xDisplay, xScreen) == 1) {
2974             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2975             gc_values.function = GXcopyInverted;
2976             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2977             gc_values.function = GXcopy;
2978             if (XBlackPixel(xDisplay, xScreen) == 1) {
2979                 bwPieceGC = darkSquareGC;
2980                 wbPieceGC = copyInvertedGC;
2981             } else {
2982                 bwPieceGC = copyInvertedGC;
2983                 wbPieceGC = lightSquareGC;
2984             }
2985         }
2986     } else {
2987         gc_values.foreground = highlightSquareColor;
2988         gc_values.background = highlightSquareColor;
2989         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2990
2991         gc_values.foreground = premoveHighlightColor;
2992         gc_values.background = premoveHighlightColor;
2993         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2994
2995         gc_values.foreground = lightSquareColor;
2996         gc_values.background = darkSquareColor;
2997         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2998
2999         gc_values.foreground = darkSquareColor;
3000         gc_values.background = lightSquareColor;
3001         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3002
3003         gc_values.foreground = jailSquareColor;
3004         gc_values.background = jailSquareColor;
3005         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3006
3007         gc_values.foreground = whitePieceColor;
3008         gc_values.background = darkSquareColor;
3009         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3010
3011         gc_values.foreground = whitePieceColor;
3012         gc_values.background = lightSquareColor;
3013         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3014
3015         gc_values.foreground = whitePieceColor;
3016         gc_values.background = jailSquareColor;
3017         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3018
3019         gc_values.foreground = blackPieceColor;
3020         gc_values.background = darkSquareColor;
3021         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3022
3023         gc_values.foreground = blackPieceColor;
3024         gc_values.background = lightSquareColor;
3025         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3026
3027         gc_values.foreground = blackPieceColor;
3028         gc_values.background = jailSquareColor;
3029         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3030     }
3031 }
3032
3033 void loadXIM(xim, xmask, filename, dest, mask)
3034      XImage *xim;
3035      XImage *xmask;
3036      char *filename;
3037      Pixmap *dest;
3038      Pixmap *mask;
3039 {
3040     int x, y, w, h, p;
3041     FILE *fp;
3042     Pixmap temp;
3043     XGCValues   values;
3044     GC maskGC;
3045
3046     fp = fopen(filename, "rb");
3047     if (!fp) {
3048         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3049         exit(1);
3050     }
3051
3052     w = fgetc(fp);
3053     h = fgetc(fp);
3054
3055     for (y=0; y<h; ++y) {
3056         for (x=0; x<h; ++x) {
3057             p = fgetc(fp);
3058
3059             switch (p) {
3060               case 0:
3061                 XPutPixel(xim, x, y, blackPieceColor);
3062                 if (xmask)
3063                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3064                 break;
3065               case 1:
3066                 XPutPixel(xim, x, y, darkSquareColor);
3067                 if (xmask)
3068                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3069                 break;
3070               case 2:
3071                 XPutPixel(xim, x, y, whitePieceColor);
3072                 if (xmask)
3073                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3074                 break;
3075               case 3:
3076                 XPutPixel(xim, x, y, lightSquareColor);
3077                 if (xmask)
3078                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3079                 break;
3080             }
3081         }
3082     }
3083
3084     /* create Pixmap of piece */
3085     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3086                           w, h, xim->depth);
3087     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3088               0, 0, 0, 0, w, h);
3089
3090     /* create Pixmap of clipmask
3091        Note: We assume the white/black pieces have the same
3092              outline, so we make only 6 masks. This is okay
3093              since the XPM clipmask routines do the same. */
3094     if (xmask) {
3095       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3096                             w, h, xim->depth);
3097       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3098               0, 0, 0, 0, w, h);
3099
3100       /* now create the 1-bit version */
3101       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3102                           w, h, 1);
3103
3104       values.foreground = 1;
3105       values.background = 0;
3106
3107       /* Don't use XtGetGC, not read only */
3108       maskGC = XCreateGC(xDisplay, *mask,
3109                     GCForeground | GCBackground, &values);
3110       XCopyPlane(xDisplay, temp, *mask, maskGC,
3111                   0, 0, squareSize, squareSize, 0, 0, 1);
3112       XFreePixmap(xDisplay, temp);
3113     }
3114 }
3115
3116
3117 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3118
3119 void CreateXIMPieces()
3120 {
3121     int piece, kind;
3122     char buf[MSG_SIZ];
3123     u_int ss;
3124     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3125     XImage *ximtemp;
3126
3127     ss = squareSize;
3128
3129     /* The XSynchronize calls were copied from CreatePieces.
3130        Not sure if needed, but can't hurt */
3131     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3132                                      buffering bug */
3133
3134     /* temp needed by loadXIM() */
3135     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3136                  0, 0, ss, ss, AllPlanes, XYPixmap);
3137
3138     if (strlen(appData.pixmapDirectory) == 0) {
3139       useImages = 0;
3140     } else {
3141         useImages = 1;
3142         if (appData.monoMode) {
3143           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3144                             0, 2);
3145           ExitEvent(2);
3146         }
3147         fprintf(stderr, _("\nLoading XIMs...\n"));
3148         /* Load pieces */
3149         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3150             fprintf(stderr, "%d", piece+1);
3151             for (kind=0; kind<4; kind++) {
3152                 fprintf(stderr, ".");
3153                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3154                         ExpandPathName(appData.pixmapDirectory),
3155                         piece <= (int) WhiteKing ? "" : "w",
3156                         pieceBitmapNames[piece],
3157                         ximkind[kind], ss);
3158                 ximPieceBitmap[kind][piece] =
3159                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3160                             0, 0, ss, ss, AllPlanes, XYPixmap);
3161                 if (appData.debugMode)
3162                   fprintf(stderr, _("(File:%s:) "), buf);
3163                 loadXIM(ximPieceBitmap[kind][piece],
3164                         ximtemp, buf,
3165                         &(xpmPieceBitmap2[kind][piece]),
3166                         &(ximMaskPm2[piece]));
3167                 if(piece <= (int)WhiteKing)
3168                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3169             }
3170             fprintf(stderr," ");
3171         }
3172         /* Load light and dark squares */
3173         /* If the LSQ and DSQ pieces don't exist, we will
3174            draw them with solid squares. */
3175         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3176         if (access(buf, 0) != 0) {
3177             useImageSqs = 0;
3178         } else {
3179             useImageSqs = 1;
3180             fprintf(stderr, _("light square "));
3181             ximLightSquare=
3182               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3183                         0, 0, ss, ss, AllPlanes, XYPixmap);
3184             if (appData.debugMode)
3185               fprintf(stderr, _("(File:%s:) "), buf);
3186
3187             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3188             fprintf(stderr, _("dark square "));
3189             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3190                     ExpandPathName(appData.pixmapDirectory), ss);
3191             if (appData.debugMode)
3192               fprintf(stderr, _("(File:%s:) "), buf);
3193             ximDarkSquare=
3194               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3195                         0, 0, ss, ss, AllPlanes, XYPixmap);
3196             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3197             xpmJailSquare = xpmLightSquare;
3198         }
3199         fprintf(stderr, _("Done.\n"));
3200     }
3201     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3202 }
3203
3204 #if HAVE_LIBXPM
3205 void CreateXPMPieces()
3206 {
3207     int piece, kind, r;
3208     char buf[MSG_SIZ];
3209     u_int ss = squareSize;
3210     XpmAttributes attr;
3211     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3212     XpmColorSymbol symbols[4];
3213
3214     /* The XSynchronize calls were copied from CreatePieces.
3215        Not sure if needed, but can't hurt */
3216     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3217
3218     /* Setup translations so piece colors match square colors */
3219     symbols[0].name = "light_piece";
3220     symbols[0].value = appData.whitePieceColor;
3221     symbols[1].name = "dark_piece";
3222     symbols[1].value = appData.blackPieceColor;
3223     symbols[2].name = "light_square";
3224     symbols[2].value = appData.lightSquareColor;
3225     symbols[3].name = "dark_square";
3226     symbols[3].value = appData.darkSquareColor;
3227
3228     attr.valuemask = XpmColorSymbols;
3229     attr.colorsymbols = symbols;
3230     attr.numsymbols = 4;
3231
3232     if (appData.monoMode) {
3233       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3234                         0, 2);
3235       ExitEvent(2);
3236     }
3237     if (strlen(appData.pixmapDirectory) == 0) {
3238         XpmPieces* pieces = builtInXpms;
3239         useImages = 1;
3240         /* Load pieces */
3241         while (pieces->size != squareSize && pieces->size) pieces++;
3242         if (!pieces->size) {
3243           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3244           exit(1);
3245         }
3246         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3247             for (kind=0; kind<4; kind++) {
3248
3249                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3250                                                pieces->xpm[piece][kind],
3251                                                &(xpmPieceBitmap2[kind][piece]),
3252                                                NULL, &attr)) != 0) {
3253                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3254                           r, buf);
3255                   exit(1);
3256                 }
3257                 if(piece <= (int) WhiteKing)
3258                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3259             }
3260         }
3261         useImageSqs = 0;
3262         xpmJailSquare = xpmLightSquare;
3263     } else {
3264         useImages = 1;
3265
3266         fprintf(stderr, _("\nLoading XPMs...\n"));
3267
3268         /* Load pieces */
3269         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3270             fprintf(stderr, "%d ", piece+1);
3271             for (kind=0; kind<4; kind++) {
3272               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3273                         ExpandPathName(appData.pixmapDirectory),
3274                         piece > (int) WhiteKing ? "w" : "",
3275                         pieceBitmapNames[piece],
3276                         xpmkind[kind], ss);
3277                 if (appData.debugMode) {
3278                     fprintf(stderr, _("(File:%s:) "), buf);
3279                 }
3280                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3281                                            &(xpmPieceBitmap2[kind][piece]),
3282                                            NULL, &attr)) != 0) {
3283                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3284                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3285                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3286                                 ExpandPathName(appData.pixmapDirectory),
3287                                 xpmkind[kind], ss);
3288                         if (appData.debugMode) {
3289                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3290                         }
3291                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3292                                                 &(xpmPieceBitmap2[kind][piece]),
3293                                                 NULL, &attr);
3294                     }
3295                     if (r != 0) {
3296                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3297                                 r, buf);
3298                         exit(1);
3299                     }
3300                 }
3301                 if(piece <= (int) WhiteKing) 
3302                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3303             }
3304         }
3305         /* Load light and dark squares */
3306         /* If the LSQ and DSQ pieces don't exist, we will
3307            draw them with solid squares. */
3308         fprintf(stderr, _("light square "));
3309         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3310         if (access(buf, 0) != 0) {
3311             useImageSqs = 0;
3312         } else {
3313             useImageSqs = 1;
3314             if (appData.debugMode)
3315               fprintf(stderr, _("(File:%s:) "), buf);
3316
3317             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3318                                        &xpmLightSquare, NULL, &attr)) != 0) {
3319                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3320                 exit(1);
3321             }
3322             fprintf(stderr, _("dark square "));
3323             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3324                     ExpandPathName(appData.pixmapDirectory), ss);
3325             if (appData.debugMode) {
3326                 fprintf(stderr, _("(File:%s:) "), buf);
3327             }
3328             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3329                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3330                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3331                 exit(1);
3332             }
3333         }
3334         xpmJailSquare = xpmLightSquare;
3335         fprintf(stderr, _("Done.\n"));
3336     }
3337     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3338                                       buffering bug */
3339 }
3340 #endif /* HAVE_LIBXPM */
3341
3342 #if HAVE_LIBXPM
3343 /* No built-in bitmaps */
3344 void CreatePieces()
3345 {
3346     int piece, kind;
3347     char buf[MSG_SIZ];
3348     u_int ss = squareSize;
3349
3350     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3351                                      buffering bug */
3352
3353     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3354         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3355             sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3356                     pieceBitmapNames[piece],
3357                     ss, kind == SOLID ? 's' : 'o');
3358             ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3359             if(piece <= (int)WhiteKing)
3360                 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3361         }
3362     }
3363
3364     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3365                                       buffering bug */
3366 }
3367 #else
3368 /* With built-in bitmaps */
3369 void CreatePieces()
3370 {
3371     BuiltInBits* bib = builtInBits;
3372     int piece, kind;
3373     char buf[MSG_SIZ];
3374     u_int ss = squareSize;
3375
3376     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3377                                      buffering bug */
3378
3379     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3380
3381     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3382         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3383             sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3384                     pieceBitmapNames[piece],
3385                     ss, kind == SOLID ? 's' : 'o');
3386             ReadBitmap(&pieceBitmap2[kind][piece], buf,
3387                        bib->bits[kind][piece], ss, ss);
3388             if(piece <= (int)WhiteKing)
3389                 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3390         }
3391     }
3392
3393     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3394                                       buffering bug */
3395 }
3396 #endif
3397
3398 void ReadBitmap(pm, name, bits, wreq, hreq)
3399      Pixmap *pm;
3400      String name;
3401      unsigned char bits[];
3402      u_int wreq, hreq;
3403 {
3404     int x_hot, y_hot;
3405     u_int w, h;
3406     int errcode;
3407     char msg[MSG_SIZ], fullname[MSG_SIZ];
3408
3409     if (*appData.bitmapDirectory != NULLCHAR) {
3410         strcpy(fullname, appData.bitmapDirectory);
3411         strcat(fullname, "/");
3412         strcat(fullname, name);
3413         errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3414                                   &w, &h, pm, &x_hot, &y_hot);
3415     fprintf(stderr, "load %s\n", name);
3416         if (errcode != BitmapSuccess) {
3417             switch (errcode) {
3418               case BitmapOpenFailed:
3419                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3420                 break;
3421               case BitmapFileInvalid:
3422                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3423                 break;
3424               case BitmapNoMemory:
3425                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3426                         fullname);
3427                 break;
3428               default:
3429                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3430                         errcode, fullname);
3431                 break;
3432             }
3433             fprintf(stderr, _("%s: %s...using built-in\n"),
3434                     programName, msg);
3435         } else if (w != wreq || h != hreq) {
3436             fprintf(stderr,
3437                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3438                     programName, fullname, w, h, wreq, hreq);
3439         } else {
3440             return;
3441         }
3442     }
3443     if (bits != NULL) {
3444         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3445                                     wreq, hreq);
3446     }
3447 }
3448
3449 void CreateGrid()
3450 {
3451     int i, j;
3452
3453     if (lineGap == 0) return;
3454
3455     /* [HR] Split this into 2 loops for non-square boards. */
3456
3457     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3458         gridSegments[i].x1 = 0;
3459         gridSegments[i].x2 =
3460           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3461         gridSegments[i].y1 = gridSegments[i].y2
3462           = lineGap / 2 + (i * (squareSize + lineGap));
3463     }
3464
3465     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3466         gridSegments[j + i].y1 = 0;
3467         gridSegments[j + i].y2 =
3468           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3469         gridSegments[j + i].x1 = gridSegments[j + i].x2
3470           = lineGap / 2 + (j * (squareSize + lineGap));
3471     }
3472 }
3473
3474 static void MenuBarSelect(w, addr, index)
3475      Widget w;
3476      caddr_t addr;
3477      caddr_t index;
3478 {
3479     XtActionProc proc = (XtActionProc) addr;
3480
3481     (proc)(NULL, NULL, NULL, NULL);
3482 }
3483
3484 void CreateMenuBarPopup(parent, name, mb)
3485      Widget parent;
3486      String name;
3487      Menu *mb;
3488 {
3489     int j;
3490     Widget menu, entry;
3491     MenuItem *mi;
3492     Arg args[16];
3493
3494     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3495                               parent, NULL, 0);
3496     j = 0;
3497     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3498     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3499     mi = mb->mi;
3500     while (mi->string != NULL) {
3501         if (strcmp(mi->string, "----") == 0) {
3502             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3503                                           menu, args, j);
3504         } else {
3505           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3506             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3507                                           menu, args, j+1);
3508             XtAddCallback(entry, XtNcallback,
3509                           (XtCallbackProc) MenuBarSelect,
3510                           (caddr_t) mi->proc);
3511         }
3512         mi++;
3513     }
3514 }
3515
3516 Widget CreateMenuBar(mb)
3517      Menu *mb;
3518 {
3519     int j;
3520     Widget anchor, menuBar;
3521     Arg args[16];
3522     char menuName[MSG_SIZ];
3523
3524     j = 0;
3525     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3526     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3527     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3528     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3529                              formWidget, args, j);
3530
3531     while (mb->name != NULL) {
3532         strcpy(menuName, "menu");
3533         strcat(menuName, mb->name);
3534         j = 0;
3535         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3536         if (tinyLayout) {
3537             char shortName[2];
3538             shortName[0] = _(mb->name)[0];
3539             shortName[1] = NULLCHAR;
3540             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3541         }
3542       else {
3543           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3544       }
3545
3546         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3547         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3548                                        menuBar, args, j);
3549         CreateMenuBarPopup(menuBar, menuName, mb);
3550         mb++;
3551     }
3552     return menuBar;
3553 }
3554
3555 Widget CreateButtonBar(mi)
3556      MenuItem *mi;
3557 {
3558     int j;
3559     Widget button, buttonBar;
3560     Arg args[16];
3561
3562     j = 0;
3563     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3564     if (tinyLayout) {
3565         XtSetArg(args[j], XtNhSpace, 0); j++;
3566     }
3567     XtSetArg(args[j], XtNborderWidth, 0); j++;
3568     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3569     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3570                                formWidget, args, j);
3571
3572     while (mi->string != NULL) {
3573         j = 0;
3574         if (tinyLayout) {
3575             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3576             XtSetArg(args[j], XtNborderWidth, 0); j++;
3577         }
3578       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3579         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3580                                        buttonBar, args, j);
3581         XtAddCallback(button, XtNcallback,
3582                       (XtCallbackProc) MenuBarSelect,
3583                       (caddr_t) mi->proc);
3584         mi++;
3585     }
3586     return buttonBar;
3587 }
3588
3589 Widget
3590 CreatePieceMenu(name, color)
3591      char *name;
3592      int color;
3593 {
3594     int i;
3595     Widget entry, menu;
3596     Arg args[16];
3597     ChessSquare selection;
3598
3599     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3600                               boardWidget, args, 0);
3601
3602     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3603         String item = pieceMenuStrings[color][i];
3604
3605         if (strcmp(item, "----") == 0) {
3606             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3607                                           menu, NULL, 0);
3608         } else {
3609           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3610             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3611                                 menu, args, 1);
3612             selection = pieceMenuTranslation[color][i];
3613             XtAddCallback(entry, XtNcallback,
3614                           (XtCallbackProc) PieceMenuSelect,
3615                           (caddr_t) selection);
3616             if (selection == WhitePawn || selection == BlackPawn) {
3617                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3618                 XtSetValues(menu, args, 1);
3619             }
3620         }
3621     }
3622     return menu;
3623 }
3624
3625 void
3626 CreatePieceMenus()
3627 {
3628     int i;
3629     Widget entry;
3630     Arg args[16];
3631     ChessSquare selection;
3632
3633     whitePieceMenu = CreatePieceMenu("menuW", 0);
3634     blackPieceMenu = CreatePieceMenu("menuB", 1);
3635
3636     XtRegisterGrabAction(PieceMenuPopup, True,
3637                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3638                          GrabModeAsync, GrabModeAsync);
3639
3640     XtSetArg(args[0], XtNlabel, _("Drop"));
3641     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3642                                   boardWidget, args, 1);
3643     for (i = 0; i < DROP_MENU_SIZE; i++) {
3644         String item = dropMenuStrings[i];
3645
3646         if (strcmp(item, "----") == 0) {
3647             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3648                                           dropMenu, NULL, 0);
3649         } else {
3650           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3651             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3652                                 dropMenu, args, 1);
3653             selection = dropMenuTranslation[i];
3654             XtAddCallback(entry, XtNcallback,
3655                           (XtCallbackProc) DropMenuSelect,
3656                           (caddr_t) selection);
3657         }
3658     }
3659 }
3660
3661 void SetupDropMenu()
3662 {
3663     int i, j, count;
3664     char label[32];
3665     Arg args[16];
3666     Widget entry;
3667     char* p;
3668
3669     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3670         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3671         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3672                    dmEnables[i].piece);
3673         XtSetSensitive(entry, p != NULL || !appData.testLegality
3674                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3675                                        && !appData.icsActive));
3676         count = 0;
3677         while (p && *p++ == dmEnables[i].piece) count++;
3678         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3679         j = 0;
3680         XtSetArg(args[j], XtNlabel, label); j++;
3681         XtSetValues(entry, args, j);
3682     }
3683 }
3684
3685 void PieceMenuPopup(w, event, params, num_params)
3686      Widget w;
3687      XEvent *event;
3688      String *params;
3689      Cardinal *num_params;
3690 {
3691     String whichMenu;
3692     if (event->type != ButtonPress) return;
3693     if (errorUp) ErrorPopDown();
3694     switch (gameMode) {
3695       case EditPosition:
3696       case IcsExamining:
3697         whichMenu = params[0];
3698         break;
3699       case IcsPlayingWhite:
3700       case IcsPlayingBlack:
3701       case EditGame:
3702       case MachinePlaysWhite:
3703       case MachinePlaysBlack:
3704         if (appData.testLegality &&
3705             gameInfo.variant != VariantBughouse &&
3706             gameInfo.variant != VariantCrazyhouse) return;
3707         SetupDropMenu();
3708         whichMenu = "menuD";
3709         break;
3710       default:
3711         return;
3712     }
3713
3714     if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
3715         ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
3716         pmFromX = pmFromY = -1;
3717         return;
3718     }
3719     if (flipView)
3720       pmFromX = BOARD_WIDTH - 1 - pmFromX;
3721     else
3722       pmFromY = BOARD_HEIGHT - 1 - pmFromY;
3723
3724     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3725 }
3726
3727 static void PieceMenuSelect(w, piece, junk)
3728      Widget w;
3729      ChessSquare piece;
3730      caddr_t junk;
3731 {
3732     if (pmFromX < 0 || pmFromY < 0) return;
3733     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3734 }
3735
3736 static void DropMenuSelect(w, piece, junk)
3737      Widget w;
3738      ChessSquare piece;
3739      caddr_t junk;
3740 {
3741     if (pmFromX < 0 || pmFromY < 0) return;
3742     DropMenuEvent(piece, pmFromX, pmFromY);
3743 }
3744
3745 void WhiteClock(w, event, prms, nprms)
3746      Widget w;
3747      XEvent *event;
3748      String *prms;
3749      Cardinal *nprms;
3750 {
3751     if (gameMode == EditPosition || gameMode == IcsExamining) {
3752         SetWhiteToPlayEvent();
3753     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3754         CallFlagEvent();
3755     }
3756 }
3757
3758 void BlackClock(w, event, prms, nprms)
3759      Widget w;
3760      XEvent *event;
3761      String *prms;
3762      Cardinal *nprms;
3763 {
3764     if (gameMode == EditPosition || gameMode == IcsExamining) {
3765         SetBlackToPlayEvent();
3766     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3767         CallFlagEvent();
3768     }
3769 }
3770
3771
3772 /*
3773  * If the user selects on a border boundary, return -1; if off the board,
3774  *   return -2.  Otherwise map the event coordinate to the square.
3775  */
3776 int EventToSquare(x, limit)
3777      int x;
3778 {
3779     if (x <= 0)
3780       return -2;
3781     if (x < lineGap)
3782       return -1;
3783     x -= lineGap;
3784     if ((x % (squareSize + lineGap)) >= squareSize)
3785       return -1;
3786     x /= (squareSize + lineGap);
3787     if (x >= limit)
3788       return -2;
3789     return x;
3790 }
3791
3792 static void do_flash_delay(msec)
3793      unsigned long msec;
3794 {
3795     TimeDelay(msec);
3796 }
3797
3798 static void drawHighlight(file, rank, gc)
3799      int file, rank;
3800      GC gc;
3801 {
3802     int x, y;
3803
3804     if (lineGap == 0 || appData.blindfold) return;
3805
3806     if (flipView) {
3807         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3808           (squareSize + lineGap);
3809         y = lineGap/2 + rank * (squareSize + lineGap);
3810     } else {
3811         x = lineGap/2 + file * (squareSize + lineGap);
3812         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3813           (squareSize + lineGap);
3814     }
3815
3816     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3817                    squareSize+lineGap, squareSize+lineGap);
3818 }
3819
3820 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3821 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3822
3823 void
3824 SetHighlights(fromX, fromY, toX, toY)
3825      int fromX, fromY, toX, toY;
3826 {
3827     if (hi1X != fromX || hi1Y != fromY) {
3828         if (hi1X >= 0 && hi1Y >= 0) {
3829             drawHighlight(hi1X, hi1Y, lineGC);
3830         }
3831         if (fromX >= 0 && fromY >= 0) {
3832             drawHighlight(fromX, fromY, highlineGC);
3833         }
3834     }
3835     if (hi2X != toX || hi2Y != toY) {
3836         if (hi2X >= 0 && hi2Y >= 0) {
3837             drawHighlight(hi2X, hi2Y, lineGC);
3838         }
3839         if (toX >= 0 && toY >= 0) {
3840             drawHighlight(toX, toY, highlineGC);
3841         }
3842     }
3843     hi1X = fromX;
3844     hi1Y = fromY;
3845     hi2X = toX;
3846     hi2Y = toY;
3847 }
3848
3849 void
3850 ClearHighlights()
3851 {
3852     SetHighlights(-1, -1, -1, -1);
3853 }
3854
3855
3856 void
3857 SetPremoveHighlights(fromX, fromY, toX, toY)
3858      int fromX, fromY, toX, toY;
3859 {
3860     if (pm1X != fromX || pm1Y != fromY) {
3861         if (pm1X >= 0 && pm1Y >= 0) {
3862             drawHighlight(pm1X, pm1Y, lineGC);
3863         }
3864         if (fromX >= 0 && fromY >= 0) {
3865             drawHighlight(fromX, fromY, prelineGC);
3866         }
3867     }
3868     if (pm2X != toX || pm2Y != toY) {
3869         if (pm2X >= 0 && pm2Y >= 0) {
3870             drawHighlight(pm2X, pm2Y, lineGC);
3871         }
3872         if (toX >= 0 && toY >= 0) {
3873             drawHighlight(toX, toY, prelineGC);
3874         }
3875     }
3876     pm1X = fromX;
3877     pm1Y = fromY;
3878     pm2X = toX;
3879     pm2Y = toY;
3880 }
3881
3882 void
3883 ClearPremoveHighlights()
3884 {
3885   SetPremoveHighlights(-1, -1, -1, -1);
3886 }
3887
3888 static void BlankSquare(x, y, color, piece, dest)
3889      int x, y, color;
3890      ChessSquare piece;
3891      Drawable dest;
3892 {
3893     if (useImages && useImageSqs) {
3894         Pixmap pm;
3895         switch (color) {
3896           case 1: /* light */
3897             pm = xpmLightSquare;
3898             break;
3899           case 0: /* dark */
3900             pm = xpmDarkSquare;
3901             break;
3902           case 2: /* neutral */
3903           default:
3904             pm = xpmJailSquare;
3905             break;
3906         }
3907         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3908                   squareSize, squareSize, x, y);
3909     } else {
3910         GC gc;
3911         switch (color) {
3912           case 1: /* light */
3913             gc = lightSquareGC;
3914             break;
3915           case 0: /* dark */
3916             gc = darkSquareGC;
3917             break;
3918           case 2: /* neutral */
3919           default:
3920             gc = jailSquareGC;
3921             break;
3922         }
3923         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
3924     }
3925 }
3926
3927 /*
3928    I split out the routines to draw a piece so that I could
3929    make a generic flash routine.
3930 */
3931 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
3932      ChessSquare piece;
3933      int square_color, x, y;
3934      Drawable dest;
3935 {
3936     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3937     switch (square_color) {
3938       case 1: /* light */
3939       case 2: /* neutral */
3940       default:
3941         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3942                   ? *pieceToOutline(piece)
3943                   : *pieceToSolid(piece),
3944                   dest, bwPieceGC, 0, 0,
3945                   squareSize, squareSize, x, y);
3946         break;
3947       case 0: /* dark */
3948         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3949                   ? *pieceToSolid(piece)
3950                   : *pieceToOutline(piece),
3951                   dest, wbPieceGC, 0, 0,
3952                   squareSize, squareSize, x, y);
3953         break;
3954     }
3955 }
3956
3957 static void monoDrawPiece(piece, square_color, x, y, dest)
3958      ChessSquare piece;
3959      int square_color, x, y;
3960      Drawable dest;
3961 {
3962     switch (square_color) {
3963       case 1: /* light */
3964       case 2: /* neutral */
3965       default:
3966         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3967                    ? *pieceToOutline(piece)
3968                    : *pieceToSolid(piece),
3969                    dest, bwPieceGC, 0, 0,
3970                    squareSize, squareSize, x, y, 1);
3971         break;
3972       case 0: /* dark */
3973         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3974                    ? *pieceToSolid(piece)
3975                    : *pieceToOutline(piece),
3976                    dest, wbPieceGC, 0, 0,
3977                    squareSize, squareSize, x, y, 1);
3978         break;
3979     }
3980 }
3981
3982 static void colorDrawPiece(piece, square_color, x, y, dest)
3983      ChessSquare piece;
3984      int square_color, x, y;
3985      Drawable dest;
3986 {
3987     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3988     switch (square_color) {
3989       case 1: /* light */
3990         XCopyPlane(xDisplay, *pieceToSolid(piece),
3991                    dest, (int) piece < (int) BlackPawn
3992                    ? wlPieceGC : blPieceGC, 0, 0,
3993                    squareSize, squareSize, x, y, 1);
3994         break;
3995       case 0: /* dark */
3996         XCopyPlane(xDisplay, *pieceToSolid(piece),
3997                    dest, (int) piece < (int) BlackPawn
3998                    ? wdPieceGC : bdPieceGC, 0, 0,
3999                    squareSize, squareSize, x, y, 1);
4000         break;
4001       case 2: /* neutral */
4002       default:
4003         XCopyPlane(xDisplay, *pieceToSolid(piece),
4004                    dest, (int) piece < (int) BlackPawn
4005                    ? wjPieceGC : bjPieceGC, 0, 0,
4006                    squareSize, squareSize, x, y, 1);
4007         break;
4008     }
4009 }
4010
4011 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4012      ChessSquare piece;
4013      int square_color, x, y;
4014      Drawable dest;
4015 {
4016     int kind;
4017
4018     switch (square_color) {
4019       case 1: /* light */
4020       case 2: /* neutral */
4021       default:
4022         if ((int)piece < (int) BlackPawn) {
4023             kind = 0;
4024         } else {
4025             kind = 2;
4026             piece -= BlackPawn;
4027         }
4028         break;
4029       case 0: /* dark */
4030         if ((int)piece < (int) BlackPawn) {
4031             kind = 1;
4032         } else {
4033             kind = 3;
4034             piece -= BlackPawn;
4035         }
4036         break;
4037     }
4038     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4039               dest, wlPieceGC, 0, 0,
4040               squareSize, squareSize, x, y);
4041 }
4042
4043 typedef void (*DrawFunc)();
4044
4045 DrawFunc ChooseDrawFunc()
4046 {
4047     if (appData.monoMode) {
4048         if (DefaultDepth(xDisplay, xScreen) == 1) {
4049             return monoDrawPiece_1bit;
4050         } else {
4051             return monoDrawPiece;
4052         }
4053     } else {
4054         if (useImages)
4055           return colorDrawPieceImage;
4056         else
4057           return colorDrawPiece;
4058     }
4059 }
4060
4061 /* [HR] determine square color depending on chess variant. */
4062 static int SquareColor(row, column)
4063      int row, column;
4064 {
4065     int square_color;
4066
4067     if (gameInfo.variant == VariantXiangqi) {
4068         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4069             square_color = 1;
4070         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4071             square_color = 0;
4072         } else if (row <= 4) {
4073             square_color = 0;
4074         } else {
4075             square_color = 1;
4076         }
4077     } else {
4078         square_color = ((column + row) % 2) == 1;
4079     }
4080
4081     /* [hgm] holdings: next line makes all holdings squares light */
4082     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4083
4084     return square_color;
4085 }
4086
4087 void DrawSquare(row, column, piece, do_flash)
4088      int row, column, do_flash;
4089      ChessSquare piece;
4090 {
4091     int square_color, x, y, direction, font_ascent, font_descent;
4092     int i;
4093     char string[2];
4094     XCharStruct overall;
4095     DrawFunc drawfunc;
4096     int flash_delay;
4097
4098     /* Calculate delay in milliseconds (2-delays per complete flash) */
4099     flash_delay = 500 / appData.flashRate;
4100
4101     if (flipView) {
4102         x = lineGap + ((BOARD_WIDTH-1)-column) *
4103           (squareSize + lineGap);
4104         y = lineGap + row * (squareSize + lineGap);
4105     } else {
4106         x = lineGap + column * (squareSize + lineGap);
4107         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4108           (squareSize + lineGap);
4109     }
4110
4111     square_color = SquareColor(row, column);
4112
4113     if ( // [HGM] holdings: blank out area between board and holdings
4114                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4115               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4116                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4117                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4118
4119                         // [HGM] print piece counts next to holdings
4120                         string[1] = NULLCHAR;
4121                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4122                             string[0] = '0' + piece;
4123                             XTextExtents(countFontStruct, string, 1, &direction,
4124                                  &font_ascent, &font_descent, &overall);
4125                             if (appData.monoMode) {
4126                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4127                                                  x + squareSize - overall.width - 2,
4128                                                  y + font_ascent + 1, string, 1);
4129                             } else {
4130                                 XDrawString(xDisplay, xBoardWindow, countGC,
4131                                             x + squareSize - overall.width - 2,
4132                                             y + font_ascent + 1, string, 1);
4133                             }
4134                         }
4135                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4136                             string[0] = '0' + piece;
4137                             XTextExtents(countFontStruct, string, 1, &direction,
4138                                          &font_ascent, &font_descent, &overall);
4139                             if (appData.monoMode) {
4140                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4141                                                  x + 2, y + font_ascent + 1, string, 1);
4142                             } else {
4143                                 XDrawString(xDisplay, xBoardWindow, countGC,
4144                                             x + 2, y + font_ascent + 1, string, 1);
4145                             }
4146                         }
4147     } else {
4148             if (piece == EmptySquare || appData.blindfold) {
4149                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4150             } else {
4151                         drawfunc = ChooseDrawFunc();
4152                         if (do_flash && appData.flashCount > 0) {
4153                             for (i=0; i<appData.flashCount; ++i) {
4154
4155                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4156                                         XSync(xDisplay, False);
4157                                         do_flash_delay(flash_delay);
4158
4159                                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4160                                         XSync(xDisplay, False);
4161                                         do_flash_delay(flash_delay);
4162                             }
4163                         }
4164                         drawfunc(piece, square_color, x, y, xBoardWindow);
4165         }
4166         }
4167
4168     string[1] = NULLCHAR;
4169     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4170                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4171         string[0] = 'a' + column - BOARD_LEFT;
4172         XTextExtents(coordFontStruct, string, 1, &direction,
4173                      &font_ascent, &font_descent, &overall);
4174         if (appData.monoMode) {
4175             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4176                              x + squareSize - overall.width - 2,
4177                              y + squareSize - font_descent - 1, string, 1);
4178         } else {
4179             XDrawString(xDisplay, xBoardWindow, coordGC,
4180                         x + squareSize - overall.width - 2,
4181                         y + squareSize - font_descent - 1, string, 1);
4182         }
4183     }
4184     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4185         string[0] = ONE + row;
4186         XTextExtents(coordFontStruct, string, 1, &direction,
4187                      &font_ascent, &font_descent, &overall);
4188         if (appData.monoMode) {
4189             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4190                              x + 2, y + font_ascent + 1, string, 1);
4191         } else {
4192             XDrawString(xDisplay, xBoardWindow, coordGC,
4193                         x + 2, y + font_ascent + 1, string, 1);
4194         }
4195     }
4196 }
4197
4198
4199 /* Why is this needed on some versions of X? */
4200 void EventProc(widget, unused, event)
4201      Widget widget;
4202      caddr_t unused;
4203      XEvent *event;
4204 {
4205     if (!XtIsRealized(widget))
4206       return;
4207
4208     switch (event->type) {
4209       case Expose:
4210         if (event->xexpose.count > 0) return;  /* no clipping is done */
4211         XDrawPosition(widget, True, NULL);
4212         break;
4213       default:
4214         return;
4215     }
4216 }
4217 /* end why */
4218
4219 void DrawPosition(fullRedraw, board)
4220      /*Boolean*/int fullRedraw;
4221      Board board;
4222 {
4223     XDrawPosition(boardWidget, fullRedraw, board);
4224 }
4225
4226 /* Returns 1 if there are "too many" differences between b1 and b2
4227    (i.e. more than 1 move was made) */
4228 static int too_many_diffs(b1, b2)
4229      Board b1, b2;
4230 {
4231     int i, j;
4232     int c = 0;
4233
4234     for (i=0; i<BOARD_HEIGHT; ++i) {
4235         for (j=0; j<BOARD_WIDTH; ++j) {
4236             if (b1[i][j] != b2[i][j]) {
4237                 if (++c > 4)    /* Castling causes 4 diffs */
4238                   return 1;
4239             }
4240         }
4241     }
4242
4243     return 0;
4244 }
4245
4246 /* Matrix describing castling maneuvers */
4247 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4248 static int castling_matrix[4][5] = {
4249     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4250     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4251     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4252     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4253 };
4254
4255 /* Checks whether castling occurred. If it did, *rrow and *rcol
4256    are set to the destination (row,col) of the rook that moved.
4257
4258    Returns 1 if castling occurred, 0 if not.
4259
4260    Note: Only handles a max of 1 castling move, so be sure
4261    to call too_many_diffs() first.
4262    */
4263 static int check_castle_draw(newb, oldb, rrow, rcol)
4264      Board newb, oldb;
4265      int *rrow, *rcol;
4266 {
4267     int i, *r, j;
4268     int match;
4269
4270     /* For each type of castling... */
4271     for (i=0; i<4; ++i) {
4272         r = castling_matrix[i];
4273
4274         /* Check the 4 squares involved in the castling move */
4275         match = 0;
4276         for (j=1; j<=4; ++j) {
4277             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4278                 match = 1;
4279                 break;
4280             }
4281         }
4282
4283         if (!match) {
4284             /* All 4 changed, so it must be a castling move */
4285             *rrow = r[0];
4286             *rcol = r[3];
4287             return 1;
4288         }
4289     }
4290     return 0;
4291 }
4292
4293 static int damage[BOARD_RANKS][BOARD_FILES];
4294
4295 /*
4296  * event handler for redrawing the board
4297  */
4298 void XDrawPosition(w, repaint, board)
4299      Widget w;
4300      /*Boolean*/int repaint;
4301      Board board;
4302 {
4303     int i, j, do_flash;
4304     static int lastFlipView = 0;
4305     static int lastBoardValid = 0;
4306     static Board lastBoard;
4307     Arg args[16];
4308     int rrow, rcol;
4309
4310     if (board == NULL) {
4311         if (!lastBoardValid) return;
4312         board = lastBoard;
4313     }
4314     if (!lastBoardValid || lastFlipView != flipView) {
4315         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4316         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
4317                     args, 1);
4318     }
4319
4320     /*
4321      * It would be simpler to clear the window with XClearWindow()
4322      * but this causes a very distracting flicker.
4323      */
4324
4325     if (!repaint && lastBoardValid && lastFlipView == flipView) {
4326
4327         /* If too much changes (begin observing new game, etc.), don't
4328            do flashing */
4329         do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
4330
4331         /* Special check for castling so we don't flash both the king
4332            and the rook (just flash the king). */
4333         if (do_flash) {
4334             if (check_castle_draw(board, lastBoard, &rrow, &rcol)) {
4335                 /* Draw rook with NO flashing. King will be drawn flashing later */
4336                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4337                 lastBoard[rrow][rcol] = board[rrow][rcol];
4338             }
4339         }
4340
4341         /* First pass -- Draw (newly) empty squares and repair damage.
4342            This prevents you from having a piece show up twice while it
4343            is flashing on its new square */
4344         for (i = 0; i < BOARD_HEIGHT; i++)
4345           for (j = 0; j < BOARD_WIDTH; j++)
4346             if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
4347                 || damage[i][j]) {
4348                 DrawSquare(i, j, board[i][j], 0);
4349                 damage[i][j] = False;
4350             }
4351
4352         /* Second pass -- Draw piece(s) in new position and flash them */
4353         for (i = 0; i < BOARD_HEIGHT; i++)
4354           for (j = 0; j < BOARD_WIDTH; j++)
4355             if (board[i][j] != lastBoard[i][j]) {
4356                 DrawSquare(i, j, board[i][j], do_flash);
4357             }
4358     } else {
4359         if (lineGap > 0)
4360           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4361                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4362
4363         for (i = 0; i < BOARD_HEIGHT; i++)
4364           for (j = 0; j < BOARD_WIDTH; j++) {
4365               DrawSquare(i, j, board[i][j], 0);
4366               damage[i][j] = False;
4367           }
4368     }
4369
4370     CopyBoard(lastBoard, board);
4371     lastBoardValid = 1;
4372     lastFlipView = flipView;
4373
4374     /* Draw highlights */
4375     if (pm1X >= 0 && pm1Y >= 0) {
4376       drawHighlight(pm1X, pm1Y, prelineGC);
4377     }
4378     if (pm2X >= 0 && pm2Y >= 0) {
4379       drawHighlight(pm2X, pm2Y, prelineGC);
4380     }
4381     if (hi1X >= 0 && hi1Y >= 0) {
4382       drawHighlight(hi1X, hi1Y, highlineGC);
4383     }
4384     if (hi2X >= 0 && hi2Y >= 0) {
4385       drawHighlight(hi2X, hi2Y, highlineGC);
4386     }
4387
4388     /* If piece being dragged around board, must redraw that too */
4389     DrawDragPiece();
4390
4391     XSync(xDisplay, False);
4392 }
4393
4394
4395 /*
4396  * event handler for redrawing the board
4397  */
4398 void DrawPositionProc(w, event, prms, nprms)
4399      Widget w;
4400      XEvent *event;
4401      String *prms;
4402      Cardinal *nprms;
4403 {
4404     XDrawPosition(w, True, NULL);
4405 }
4406
4407
4408 /*
4409  * event handler for parsing user moves
4410  */
4411 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4412 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4413 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4414 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
4415 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4416 //       and at the end FinishMove() to perform the move after optional promotion popups.
4417 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4418 void HandleUserMove(w, event, prms, nprms)
4419      Widget w;
4420      XEvent *event;
4421      String *prms;
4422      Cardinal *nprms;
4423 {
4424     if (w != boardWidget || errorExitStatus != -1) return;
4425
4426     if (promotionUp) {
4427         if (event->type == ButtonPress) {
4428             XtPopdown(promotionShell);
4429             XtDestroyWidget(promotionShell);
4430             promotionUp = False;
4431             ClearHighlights();
4432             fromX = fromY = -1;
4433         } else {
4434             return;
4435         }
4436     }
4437
4438     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4439     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
4440     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4441 }
4442
4443 void AnimateUserMove (Widget w, XEvent * event,
4444                       String * params, Cardinal * nParams)
4445 {
4446     DragPieceMove(event->xmotion.x, event->xmotion.y);
4447 }
4448
4449 Widget CommentCreate(name, text, mutable, callback, lines)
4450      char *name, *text;
4451      int /*Boolean*/ mutable;
4452      XtCallbackProc callback;
4453      int lines;
4454 {
4455     Arg args[16];
4456     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4457     Dimension bw_width;
4458     int j;
4459
4460     j = 0;
4461     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4462     XtGetValues(boardWidget, args, j);
4463
4464     j = 0;
4465     XtSetArg(args[j], XtNresizable, True);  j++;
4466 #if TOPLEVEL
4467     shell =
4468       XtCreatePopupShell(name, topLevelShellWidgetClass,
4469                          shellWidget, args, j);
4470 #else
4471     shell =
4472       XtCreatePopupShell(name, transientShellWidgetClass,
4473                          shellWidget, args, j);
4474 #endif
4475     layout =
4476       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4477                             layoutArgs, XtNumber(layoutArgs));
4478     form =
4479       XtCreateManagedWidget("form", formWidgetClass, layout,
4480                             formArgs, XtNumber(formArgs));
4481
4482     j = 0;
4483     if (mutable) {
4484         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4485         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4486     }
4487     XtSetArg(args[j], XtNstring, text);  j++;
4488     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4489     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4490     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4491     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4492     XtSetArg(args[j], XtNresizable, True);  j++;
4493     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
4494     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4495     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4496     XtSetArg(args[j], XtNautoFill, True);  j++;
4497     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4498     edit =
4499       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4500
4501     if (mutable) {
4502         j = 0;
4503         XtSetArg(args[j], XtNfromVert, edit);  j++;
4504         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4505         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4506         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4507         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4508         b_ok =
4509           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4510         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4511
4512         j = 0;
4513         XtSetArg(args[j], XtNfromVert, edit);  j++;
4514         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4515         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4516         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4517         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4518         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4519         b_cancel =
4520           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4521         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4522
4523         j = 0;
4524         XtSetArg(args[j], XtNfromVert, edit);  j++;
4525         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4526         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4527         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4528         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4529         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4530         b_clear =
4531           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4532         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4533     } else {
4534         j = 0;
4535         XtSetArg(args[j], XtNfromVert, edit);  j++;
4536         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4537         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4538         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4539         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4540         b_close =
4541           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4542         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4543
4544         j = 0;
4545         XtSetArg(args[j], XtNfromVert, edit);  j++;
4546         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4547         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4548         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4549         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4550         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4551         b_edit =
4552           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4553         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4554     }
4555
4556     XtRealizeWidget(shell);
4557
4558     if (commentX == -1) {
4559         int xx, yy;
4560         Window junk;
4561         Dimension pw_height;
4562         Dimension ew_height;
4563
4564         j = 0;
4565         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4566         XtGetValues(edit, args, j);
4567
4568         j = 0;
4569         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4570         XtGetValues(shell, args, j);
4571         commentH = pw_height + (lines - 1) * ew_height;
4572         commentW = bw_width - 16;
4573
4574         XSync(xDisplay, False);
4575 #ifdef NOTDEF
4576         /* This code seems to tickle an X bug if it is executed too soon
4577            after xboard starts up.  The coordinates get transformed as if
4578            the main window was positioned at (0, 0).
4579            */
4580         XtTranslateCoords(shellWidget,
4581                           (bw_width - commentW) / 2, 0 - commentH / 2,
4582                           &commentX, &commentY);
4583 #else  /*!NOTDEF*/
4584         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4585                               RootWindowOfScreen(XtScreen(shellWidget)),
4586                               (bw_width - commentW) / 2, 0 - commentH / 2,
4587                               &xx, &yy, &junk);
4588         commentX = xx;
4589         commentY = yy;
4590 #endif /*!NOTDEF*/
4591         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4592     }
4593
4594     if(wpComment.width > 0) {
4595       commentX = wpComment.x;
4596       commentY = wpComment.y;
4597       commentW = wpComment.width;
4598       commentH = wpComment.height;
4599     }
4600
4601     j = 0;
4602     XtSetArg(args[j], XtNheight, commentH);  j++;
4603     XtSetArg(args[j], XtNwidth, commentW);  j++;
4604     XtSetArg(args[j], XtNx, commentX);  j++;
4605     XtSetArg(args[j], XtNy, commentY);  j++;
4606     XtSetValues(shell, args, j);
4607     XtSetKeyboardFocus(shell, edit);
4608
4609     return shell;
4610 }
4611
4612 /* Used for analysis window and ICS input window */
4613 Widget MiscCreate(name, text, mutable, callback, lines)
4614      char *name, *text;
4615      int /*Boolean*/ mutable;
4616      XtCallbackProc callback;
4617      int lines;
4618 {
4619     Arg args[16];
4620     Widget shell, layout, form, edit;
4621     Position x, y;
4622     Dimension bw_width, pw_height, ew_height, w, h;
4623     int j;
4624     int xx, yy;
4625     Window junk;
4626
4627     j = 0;
4628     XtSetArg(args[j], XtNresizable, True);  j++;
4629 #if TOPLEVEL
4630     shell =
4631       XtCreatePopupShell(name, topLevelShellWidgetClass,
4632                          shellWidget, args, j);
4633 #else
4634     shell =
4635       XtCreatePopupShell(name, transientShellWidgetClass,
4636                          shellWidget, args, j);
4637 #endif
4638     layout =
4639       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4640                             layoutArgs, XtNumber(layoutArgs));
4641     form =
4642       XtCreateManagedWidget("form", formWidgetClass, layout,
4643                             formArgs, XtNumber(formArgs));
4644
4645     j = 0;
4646     if (mutable) {
4647         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4648         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4649     }
4650     XtSetArg(args[j], XtNstring, text);  j++;
4651     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4652     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4653     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4654     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4655     XtSetArg(args[j], XtNresizable, True);  j++;
4656     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4657     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4658     XtSetArg(args[j], XtNautoFill, True);  j++;
4659     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4660     edit =
4661       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4662
4663     XtRealizeWidget(shell);
4664
4665     j = 0;
4666     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4667     XtGetValues(boardWidget, args, j);
4668
4669     j = 0;
4670     XtSetArg(args[j], XtNheight, &ew_height);  j++;
4671     XtGetValues(edit, args, j);
4672
4673     j = 0;
4674     XtSetArg(args[j], XtNheight, &pw_height);  j++;
4675     XtGetValues(shell, args, j);
4676     h = pw_height + (lines - 1) * ew_height;
4677     w = bw_width - 16;
4678
4679     XSync(xDisplay, False);
4680 #ifdef NOTDEF
4681     /* This code seems to tickle an X bug if it is executed too soon
4682        after xboard starts up.  The coordinates get transformed as if
4683        the main window was positioned at (0, 0).
4684     */
4685     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4686 #else  /*!NOTDEF*/
4687     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4688                           RootWindowOfScreen(XtScreen(shellWidget)),
4689                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4690 #endif /*!NOTDEF*/
4691     x = xx;
4692     y = yy;
4693     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4694
4695     j = 0;
4696     XtSetArg(args[j], XtNheight, h);  j++;
4697     XtSetArg(args[j], XtNwidth, w);  j++;
4698     XtSetArg(args[j], XtNx, x);  j++;
4699     XtSetArg(args[j], XtNy, y);  j++;
4700     XtSetValues(shell, args, j);
4701
4702     return shell;
4703 }
4704
4705
4706 static int savedIndex;  /* gross that this is global */
4707
4708 void EditCommentPopUp(index, title, text)
4709      int index;
4710      char *title, *text;
4711 {
4712     Widget edit;
4713     Arg args[16];
4714     int j;
4715
4716     savedIndex = index;
4717     if (text == NULL) text = "";
4718
4719     if (editShell == NULL) {
4720         editShell =
4721           CommentCreate(title, text, True, EditCommentCallback, 4);
4722         XtRealizeWidget(editShell);
4723         CatchDeleteWindow(editShell, "EditCommentPopDown");
4724     } else {
4725         edit = XtNameToWidget(editShell, "*form.text");
4726         j = 0;
4727         XtSetArg(args[j], XtNstring, text); j++;
4728         XtSetValues(edit, args, j);
4729         j = 0;
4730         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4731         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4732         XtSetValues(editShell, args, j);
4733     }
4734
4735     XtPopup(editShell, XtGrabNone);
4736
4737     editUp = True;
4738     j = 0;
4739     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4740     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4741                 args, j);
4742 }
4743
4744 void EditCommentCallback(w, client_data, call_data)
4745      Widget w;
4746      XtPointer client_data, call_data;
4747 {
4748     String name, val;
4749     Arg args[16];
4750     int j;
4751     Widget edit;
4752
4753     j = 0;
4754     XtSetArg(args[j], XtNlabel, &name);  j++;
4755     XtGetValues(w, args, j);
4756
4757     if (strcmp(name, _("ok")) == 0) {
4758         edit = XtNameToWidget(editShell, "*form.text");
4759         j = 0;
4760         XtSetArg(args[j], XtNstring, &val); j++;
4761         XtGetValues(edit, args, j);
4762         ReplaceComment(savedIndex, val);
4763         EditCommentPopDown();
4764     } else if (strcmp(name, _("cancel")) == 0) {
4765         EditCommentPopDown();
4766     } else if (strcmp(name, _("clear")) == 0) {
4767         edit = XtNameToWidget(editShell, "*form.text");
4768         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4769         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4770     }
4771 }
4772
4773 void EditCommentPopDown()
4774 {
4775     Arg args[16];
4776     int j;
4777
4778     if (!editUp) return;
4779     j = 0;
4780     XtSetArg(args[j], XtNx, &commentX); j++;
4781     XtSetArg(args[j], XtNy, &commentY); j++;
4782     XtSetArg(args[j], XtNheight, &commentH); j++;
4783     XtSetArg(args[j], XtNwidth, &commentW); j++;
4784     XtGetValues(editShell, args, j);
4785     XtPopdown(editShell);
4786     editUp = False;
4787     j = 0;
4788     XtSetArg(args[j], XtNleftBitmap, None); j++;
4789     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4790                 args, j);
4791 }
4792
4793 void ICSInputBoxPopUp()
4794 {
4795     Widget edit;
4796     Arg args[16];
4797     int j;
4798     char *title = _("ICS Input");
4799     XtTranslations tr;
4800
4801     if (ICSInputShell == NULL) {
4802         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
4803         tr = XtParseTranslationTable(ICSInputTranslations);
4804         edit = XtNameToWidget(ICSInputShell, "*form.text");
4805         XtOverrideTranslations(edit, tr);
4806         XtRealizeWidget(ICSInputShell);
4807         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
4808
4809     } else {
4810         edit = XtNameToWidget(ICSInputShell, "*form.text");
4811         j = 0;
4812         XtSetArg(args[j], XtNstring, ""); j++;
4813         XtSetValues(edit, args, j);
4814         j = 0;
4815         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4816         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4817         XtSetValues(ICSInputShell, args, j);
4818     }
4819
4820     XtPopup(ICSInputShell, XtGrabNone);
4821     XtSetKeyboardFocus(ICSInputShell, edit);
4822
4823     ICSInputBoxUp = True;
4824     j = 0;
4825     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4826     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4827                 args, j);
4828 }
4829
4830 void ICSInputSendText()
4831 {
4832     Widget edit;
4833     int j;
4834     Arg args[16];
4835     String val;
4836
4837     edit = XtNameToWidget(ICSInputShell, "*form.text");
4838     j = 0;
4839     XtSetArg(args[j], XtNstring, &val); j++;
4840     XtGetValues(edit, args, j);
4841     SendMultiLineToICS(val);
4842     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4843     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4844 }
4845
4846 void ICSInputBoxPopDown()
4847 {
4848     Arg args[16];
4849     int j;
4850
4851     if (!ICSInputBoxUp) return;
4852     j = 0;
4853     XtPopdown(ICSInputShell);
4854     ICSInputBoxUp = False;
4855     j = 0;
4856     XtSetArg(args[j], XtNleftBitmap, None); j++;
4857     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4858                 args, j);
4859 }
4860
4861 void CommentPopUp(title, text)
4862      char *title, *text;
4863 {
4864     Arg args[16];
4865     int j;
4866     Widget edit;
4867
4868     if (commentShell == NULL) {
4869         commentShell =
4870           CommentCreate(title, text, False, CommentCallback, 4);
4871         XtRealizeWidget(commentShell);
4872         CatchDeleteWindow(commentShell, "CommentPopDown");
4873     } else {
4874         edit = XtNameToWidget(commentShell, "*form.text");
4875         j = 0;
4876         XtSetArg(args[j], XtNstring, text); j++;
4877         XtSetValues(edit, args, j);
4878         j = 0;
4879         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4880         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4881         XtSetValues(commentShell, args, j);
4882     }
4883
4884     XtPopup(commentShell, XtGrabNone);
4885     XSync(xDisplay, False);
4886
4887     commentUp = True;
4888 }
4889
4890 void CommentCallback(w, client_data, call_data)
4891      Widget w;
4892      XtPointer client_data, call_data;
4893 {
4894     String name;
4895     Arg args[16];
4896     int j;
4897
4898     j = 0;
4899     XtSetArg(args[j], XtNlabel, &name);  j++;
4900     XtGetValues(w, args, j);
4901
4902     if (strcmp(name, _("close")) == 0) {
4903         CommentPopDown();
4904     } else if (strcmp(name, _("edit")) == 0) {
4905         CommentPopDown();
4906         EditCommentEvent();
4907     }
4908 }
4909
4910
4911 void CommentPopDown()
4912 {
4913     Arg args[16];
4914     int j;
4915
4916     if (!commentUp) return;
4917     j = 0;
4918     XtSetArg(args[j], XtNx, &commentX); j++;
4919     XtSetArg(args[j], XtNy, &commentY); j++;
4920     XtSetArg(args[j], XtNwidth, &commentW); j++;
4921     XtSetArg(args[j], XtNheight, &commentH); j++;
4922     XtGetValues(commentShell, args, j);
4923     XtPopdown(commentShell);
4924     XSync(xDisplay, False);
4925     commentUp = False;
4926 }
4927
4928 void FileNamePopUp(label, def, proc, openMode)
4929      char *label;
4930      char *def;
4931      FileProc proc;
4932      char *openMode;
4933 {
4934     Arg args[16];
4935     Widget popup, layout, dialog, edit;
4936     Window root, child;
4937     int x, y, i;
4938     int win_x, win_y;
4939     unsigned int mask;
4940
4941     fileProc = proc;            /* I can't see a way not */
4942     fileOpenMode = openMode;    /*   to use globals here */
4943
4944     i = 0;
4945     XtSetArg(args[i], XtNresizable, True); i++;
4946     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4947     XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
4948     fileNameShell = popup =
4949       XtCreatePopupShell("File name prompt", transientShellWidgetClass,
4950                          shellWidget, args, i);
4951
4952     layout =
4953       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4954                             layoutArgs, XtNumber(layoutArgs));
4955
4956     i = 0;
4957     XtSetArg(args[i], XtNlabel, label); i++;
4958     XtSetArg(args[i], XtNvalue, def); i++;
4959     XtSetArg(args[i], XtNborderWidth, 0); i++;
4960     dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
4961                                    layout, args, i);
4962
4963     XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
4964     XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
4965                        (XtPointer) dialog);
4966
4967     XtRealizeWidget(popup);
4968     CatchDeleteWindow(popup, "FileNamePopDown");
4969
4970     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4971                   &x, &y, &win_x, &win_y, &mask);
4972
4973     XtSetArg(args[0], XtNx, x - 10);
4974     XtSetArg(args[1], XtNy, y - 30);
4975     XtSetValues(popup, args, 2);
4976
4977     XtPopup(popup, XtGrabExclusive);
4978     filenameUp = True;
4979
4980     edit = XtNameToWidget(dialog, "*value");
4981     XtSetKeyboardFocus(popup, edit);
4982 }
4983
4984 void FileNamePopDown()
4985 {
4986     if (!filenameUp) return;
4987     XtPopdown(fileNameShell);
4988     XtDestroyWidget(fileNameShell);
4989     filenameUp = False;
4990     ModeHighlight();
4991 }
4992
4993 void FileNameCallback(w, client_data, call_data)
4994      Widget w;
4995      XtPointer client_data, call_data;
4996 {
4997     String name;
4998     Arg args[16];
4999
5000     XtSetArg(args[0], XtNlabel, &name);
5001     XtGetValues(w, args, 1);
5002
5003     if (strcmp(name, _("cancel")) == 0) {
5004         FileNamePopDown();
5005         return;
5006     }
5007
5008     FileNameAction(w, NULL, NULL, NULL);
5009 }
5010
5011 void FileNameAction(w, event, prms, nprms)
5012      Widget w;
5013      XEvent *event;
5014      String *prms;
5015      Cardinal *nprms;
5016 {
5017     char buf[MSG_SIZ];
5018     String name;
5019     FILE *f;
5020     char *p, *fullname;
5021     int index;
5022
5023     name = XawDialogGetValueString(w = XtParent(w));
5024
5025     if ((name != NULL) && (*name != NULLCHAR)) {
5026         strcpy(buf, name);
5027         XtPopdown(w = XtParent(XtParent(w)));
5028         XtDestroyWidget(w);
5029         filenameUp = False;
5030
5031         p = strrchr(buf, ' ');
5032         if (p == NULL) {
5033             index = 0;
5034         } else {
5035             *p++ = NULLCHAR;
5036             index = atoi(p);
5037         }
5038         fullname = ExpandPathName(buf);
5039         if (!fullname) {
5040             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5041         }
5042         else {
5043             f = fopen(fullname, fileOpenMode);
5044             if (f == NULL) {
5045                 DisplayError(_("Failed to open file"), errno);
5046             } else {
5047                 (void) (*fileProc)(f, index, buf);
5048             }
5049         }
5050         ModeHighlight();
5051         return;
5052     }
5053
5054     XtPopdown(w = XtParent(XtParent(w)));
5055     XtDestroyWidget(w);
5056     filenameUp = False;
5057     ModeHighlight();
5058 }
5059
5060 void PromotionPopUp()
5061 {
5062     Arg args[16];
5063     Widget dialog, layout;
5064     Position x, y;
5065     Dimension bw_width, pw_width;
5066     int j;
5067
5068     j = 0;
5069     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5070     XtGetValues(boardWidget, args, j);
5071
5072     j = 0;
5073     XtSetArg(args[j], XtNresizable, True); j++;
5074     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5075     promotionShell =
5076       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5077                          shellWidget, args, j);
5078     layout =
5079       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5080                             layoutArgs, XtNumber(layoutArgs));
5081
5082     j = 0;
5083     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5084     XtSetArg(args[j], XtNborderWidth, 0); j++;
5085     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5086                                    layout, args, j);
5087
5088   if(gameInfo.variant != VariantShogi) {
5089     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5090                        (XtPointer) dialog);
5091     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5092                        (XtPointer) dialog);
5093     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5094                        (XtPointer) dialog);
5095     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5096                        (XtPointer) dialog);
5097     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5098         gameInfo.variant == VariantGiveaway) {
5099       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5100                          (XtPointer) dialog);
5101     }
5102     if(gameInfo.variant == VariantCapablanca || 
5103        gameInfo.variant == VariantGothic || 
5104        gameInfo.variant == VariantCapaRandom) {
5105       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5106                          (XtPointer) dialog);
5107       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5108                          (XtPointer) dialog);
5109     }
5110   } else // [HGM] shogi
5111   {
5112       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5113                          (XtPointer) dialog);
5114       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5115                          (XtPointer) dialog);
5116   }
5117     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5118                        (XtPointer) dialog);
5119
5120     XtRealizeWidget(promotionShell);
5121     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5122
5123     j = 0;
5124     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5125     XtGetValues(promotionShell, args, j);
5126
5127     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5128                       lineGap + squareSize/3 +
5129                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5130                        0 : 6*(squareSize + lineGap)), &x, &y);
5131
5132     j = 0;
5133     XtSetArg(args[j], XtNx, x); j++;
5134     XtSetArg(args[j], XtNy, y); j++;
5135     XtSetValues(promotionShell, args, j);
5136
5137     XtPopup(promotionShell, XtGrabNone);
5138
5139     promotionUp = True;
5140 }
5141
5142 void PromotionPopDown()
5143 {
5144     if (!promotionUp) return;
5145     XtPopdown(promotionShell);
5146     XtDestroyWidget(promotionShell);
5147     promotionUp = False;
5148 }
5149
5150 void PromotionCallback(w, client_data, call_data)
5151      Widget w;
5152      XtPointer client_data, call_data;
5153 {
5154     String name;
5155     Arg args[16];
5156     int promoChar;
5157
5158     XtSetArg(args[0], XtNlabel, &name);
5159     XtGetValues(w, args, 1);
5160
5161     PromotionPopDown();
5162
5163     if (fromX == -1) return;
5164
5165     if (strcmp(name, _("cancel")) == 0) {
5166         fromX = fromY = -1;
5167         ClearHighlights();
5168         return;
5169     } else if (strcmp(name, _("Knight")) == 0) {
5170         promoChar = 'n';
5171     } else if (strcmp(name, _("Promote")) == 0) {
5172         promoChar = '+';
5173     } else if (strcmp(name, _("Defer")) == 0) {
5174         promoChar = '=';
5175     } else {
5176         promoChar = ToLower(name[0]);
5177     }
5178
5179     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5180
5181     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5182     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5183     fromX = fromY = -1;
5184 }
5185
5186
5187 void ErrorCallback(w, client_data, call_data)
5188      Widget w;
5189      XtPointer client_data, call_data;
5190 {
5191     errorUp = False;
5192     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5193     XtDestroyWidget(w);
5194     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5195 }
5196
5197
5198 void ErrorPopDown()
5199 {
5200     if (!errorUp) return;
5201     errorUp = False;
5202     XtPopdown(errorShell);
5203     XtDestroyWidget(errorShell);
5204     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5205 }
5206
5207 void ErrorPopUp(title, label, modal)
5208      char *title, *label;
5209      int modal;
5210 {
5211     Arg args[16];
5212     Widget dialog, layout;
5213     Position x, y;
5214     int xx, yy;
5215     Window junk;
5216     Dimension bw_width, pw_width;
5217     Dimension pw_height;
5218     int i;
5219
5220     i = 0;
5221     XtSetArg(args[i], XtNresizable, True);  i++;
5222     XtSetArg(args[i], XtNtitle, title); i++;
5223     errorShell =
5224       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5225                          shellWidget, args, i);
5226     layout =
5227       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5228                             layoutArgs, XtNumber(layoutArgs));
5229
5230     i = 0;
5231     XtSetArg(args[i], XtNlabel, label); i++;
5232     XtSetArg(args[i], XtNborderWidth, 0); i++;
5233     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5234                                    layout, args, i);
5235
5236     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5237
5238     XtRealizeWidget(errorShell);
5239     CatchDeleteWindow(errorShell, "ErrorPopDown");
5240
5241     i = 0;
5242     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5243     XtGetValues(boardWidget, args, i);
5244     i = 0;
5245     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5246     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5247     XtGetValues(errorShell, args, i);
5248
5249 #ifdef NOTDEF
5250     /* This code seems to tickle an X bug if it is executed too soon
5251        after xboard starts up.  The coordinates get transformed as if
5252        the main window was positioned at (0, 0).
5253        */
5254     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5255                       0 - pw_height + squareSize / 3, &x, &y);
5256 #else
5257     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5258                           RootWindowOfScreen(XtScreen(boardWidget)),
5259                           (bw_width - pw_width) / 2,
5260                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5261     x = xx;
5262     y = yy;
5263 #endif
5264     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5265
5266     i = 0;
5267     XtSetArg(args[i], XtNx, x);  i++;
5268     XtSetArg(args[i], XtNy, y);  i++;
5269     XtSetValues(errorShell, args, i);
5270
5271     errorUp = True;
5272     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5273 }
5274
5275 /* Disable all user input other than deleting the window */
5276 static int frozen = 0;
5277 void FreezeUI()
5278 {
5279   if (frozen) return;
5280   /* Grab by a widget that doesn't accept input */
5281   XtAddGrab(messageWidget, TRUE, FALSE);
5282   frozen = 1;
5283 }
5284
5285 /* Undo a FreezeUI */
5286 void ThawUI()
5287 {
5288   if (!frozen) return;
5289   XtRemoveGrab(messageWidget);
5290   frozen = 0;
5291 }
5292
5293 char *ModeToWidgetName(mode)
5294      GameMode mode;
5295 {
5296     switch (mode) {
5297       case BeginningOfGame:
5298         if (appData.icsActive)
5299           return "menuMode.ICS Client";
5300         else if (appData.noChessProgram ||
5301                  *appData.cmailGameName != NULLCHAR)
5302           return "menuMode.Edit Game";
5303         else
5304           return "menuMode.Machine Black";
5305       case MachinePlaysBlack:
5306         return "menuMode.Machine Black";
5307       case MachinePlaysWhite:
5308         return "menuMode.Machine White";
5309       case AnalyzeMode:
5310         return "menuMode.Analysis Mode";
5311       case AnalyzeFile:
5312         return "menuMode.Analyze File";
5313       case TwoMachinesPlay:
5314         return "menuMode.Two Machines";
5315       case EditGame:
5316         return "menuMode.Edit Game";
5317       case PlayFromGameFile:
5318         return "menuFile.Load Game";
5319       case EditPosition:
5320         return "menuMode.Edit Position";
5321       case Training:
5322         return "menuMode.Training";
5323       case IcsPlayingWhite:
5324       case IcsPlayingBlack:
5325       case IcsObserving:
5326       case IcsIdle:
5327       case IcsExamining:
5328         return "menuMode.ICS Client";
5329       default:
5330       case EndOfGame:
5331         return NULL;
5332     }
5333 }
5334
5335 void ModeHighlight()
5336 {
5337     Arg args[16];
5338     static int oldPausing = FALSE;
5339     static GameMode oldmode = (GameMode) -1;
5340     char *wname;
5341
5342     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5343
5344     if (pausing != oldPausing) {
5345         oldPausing = pausing;
5346         if (pausing) {
5347             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5348         } else {
5349             XtSetArg(args[0], XtNleftBitmap, None);
5350         }
5351         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5352                     args, 1);
5353
5354         if (appData.showButtonBar) {
5355           /* Always toggle, don't set.  Previous code messes up when
5356              invoked while the button is pressed, as releasing it
5357              toggles the state again. */
5358           {
5359             Pixel oldbg, oldfg;
5360             XtSetArg(args[0], XtNbackground, &oldbg);
5361             XtSetArg(args[1], XtNforeground, &oldfg);
5362             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5363                         args, 2);
5364             XtSetArg(args[0], XtNbackground, oldfg);
5365             XtSetArg(args[1], XtNforeground, oldbg);
5366           }
5367           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5368         }
5369     }
5370
5371     wname = ModeToWidgetName(oldmode);
5372     if (wname != NULL) {
5373         XtSetArg(args[0], XtNleftBitmap, None);
5374         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5375     }
5376     wname = ModeToWidgetName(gameMode);
5377     if (wname != NULL) {
5378         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5379         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5380     }
5381     oldmode = gameMode;
5382
5383     /* Maybe all the enables should be handled here, not just this one */
5384     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5385                    gameMode == Training || gameMode == PlayFromGameFile);
5386 }
5387
5388
5389 /*
5390  * Button/menu procedures
5391  */
5392 void ResetProc(w, event, prms, nprms)
5393      Widget w;
5394      XEvent *event;
5395      String *prms;
5396      Cardinal *nprms;
5397 {
5398     ResetGameEvent();
5399 }
5400
5401 int LoadGamePopUp(f, gameNumber, title)
5402      FILE *f;
5403      int gameNumber;
5404      char *title;
5405 {
5406     cmailMsgLoaded = FALSE;
5407     if (gameNumber == 0) {
5408         int error = GameListBuild(f);
5409         if (error) {
5410             DisplayError(_("Cannot build game list"), error);
5411         } else if (!ListEmpty(&gameList) &&
5412                    ((ListGame *) gameList.tailPred)->number > 1) {
5413             GameListPopUp(f, title);
5414             return TRUE;
5415         }
5416         GameListDestroy();
5417         gameNumber = 1;
5418     }
5419     return LoadGame(f, gameNumber, title, FALSE);
5420 }
5421
5422 void LoadGameProc(w, event, prms, nprms)
5423      Widget w;
5424      XEvent *event;
5425      String *prms;
5426      Cardinal *nprms;
5427 {
5428     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5429         Reset(FALSE, TRUE);
5430     }
5431     FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5432 }
5433
5434 void LoadNextGameProc(w, event, prms, nprms)
5435      Widget w;
5436      XEvent *event;
5437      String *prms;
5438      Cardinal *nprms;
5439 {
5440     ReloadGame(1);
5441 }
5442
5443 void LoadPrevGameProc(w, event, prms, nprms)
5444      Widget w;
5445      XEvent *event;
5446      String *prms;
5447      Cardinal *nprms;
5448 {
5449     ReloadGame(-1);
5450 }
5451
5452 void ReloadGameProc(w, event, prms, nprms)
5453      Widget w;
5454      XEvent *event;
5455      String *prms;
5456      Cardinal *nprms;
5457 {
5458     ReloadGame(0);
5459 }
5460
5461 void LoadNextPositionProc(w, event, prms, nprms)
5462      Widget w;
5463      XEvent *event;
5464      String *prms;
5465      Cardinal *nprms;
5466 {
5467     ReloadPosition(1);
5468 }
5469
5470 void LoadPrevPositionProc(w, event, prms, nprms)
5471      Widget w;
5472      XEvent *event;
5473      String *prms;
5474      Cardinal *nprms;
5475 {
5476     ReloadPosition(-1);
5477 }
5478
5479 void ReloadPositionProc(w, event, prms, nprms)
5480      Widget w;
5481      XEvent *event;
5482      String *prms;
5483      Cardinal *nprms;
5484 {
5485     ReloadPosition(0);
5486 }
5487
5488 void LoadPositionProc(w, event, prms, nprms)
5489      Widget w;
5490      XEvent *event;
5491      String *prms;
5492      Cardinal *nprms;
5493 {
5494     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5495         Reset(FALSE, TRUE);
5496     }
5497     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5498 }
5499
5500 void SaveGameProc(w, event, prms, nprms)
5501      Widget w;
5502      XEvent *event;
5503      String *prms;
5504      Cardinal *nprms;
5505 {
5506     FileNamePopUp(_("Save game file name?"),
5507                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5508                   SaveGame, "a");
5509 }
5510
5511 void SavePositionProc(w, event, prms, nprms)
5512      Widget w;
5513      XEvent *event;
5514      String *prms;
5515      Cardinal *nprms;
5516 {
5517     FileNamePopUp(_("Save position file name?"),
5518                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5519                   SavePosition, "a");
5520 }
5521
5522 void ReloadCmailMsgProc(w, event, prms, nprms)
5523      Widget w;
5524      XEvent *event;
5525      String *prms;
5526      Cardinal *nprms;
5527 {
5528     ReloadCmailMsgEvent(FALSE);
5529 }
5530
5531 void MailMoveProc(w, event, prms, nprms)
5532      Widget w;
5533      XEvent *event;
5534      String *prms;
5535      Cardinal *nprms;
5536 {
5537     MailMoveEvent();
5538 }
5539
5540 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5541 static char *selected_fen_position=NULL;
5542
5543 static Boolean
5544 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5545                  Atom *type_return, XtPointer *value_return,
5546                  unsigned long *length_return, int *format_return)
5547 {
5548   char *selection_tmp;
5549
5550   if (!selected_fen_position) return False; /* should never happen */
5551   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5552     /* note: since no XtSelectionDoneProc was registered, Xt will
5553      * automatically call XtFree on the value returned.  So have to
5554      * make a copy of it allocated with XtMalloc */
5555     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5556     strcpy(selection_tmp, selected_fen_position);
5557
5558     *value_return=selection_tmp;
5559     *length_return=strlen(selection_tmp);
5560     *type_return=*target;
5561     *format_return = 8; /* bits per byte */
5562     return True;
5563   } else if (*target == XA_TARGETS(xDisplay)) {
5564     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5565     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5566     targets_tmp[1] = XA_STRING;
5567     *value_return = targets_tmp;
5568     *type_return = XA_ATOM;
5569     *length_return = 2;
5570     *format_return = 8 * sizeof(Atom);
5571     if (*format_return > 32) {
5572       *length_return *= *format_return / 32;
5573       *format_return = 32;
5574     }
5575     return True;
5576   } else {
5577     return False;
5578   }
5579 }
5580
5581 /* note: when called from menu all parameters are NULL, so no clue what the
5582  * Widget which was clicked on was, or what the click event was
5583  */
5584 void CopyPositionProc(w, event, prms, nprms)
5585   Widget w;
5586   XEvent *event;
5587   String *prms;
5588   Cardinal *nprms;
5589   {
5590     /*
5591      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5592      * have a notion of a position that is selected but not copied.
5593      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5594      */
5595     if (selected_fen_position) free(selected_fen_position);
5596     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5597     if (!selected_fen_position) return;
5598     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5599                    CurrentTime,
5600                    SendPositionSelection,
5601                    NULL/* lose_ownership_proc */ ,
5602                    NULL/* transfer_done_proc */);
5603     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5604                    CurrentTime,
5605                    SendPositionSelection,
5606                    NULL/* lose_ownership_proc */ ,
5607                    NULL/* transfer_done_proc */);
5608   }
5609
5610 /* function called when the data to Paste is ready */
5611 static void
5612 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5613            Atom *type, XtPointer value, unsigned long *len, int *format)
5614 {
5615   char *fenstr=value;
5616   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5617   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5618   EditPositionPasteFEN(fenstr);
5619   XtFree(value);
5620 }
5621
5622 /* called when Paste Position button is pressed,
5623  * all parameters will be NULL */
5624 void PastePositionProc(w, event, prms, nprms)
5625   Widget w;
5626   XEvent *event;
5627   String *prms;
5628   Cardinal *nprms;
5629 {
5630     XtGetSelectionValue(menuBarWidget, 
5631       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5632       /* (XtSelectionCallbackProc) */ PastePositionCB,
5633       NULL, /* client_data passed to PastePositionCB */
5634
5635       /* better to use the time field from the event that triggered the
5636        * call to this function, but that isn't trivial to get
5637        */
5638       CurrentTime
5639     );
5640     return;
5641 }
5642
5643 static Boolean
5644 SendGameSelection(Widget w, Atom *selection, Atom *target,
5645                   Atom *type_return, XtPointer *value_return,
5646                   unsigned long *length_return, int *format_return)
5647 {
5648   char *selection_tmp;
5649
5650   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5651     FILE* f = fopen(gameCopyFilename, "r");
5652     long len;
5653     size_t count;
5654     if (f == NULL) return False;
5655     fseek(f, 0, 2);
5656     len = ftell(f);
5657     rewind(f);
5658     selection_tmp = XtMalloc(len + 1);
5659     count = fread(selection_tmp, 1, len, f);
5660     if (len != count) {
5661       XtFree(selection_tmp);
5662       return False;
5663     }
5664     selection_tmp[len] = NULLCHAR;
5665     *value_return = selection_tmp;
5666     *length_return = len;
5667     *type_return = *target;
5668     *format_return = 8; /* bits per byte */
5669     return True;
5670   } else if (*target == XA_TARGETS(xDisplay)) {
5671     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5672     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5673     targets_tmp[1] = XA_STRING;
5674     *value_return = targets_tmp;
5675     *type_return = XA_ATOM;
5676     *length_return = 2;
5677     *format_return = 8 * sizeof(Atom);
5678     if (*format_return > 32) {
5679       *length_return *= *format_return / 32;
5680       *format_return = 32;
5681     }
5682     return True;
5683   } else {
5684     return False;
5685   }
5686 }
5687
5688 /* note: when called from menu all parameters are NULL, so no clue what the
5689  * Widget which was clicked on was, or what the click event was
5690  */
5691 void CopyGameProc(w, event, prms, nprms)
5692   Widget w;
5693   XEvent *event;
5694   String *prms;
5695   Cardinal *nprms;
5696 {
5697   int ret;
5698
5699   ret = SaveGameToFile(gameCopyFilename, FALSE);
5700   if (!ret) return;
5701
5702   /*
5703    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5704    * have a notion of a game that is selected but not copied.
5705    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5706    */
5707   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5708                  CurrentTime,
5709                  SendGameSelection,
5710                  NULL/* lose_ownership_proc */ ,
5711                  NULL/* transfer_done_proc */);
5712   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5713                  CurrentTime,
5714                  SendGameSelection,
5715                  NULL/* lose_ownership_proc */ ,
5716                  NULL/* transfer_done_proc */);
5717 }
5718
5719 /* function called when the data to Paste is ready */
5720 static void
5721 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5722             Atom *type, XtPointer value, unsigned long *len, int *format)
5723 {
5724   FILE* f;
5725   if (value == NULL || *len == 0) {
5726     return; /* nothing had been selected to copy */
5727   }
5728   f = fopen(gamePasteFilename, "w");
5729   if (f == NULL) {
5730     DisplayError(_("Can't open temp file"), errno);
5731     return;
5732   }
5733   fwrite(value, 1, *len, f);
5734   fclose(f);
5735   XtFree(value);
5736   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5737 }
5738
5739 /* called when Paste Game button is pressed,
5740  * all parameters will be NULL */
5741 void PasteGameProc(w, event, prms, nprms)
5742   Widget w;
5743   XEvent *event;
5744   String *prms;
5745   Cardinal *nprms;
5746 {
5747     XtGetSelectionValue(menuBarWidget,
5748       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5749       /* (XtSelectionCallbackProc) */ PasteGameCB,
5750       NULL, /* client_data passed to PasteGameCB */
5751
5752       /* better to use the time field from the event that triggered the
5753        * call to this function, but that isn't trivial to get
5754        */
5755       CurrentTime
5756     );
5757     return;
5758 }
5759
5760
5761 void AutoSaveGame()
5762 {
5763     SaveGameProc(NULL, NULL, NULL, NULL);
5764 }
5765
5766
5767 void QuitProc(w, event, prms, nprms)
5768      Widget w;
5769      XEvent *event;
5770      String *prms;
5771      Cardinal *nprms;
5772 {
5773     ExitEvent(0);
5774 }
5775
5776 void PauseProc(w, event, prms, nprms)
5777      Widget w;
5778      XEvent *event;
5779      String *prms;
5780      Cardinal *nprms;
5781 {
5782     PauseEvent();
5783 }
5784
5785
5786 void MachineBlackProc(w, event, prms, nprms)
5787      Widget w;
5788      XEvent *event;
5789      String *prms;
5790      Cardinal *nprms;
5791 {
5792     MachineBlackEvent();
5793 }
5794
5795 void MachineWhiteProc(w, event, prms, nprms)
5796      Widget w;
5797      XEvent *event;
5798      String *prms;
5799      Cardinal *nprms;
5800 {
5801     MachineWhiteEvent();
5802 }
5803
5804 void AnalyzeModeProc(w, event, prms, nprms)
5805      Widget w;
5806      XEvent *event;
5807      String *prms;
5808      Cardinal *nprms;
5809 {
5810     char buf[MSG_SIZ];
5811
5812     if (!first.analysisSupport) {
5813       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5814       DisplayError(buf, 0);
5815       return;
5816     }
5817     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5818     if (appData.icsActive) {
5819         if (gameMode != IcsObserving) {
5820             sprintf(buf,_("You are not observing a game"));
5821             DisplayError(buf, 0);
5822             /* secure check */
5823             if (appData.icsEngineAnalyze) {
5824                 if (appData.debugMode)
5825                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5826                 ExitAnalyzeMode();
5827                 ModeHighlight();
5828             }
5829             return;
5830         }
5831         /* if enable, use want disable icsEngineAnalyze */
5832         if (appData.icsEngineAnalyze) {
5833                 ExitAnalyzeMode();
5834                 ModeHighlight();
5835                 return;
5836         }
5837         appData.icsEngineAnalyze = TRUE;
5838         if (appData.debugMode)
5839             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5840     }
5841     if (!appData.showThinking)
5842       ShowThinkingProc(w,event,prms,nprms);
5843
5844     AnalyzeModeEvent();
5845 }
5846
5847 void AnalyzeFileProc(w, event, prms, nprms)
5848      Widget w;
5849      XEvent *event;
5850      String *prms;
5851      Cardinal *nprms;
5852 {
5853     if (!first.analysisSupport) {
5854       char buf[MSG_SIZ];
5855       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5856       DisplayError(buf, 0);
5857       return;
5858     }
5859     Reset(FALSE, TRUE);
5860
5861     if (!appData.showThinking)
5862       ShowThinkingProc(w,event,prms,nprms);
5863
5864     AnalyzeFileEvent();
5865     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
5866     AnalysisPeriodicEvent(1);
5867 }
5868
5869 void TwoMachinesProc(w, event, prms, nprms)
5870      Widget w;
5871      XEvent *event;
5872      String *prms;
5873      Cardinal *nprms;
5874 {
5875     TwoMachinesEvent();
5876 }
5877
5878 void IcsClientProc(w, event, prms, nprms)
5879      Widget w;
5880      XEvent *event;
5881      String *prms;
5882      Cardinal *nprms;
5883 {
5884     IcsClientEvent();
5885 }
5886
5887 void EditGameProc(w, event, prms, nprms)
5888      Widget w;
5889      XEvent *event;
5890      String *prms;
5891      Cardinal *nprms;
5892 {
5893     EditGameEvent();
5894 }
5895
5896 void EditPositionProc(w, event, prms, nprms)
5897      Widget w;
5898      XEvent *event;
5899      String *prms;
5900      Cardinal *nprms;
5901 {
5902     EditPositionEvent();
5903 }
5904
5905 void TrainingProc(w, event, prms, nprms)
5906      Widget w;
5907      XEvent *event;
5908      String *prms;
5909      Cardinal *nprms;
5910 {
5911     TrainingEvent();
5912 }
5913
5914 void EditCommentProc(w, event, prms, nprms)
5915      Widget w;
5916      XEvent *event;
5917      String *prms;
5918      Cardinal *nprms;
5919 {
5920     if (editUp) {
5921         EditCommentPopDown();
5922     } else {
5923         EditCommentEvent();
5924     }
5925 }
5926
5927 void IcsInputBoxProc(w, event, prms, nprms)
5928      Widget w;
5929      XEvent *event;
5930      String *prms;
5931      Cardinal *nprms;
5932 {
5933     if (ICSInputBoxUp) {
5934         ICSInputBoxPopDown();
5935     } else {
5936         ICSInputBoxPopUp();
5937     }
5938 }
5939
5940 void AcceptProc(w, event, prms, nprms)
5941      Widget w;
5942      XEvent *event;
5943      String *prms;
5944      Cardinal *nprms;
5945 {
5946     AcceptEvent();
5947 }
5948
5949 void DeclineProc(w, event, prms, nprms)
5950      Widget w;
5951      XEvent *event;
5952      String *prms;
5953      Cardinal *nprms;
5954 {
5955     DeclineEvent();
5956 }
5957
5958 void RematchProc(w, event, prms, nprms)
5959      Widget w;
5960      XEvent *event;
5961      String *prms;
5962      Cardinal *nprms;
5963 {
5964     RematchEvent();
5965 }
5966
5967 void CallFlagProc(w, event, prms, nprms)
5968      Widget w;
5969      XEvent *event;
5970      String *prms;
5971      Cardinal *nprms;
5972 {
5973     CallFlagEvent();
5974 }
5975
5976 void DrawProc(w, event, prms, nprms)
5977      Widget w;
5978      XEvent *event;
5979      String *prms;
5980      Cardinal *nprms;
5981 {
5982     DrawEvent();
5983 }
5984
5985 void AbortProc(w, event, prms, nprms)
5986      Widget w;
5987      XEvent *event;
5988      String *prms;
5989      Cardinal *nprms;
5990 {
5991     AbortEvent();
5992 }
5993
5994 void AdjournProc(w, event, prms, nprms)
5995      Widget w;
5996      XEvent *event;
5997      String *prms;
5998      Cardinal *nprms;
5999 {
6000     AdjournEvent();
6001 }
6002
6003 void ResignProc(w, event, prms, nprms)
6004      Widget w;
6005      XEvent *event;
6006      String *prms;
6007      Cardinal *nprms;
6008 {
6009     ResignEvent();
6010 }
6011
6012 void AdjuWhiteProc(w, event, prms, nprms)
6013      Widget w;
6014      XEvent *event;
6015      String *prms;
6016      Cardinal *nprms;
6017 {
6018     UserAdjudicationEvent(+1);
6019 }
6020
6021 void AdjuBlackProc(w, event, prms, nprms)
6022      Widget w;
6023      XEvent *event;
6024      String *prms;
6025      Cardinal *nprms;
6026 {
6027     UserAdjudicationEvent(-1);
6028 }
6029
6030 void AdjuDrawProc(w, event, prms, nprms)
6031      Widget w;
6032      XEvent *event;
6033      String *prms;
6034      Cardinal *nprms;
6035 {
6036     UserAdjudicationEvent(0);
6037 }
6038
6039 void EnterKeyProc(w, event, prms, nprms)
6040      Widget w;
6041      XEvent *event;
6042      String *prms;
6043      Cardinal *nprms;
6044 {
6045     if (ICSInputBoxUp == True)
6046       ICSInputSendText();
6047 }
6048
6049 void StopObservingProc(w, event, prms, nprms)
6050      Widget w;
6051      XEvent *event;
6052      String *prms;
6053      Cardinal *nprms;
6054 {
6055     StopObservingEvent();
6056 }
6057
6058 void StopExaminingProc(w, event, prms, nprms)
6059      Widget w;
6060      XEvent *event;
6061      String *prms;
6062      Cardinal *nprms;
6063 {
6064     StopExaminingEvent();
6065 }
6066
6067
6068 void ForwardProc(w, event, prms, nprms)
6069      Widget w;
6070      XEvent *event;
6071      String *prms;
6072      Cardinal *nprms;
6073 {
6074     ForwardEvent();
6075 }
6076
6077
6078 void BackwardProc(w, event, prms, nprms)
6079      Widget w;
6080      XEvent *event;
6081      String *prms;
6082      Cardinal *nprms;
6083 {
6084     BackwardEvent();
6085 }
6086
6087 void ToStartProc(w, event, prms, nprms)
6088      Widget w;
6089      XEvent *event;
6090      String *prms;
6091      Cardinal *nprms;
6092 {
6093     ToStartEvent();
6094 }
6095
6096 void ToEndProc(w, event, prms, nprms)
6097      Widget w;
6098      XEvent *event;
6099      String *prms;
6100      Cardinal *nprms;
6101 {
6102     ToEndEvent();
6103 }
6104
6105 void RevertProc(w, event, prms, nprms)
6106      Widget w;
6107      XEvent *event;
6108      String *prms;
6109      Cardinal *nprms;
6110 {
6111     RevertEvent();
6112 }
6113
6114 void TruncateGameProc(w, event, prms, nprms)
6115      Widget w;
6116      XEvent *event;
6117      String *prms;
6118      Cardinal *nprms;
6119 {
6120     TruncateGameEvent();
6121 }
6122 void RetractMoveProc(w, event, prms, nprms)
6123      Widget w;
6124      XEvent *event;
6125      String *prms;
6126      Cardinal *nprms;
6127 {
6128     RetractMoveEvent();
6129 }
6130
6131 void MoveNowProc(w, event, prms, nprms)
6132      Widget w;
6133      XEvent *event;
6134      String *prms;
6135      Cardinal *nprms;
6136 {
6137     MoveNowEvent();
6138 }
6139
6140
6141 void AlwaysQueenProc(w, event, prms, nprms)
6142      Widget w;
6143      XEvent *event;
6144      String *prms;
6145      Cardinal *nprms;
6146 {
6147     Arg args[16];
6148
6149     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6150
6151     if (appData.alwaysPromoteToQueen) {
6152         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6153     } else {
6154         XtSetArg(args[0], XtNleftBitmap, None);
6155     }
6156     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6157                 args, 1);
6158 }
6159
6160 void AnimateDraggingProc(w, event, prms, nprms)
6161      Widget w;
6162      XEvent *event;
6163      String *prms;
6164      Cardinal *nprms;
6165 {
6166     Arg args[16];
6167
6168     appData.animateDragging = !appData.animateDragging;
6169
6170     if (appData.animateDragging) {
6171         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6172         CreateAnimVars();
6173     } else {
6174         XtSetArg(args[0], XtNleftBitmap, None);
6175     }
6176     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6177                 args, 1);
6178 }
6179
6180 void AnimateMovingProc(w, event, prms, nprms)
6181      Widget w;
6182      XEvent *event;
6183      String *prms;
6184      Cardinal *nprms;
6185 {
6186     Arg args[16];
6187
6188     appData.animate = !appData.animate;
6189
6190     if (appData.animate) {
6191         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6192         CreateAnimVars();
6193     } else {
6194         XtSetArg(args[0], XtNleftBitmap, None);
6195     }
6196     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6197                 args, 1);
6198 }
6199
6200 void AutocommProc(w, event, prms, nprms)
6201      Widget w;
6202      XEvent *event;
6203      String *prms;
6204      Cardinal *nprms;
6205 {
6206     Arg args[16];
6207
6208     appData.autoComment = !appData.autoComment;
6209
6210     if (appData.autoComment) {
6211         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6212     } else {
6213         XtSetArg(args[0], XtNleftBitmap, None);
6214     }
6215     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6216                 args, 1);
6217 }
6218
6219
6220 void AutoflagProc(w, event, prms, nprms)
6221      Widget w;
6222      XEvent *event;
6223      String *prms;
6224      Cardinal *nprms;
6225 {
6226     Arg args[16];
6227
6228     appData.autoCallFlag = !appData.autoCallFlag;
6229
6230     if (appData.autoCallFlag) {
6231         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6232     } else {
6233         XtSetArg(args[0], XtNleftBitmap, None);
6234     }
6235     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6236                 args, 1);
6237 }
6238
6239 void AutoflipProc(w, event, prms, nprms)
6240      Widget w;
6241      XEvent *event;
6242      String *prms;
6243      Cardinal *nprms;
6244 {
6245     Arg args[16];
6246
6247     appData.autoFlipView = !appData.autoFlipView;
6248
6249     if (appData.autoFlipView) {
6250         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6251     } else {
6252         XtSetArg(args[0], XtNleftBitmap, None);
6253     }
6254     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6255                 args, 1);
6256 }
6257
6258 void AutobsProc(w, event, prms, nprms)
6259      Widget w;
6260      XEvent *event;
6261      String *prms;
6262      Cardinal *nprms;
6263 {
6264     Arg args[16];
6265
6266     appData.autoObserve = !appData.autoObserve;
6267
6268     if (appData.autoObserve) {
6269         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6270     } else {
6271         XtSetArg(args[0], XtNleftBitmap, None);
6272     }
6273     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6274                 args, 1);
6275 }
6276
6277 void AutoraiseProc(w, event, prms, nprms)
6278      Widget w;
6279      XEvent *event;
6280      String *prms;
6281      Cardinal *nprms;
6282 {
6283     Arg args[16];
6284
6285     appData.autoRaiseBoard = !appData.autoRaiseBoard;
6286
6287     if (appData.autoRaiseBoard) {
6288         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6289     } else {
6290         XtSetArg(args[0], XtNleftBitmap, None);
6291     }
6292     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
6293                 args, 1);
6294 }
6295
6296 void AutosaveProc(w, event, prms, nprms)
6297      Widget w;
6298      XEvent *event;
6299      String *prms;
6300      Cardinal *nprms;
6301 {
6302     Arg args[16];
6303
6304     appData.autoSaveGames = !appData.autoSaveGames;
6305
6306     if (appData.autoSaveGames) {
6307         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6308     } else {
6309         XtSetArg(args[0], XtNleftBitmap, None);
6310     }
6311     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
6312                 args, 1);
6313 }
6314
6315 void BlindfoldProc(w, event, prms, nprms)
6316      Widget w;
6317      XEvent *event;
6318      String *prms;
6319      Cardinal *nprms;
6320 {
6321     Arg args[16];
6322
6323     appData.blindfold = !appData.blindfold;
6324
6325     if (appData.blindfold) {
6326         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6327     } else {
6328         XtSetArg(args[0], XtNleftBitmap, None);
6329     }
6330     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6331                 args, 1);
6332
6333     DrawPosition(True, NULL);
6334 }
6335
6336 void TestLegalityProc(w, event, prms, nprms)
6337      Widget w;
6338      XEvent *event;
6339      String *prms;
6340      Cardinal *nprms;
6341 {
6342     Arg args[16];
6343
6344     appData.testLegality = !appData.testLegality;
6345
6346     if (appData.testLegality) {
6347         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6348     } else {
6349         XtSetArg(args[0], XtNleftBitmap, None);
6350     }
6351     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6352                 args, 1);
6353 }
6354
6355
6356 void FlashMovesProc(w, event, prms, nprms)
6357      Widget w;
6358      XEvent *event;
6359      String *prms;
6360      Cardinal *nprms;
6361 {
6362     Arg args[16];
6363
6364     if (appData.flashCount == 0) {
6365         appData.flashCount = 3;
6366     } else {
6367         appData.flashCount = -appData.flashCount;
6368     }
6369
6370     if (appData.flashCount > 0) {
6371         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6372     } else {
6373         XtSetArg(args[0], XtNleftBitmap, None);
6374     }
6375     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6376                 args, 1);
6377 }
6378
6379 void FlipViewProc(w, event, prms, nprms)
6380      Widget w;
6381      XEvent *event;
6382      String *prms;
6383      Cardinal *nprms;
6384 {
6385     flipView = !flipView;
6386     DrawPosition(True, NULL);
6387 }
6388
6389 void GetMoveListProc(w, event, prms, nprms)
6390      Widget w;
6391      XEvent *event;
6392      String *prms;
6393      Cardinal *nprms;
6394 {
6395     Arg args[16];
6396
6397     appData.getMoveList = !appData.getMoveList;
6398
6399     if (appData.getMoveList) {
6400         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6401         GetMoveListEvent();
6402     } else {
6403         XtSetArg(args[0], XtNleftBitmap, None);
6404     }
6405     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
6406                 args, 1);
6407 }
6408
6409 #if HIGHDRAG
6410 void HighlightDraggingProc(w, event, prms, nprms)
6411      Widget w;
6412      XEvent *event;
6413      String *prms;
6414      Cardinal *nprms;
6415 {
6416     Arg args[16];
6417
6418     appData.highlightDragging = !appData.highlightDragging;
6419
6420     if (appData.highlightDragging) {
6421         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6422     } else {
6423         XtSetArg(args[0], XtNleftBitmap, None);
6424     }
6425     XtSetValues(XtNameToWidget(menuBarWidget,
6426                                "menuOptions.Highlight Dragging"), args, 1);
6427 }
6428 #endif
6429
6430 void HighlightLastMoveProc(w, event, prms, nprms)
6431      Widget w;
6432      XEvent *event;
6433      String *prms;
6434      Cardinal *nprms;
6435 {
6436     Arg args[16];
6437
6438     appData.highlightLastMove = !appData.highlightLastMove;
6439
6440     if (appData.highlightLastMove) {
6441         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6442     } else {
6443         XtSetArg(args[0], XtNleftBitmap, None);
6444     }
6445     XtSetValues(XtNameToWidget(menuBarWidget,
6446                                "menuOptions.Highlight Last Move"), args, 1);
6447 }
6448
6449 void IcsAlarmProc(w, event, prms, nprms)
6450      Widget w;
6451      XEvent *event;
6452      String *prms;
6453      Cardinal *nprms;
6454 {
6455     Arg args[16];
6456
6457     appData.icsAlarm = !appData.icsAlarm;
6458
6459     if (appData.icsAlarm) {
6460         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6461     } else {
6462         XtSetArg(args[0], XtNleftBitmap, None);
6463     }
6464     XtSetValues(XtNameToWidget(menuBarWidget,
6465                                "menuOptions.ICS Alarm"), args, 1);
6466 }
6467
6468 void MoveSoundProc(w, event, prms, nprms)
6469      Widget w;
6470      XEvent *event;
6471      String *prms;
6472      Cardinal *nprms;
6473 {
6474     Arg args[16];
6475
6476     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6477
6478     if (appData.ringBellAfterMoves) {
6479         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6480     } else {
6481         XtSetArg(args[0], XtNleftBitmap, None);
6482     }
6483     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6484                 args, 1);
6485 }
6486
6487
6488 void OldSaveStyleProc(w, event, prms, nprms)
6489      Widget w;
6490      XEvent *event;
6491      String *prms;
6492      Cardinal *nprms;
6493 {
6494     Arg args[16];
6495
6496     appData.oldSaveStyle = !appData.oldSaveStyle;
6497
6498     if (appData.oldSaveStyle) {
6499         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6500     } else {
6501         XtSetArg(args[0], XtNleftBitmap, None);
6502     }
6503     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
6504                 args, 1);
6505 }
6506
6507 void PeriodicUpdatesProc(w, event, prms, nprms)
6508      Widget w;
6509      XEvent *event;
6510      String *prms;
6511      Cardinal *nprms;
6512 {
6513     Arg args[16];
6514
6515     PeriodicUpdatesEvent(!appData.periodicUpdates);
6516
6517     if (appData.periodicUpdates) {
6518         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6519     } else {
6520         XtSetArg(args[0], XtNleftBitmap, None);
6521     }
6522     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6523                 args, 1);
6524 }
6525
6526 void PonderNextMoveProc(w, event, prms, nprms)
6527      Widget w;
6528      XEvent *event;
6529      String *prms;
6530      Cardinal *nprms;
6531 {
6532     Arg args[16];
6533
6534     PonderNextMoveEvent(!appData.ponderNextMove);
6535
6536     if (appData.ponderNextMove) {
6537         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6538     } else {
6539         XtSetArg(args[0], XtNleftBitmap, None);
6540     }
6541     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6542                 args, 1);
6543 }
6544
6545 void PopupExitMessageProc(w, event, prms, nprms)
6546      Widget w;
6547      XEvent *event;
6548      String *prms;
6549      Cardinal *nprms;
6550 {
6551     Arg args[16];
6552
6553     appData.popupExitMessage = !appData.popupExitMessage;
6554
6555     if (appData.popupExitMessage) {
6556         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6557     } else {
6558         XtSetArg(args[0], XtNleftBitmap, None);
6559     }
6560     XtSetValues(XtNameToWidget(menuBarWidget,
6561                                "menuOptions.Popup Exit Message"), args, 1);
6562 }
6563
6564 void PopupMoveErrorsProc(w, event, prms, nprms)
6565      Widget w;
6566      XEvent *event;
6567      String *prms;
6568      Cardinal *nprms;
6569 {
6570     Arg args[16];
6571
6572     appData.popupMoveErrors = !appData.popupMoveErrors;
6573
6574     if (appData.popupMoveErrors) {
6575         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6576     } else {
6577         XtSetArg(args[0], XtNleftBitmap, None);
6578     }
6579     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6580                 args, 1);
6581 }
6582
6583 void PremoveProc(w, event, prms, nprms)
6584      Widget w;
6585      XEvent *event;
6586      String *prms;
6587      Cardinal *nprms;
6588 {
6589     Arg args[16];
6590
6591     appData.premove = !appData.premove;
6592
6593     if (appData.premove) {
6594         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6595     } else {
6596         XtSetArg(args[0], XtNleftBitmap, None);
6597     }
6598     XtSetValues(XtNameToWidget(menuBarWidget,
6599                                "menuOptions.Premove"), args, 1);
6600 }
6601
6602 void QuietPlayProc(w, event, prms, nprms)
6603      Widget w;
6604      XEvent *event;
6605      String *prms;
6606      Cardinal *nprms;
6607 {
6608     Arg args[16];
6609
6610     appData.quietPlay = !appData.quietPlay;
6611
6612     if (appData.quietPlay) {
6613         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6614     } else {
6615         XtSetArg(args[0], XtNleftBitmap, None);
6616     }
6617     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
6618                 args, 1);
6619 }
6620
6621 void ShowCoordsProc(w, event, prms, nprms)
6622      Widget w;
6623      XEvent *event;
6624      String *prms;
6625      Cardinal *nprms;
6626 {
6627     Arg args[16];
6628
6629     appData.showCoords = !appData.showCoords;
6630
6631     if (appData.showCoords) {
6632         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6633     } else {
6634         XtSetArg(args[0], XtNleftBitmap, None);
6635     }
6636     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6637                 args, 1);
6638
6639     DrawPosition(True, NULL);
6640 }
6641
6642 void ShowThinkingProc(w, event, prms, nprms)
6643      Widget w;
6644      XEvent *event;
6645      String *prms;
6646      Cardinal *nprms;
6647 {
6648     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6649     ShowThinkingEvent();
6650 }
6651
6652 void HideThinkingProc(w, event, prms, nprms)
6653      Widget w;
6654      XEvent *event;
6655      String *prms;
6656      Cardinal *nprms;
6657 {
6658     Arg args[16];
6659
6660     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6661     ShowThinkingEvent();
6662
6663     if (appData.hideThinkingFromHuman) {
6664         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6665     } else {
6666         XtSetArg(args[0], XtNleftBitmap, None);
6667     }
6668     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6669                 args, 1);
6670 }
6671
6672 void SaveOnExitProc(w, event, prms, nprms)
6673      Widget w;
6674      XEvent *event;
6675      String *prms;
6676      Cardinal *nprms;
6677 {
6678     Arg args[16];
6679
6680     saveSettingsOnExit = !saveSettingsOnExit;
6681
6682     if (saveSettingsOnExit) {
6683         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6684     } else {
6685         XtSetArg(args[0], XtNleftBitmap, None);
6686     }
6687     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6688                 args, 1);
6689 }
6690
6691 void SaveSettingsProc(w, event, prms, nprms)
6692      Widget w;
6693      XEvent *event;
6694      String *prms;
6695      Cardinal *nprms;
6696 {
6697      SaveSettings(settingsFileName);
6698 }
6699
6700 void InfoProc(w, event, prms, nprms)
6701      Widget w;
6702      XEvent *event;
6703      String *prms;
6704      Cardinal *nprms;
6705 {
6706     char buf[MSG_SIZ];
6707     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6708             INFODIR, INFOFILE);
6709     system(buf);
6710 }
6711
6712 void ManProc(w, event, prms, nprms)
6713      Widget w;
6714      XEvent *event;
6715      String *prms;
6716      Cardinal *nprms;
6717 {
6718     char buf[MSG_SIZ];
6719     String name;
6720     if (nprms && *nprms > 0)
6721       name = prms[0];
6722     else
6723       name = "xboard";
6724     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6725     system(buf);
6726 }
6727
6728 void HintProc(w, event, prms, nprms)
6729      Widget w;
6730      XEvent *event;
6731      String *prms;
6732      Cardinal *nprms;
6733 {
6734     HintEvent();
6735 }
6736
6737 void BookProc(w, event, prms, nprms)
6738      Widget w;
6739      XEvent *event;
6740      String *prms;
6741      Cardinal *nprms;
6742 {
6743     BookEvent();
6744 }
6745
6746 void AboutProc(w, event, prms, nprms)
6747      Widget w;
6748      XEvent *event;
6749      String *prms;
6750      Cardinal *nprms;
6751 {
6752     char buf[MSG_SIZ];
6753 #if ZIPPY
6754     char *zippy = " (with Zippy code)";
6755 #else
6756     char *zippy = "";
6757 #endif
6758     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6759             programVersion, zippy,
6760             "Copyright 1991 Digital Equipment Corporation",
6761             "Enhancements Copyright 1992-2009 Free Software Foundation",
6762             "Enhancements Copyright 2005 Alessandro Scotti",
6763             PACKAGE, " is free software and carries NO WARRANTY;",
6764             "see the file COPYING for more information.");
6765     ErrorPopUp(_("About XBoard"), buf, FALSE);
6766 }
6767
6768 void DebugProc(w, event, prms, nprms)
6769      Widget w;
6770      XEvent *event;
6771      String *prms;
6772      Cardinal *nprms;
6773 {
6774     appData.debugMode = !appData.debugMode;
6775 }
6776
6777 void AboutGameProc(w, event, prms, nprms)
6778      Widget w;
6779      XEvent *event;
6780      String *prms;
6781      Cardinal *nprms;
6782 {
6783     AboutGameEvent();
6784 }
6785
6786 void NothingProc(w, event, prms, nprms)
6787      Widget w;
6788      XEvent *event;
6789      String *prms;
6790      Cardinal *nprms;
6791 {
6792     return;
6793 }
6794
6795 void Iconify(w, event, prms, nprms)
6796      Widget w;
6797      XEvent *event;
6798      String *prms;
6799      Cardinal *nprms;
6800 {
6801     Arg args[16];
6802
6803     fromX = fromY = -1;
6804     XtSetArg(args[0], XtNiconic, True);
6805     XtSetValues(shellWidget, args, 1);
6806 }
6807
6808 void DisplayMessage(message, extMessage)
6809      char *message, *extMessage;
6810 {
6811   /* display a message in the message widget */
6812   
6813   char buf[MSG_SIZ];
6814   Arg arg;
6815   
6816   if (extMessage) 
6817     {
6818       if (*message) 
6819         {
6820           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
6821           message = buf;
6822         } 
6823       else 
6824         {
6825           message = extMessage;
6826         };
6827     };
6828   
6829   /* need to test if messageWidget already exists, since this function
6830      can also be called during the startup, if for example a Xresource
6831      is not set up correctly */
6832   if(messageWidget)
6833     {
6834       XtSetArg(arg, XtNlabel, message);
6835       XtSetValues(messageWidget, &arg, 1);
6836     };
6837   
6838   return;
6839 }
6840
6841 void DisplayTitle(text)
6842      char *text;
6843 {
6844     Arg args[16];
6845     int i;
6846     char title[MSG_SIZ];
6847     char icon[MSG_SIZ];
6848
6849     if (text == NULL) text = "";
6850
6851     if (appData.titleInWindow) {
6852         i = 0;
6853         XtSetArg(args[i], XtNlabel, text);   i++;
6854         XtSetValues(titleWidget, args, i);
6855     }
6856
6857     if (*text != NULLCHAR) {
6858         strcpy(icon, text);
6859         strcpy(title, text);
6860     } else if (appData.icsActive) {
6861         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6862         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6863     } else if (appData.cmailGameName[0] != NULLCHAR) {
6864         snprintf(icon, sizeof(icon), "%s", "CMail");
6865         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6866 #ifdef GOTHIC
6867     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6868     } else if (gameInfo.variant == VariantGothic) {
6869         strcpy(icon, programName);
6870         strcpy(title, GOTHIC);
6871 #endif
6872 #ifdef FALCON
6873     } else if (gameInfo.variant == VariantFalcon) {
6874         strcpy(icon, programName);
6875         strcpy(title, FALCON);
6876 #endif
6877     } else if (appData.noChessProgram) {
6878         strcpy(icon, programName);
6879         strcpy(title, programName);
6880     } else {
6881         strcpy(icon, first.tidy);
6882         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6883     }
6884     i = 0;
6885     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6886     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6887     XtSetValues(shellWidget, args, i);
6888 }
6889
6890
6891 void DisplayError(message, error)
6892      String message;
6893      int error;
6894 {
6895     char buf[MSG_SIZ];
6896
6897     if (error == 0) {
6898         if (appData.debugMode || appData.matchMode) {
6899             fprintf(stderr, "%s: %s\n", programName, message);
6900         }
6901     } else {
6902         if (appData.debugMode || appData.matchMode) {
6903             fprintf(stderr, "%s: %s: %s\n",
6904                     programName, message, strerror(error));
6905         }
6906         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6907         message = buf;
6908     }
6909     ErrorPopUp(_("Error"), message, FALSE);
6910 }
6911
6912
6913 void DisplayMoveError(message)
6914      String message;
6915 {
6916     fromX = fromY = -1;
6917     ClearHighlights();
6918     DrawPosition(FALSE, NULL);
6919     if (appData.debugMode || appData.matchMode) {
6920         fprintf(stderr, "%s: %s\n", programName, message);
6921     }
6922     if (appData.popupMoveErrors) {
6923         ErrorPopUp(_("Error"), message, FALSE);
6924     } else {
6925         DisplayMessage(message, "");
6926     }
6927 }
6928
6929
6930 void DisplayFatalError(message, error, status)
6931      String message;
6932      int error, status;
6933 {
6934     char buf[MSG_SIZ];
6935
6936     errorExitStatus = status;
6937     if (error == 0) {
6938         fprintf(stderr, "%s: %s\n", programName, message);
6939     } else {
6940         fprintf(stderr, "%s: %s: %s\n",
6941                 programName, message, strerror(error));
6942         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6943         message = buf;
6944     }
6945     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6946       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6947     } else {
6948       ExitEvent(status);
6949     }
6950 }
6951
6952 void DisplayInformation(message)
6953      String message;
6954 {
6955     ErrorPopDown();
6956     ErrorPopUp(_("Information"), message, TRUE);
6957 }
6958
6959 void DisplayNote(message)
6960      String message;
6961 {
6962     ErrorPopDown();
6963     ErrorPopUp(_("Note"), message, FALSE);
6964 }
6965
6966 static int
6967 NullXErrorCheck(dpy, error_event)
6968      Display *dpy;
6969      XErrorEvent *error_event;
6970 {
6971     return 0;
6972 }
6973
6974 void DisplayIcsInteractionTitle(message)
6975      String message;
6976 {
6977   if (oldICSInteractionTitle == NULL) {
6978     /* Magic to find the old window title, adapted from vim */
6979     char *wina = getenv("WINDOWID");
6980     if (wina != NULL) {
6981       Window win = (Window) atoi(wina);
6982       Window root, parent, *children;
6983       unsigned int nchildren;
6984       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6985       for (;;) {
6986         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6987         if (!XQueryTree(xDisplay, win, &root, &parent,
6988                         &children, &nchildren)) break;
6989         if (children) XFree((void *)children);
6990         if (parent == root || parent == 0) break;
6991         win = parent;
6992       }
6993       XSetErrorHandler(oldHandler);
6994     }
6995     if (oldICSInteractionTitle == NULL) {
6996       oldICSInteractionTitle = "xterm";
6997     }
6998   }
6999   printf("\033]0;%s\007", message);
7000   fflush(stdout);
7001 }
7002
7003 char pendingReplyPrefix[MSG_SIZ];
7004 ProcRef pendingReplyPR;
7005
7006 void AskQuestionProc(w, event, prms, nprms)
7007      Widget w;
7008      XEvent *event;
7009      String *prms;
7010      Cardinal *nprms;
7011 {
7012     if (*nprms != 4) {
7013         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7014                 *nprms);
7015         return;
7016     }
7017     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7018 }
7019
7020 void AskQuestionPopDown()
7021 {
7022     if (!askQuestionUp) return;
7023     XtPopdown(askQuestionShell);
7024     XtDestroyWidget(askQuestionShell);
7025     askQuestionUp = False;
7026 }
7027
7028 void AskQuestionReplyAction(w, event, prms, nprms)
7029      Widget w;
7030      XEvent *event;
7031      String *prms;
7032      Cardinal *nprms;
7033 {
7034     char buf[MSG_SIZ];
7035     int err;
7036     String reply;
7037
7038     reply = XawDialogGetValueString(w = XtParent(w));
7039     strcpy(buf, pendingReplyPrefix);
7040     if (*buf) strcat(buf, " ");
7041     strcat(buf, reply);
7042     strcat(buf, "\n");
7043     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7044     AskQuestionPopDown();
7045
7046     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7047 }
7048
7049 void AskQuestionCallback(w, client_data, call_data)
7050      Widget w;
7051      XtPointer client_data, call_data;
7052 {
7053     String name;
7054     Arg args[16];
7055
7056     XtSetArg(args[0], XtNlabel, &name);
7057     XtGetValues(w, args, 1);
7058
7059     if (strcmp(name, _("cancel")) == 0) {
7060         AskQuestionPopDown();
7061     } else {
7062         AskQuestionReplyAction(w, NULL, NULL, NULL);
7063     }
7064 }
7065
7066 void AskQuestion(title, question, replyPrefix, pr)
7067      char *title, *question, *replyPrefix;
7068      ProcRef pr;
7069 {
7070     Arg args[16];
7071     Widget popup, layout, dialog, edit;
7072     Window root, child;
7073     int x, y, i;
7074     int win_x, win_y;
7075     unsigned int mask;
7076
7077     strcpy(pendingReplyPrefix, replyPrefix);
7078     pendingReplyPR = pr;
7079
7080     i = 0;
7081     XtSetArg(args[i], XtNresizable, True); i++;
7082     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7083     askQuestionShell = popup =
7084       XtCreatePopupShell(title, transientShellWidgetClass,
7085                          shellWidget, args, i);
7086
7087     layout =
7088       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7089                             layoutArgs, XtNumber(layoutArgs));
7090
7091     i = 0;
7092     XtSetArg(args[i], XtNlabel, question); i++;
7093     XtSetArg(args[i], XtNvalue, ""); i++;
7094     XtSetArg(args[i], XtNborderWidth, 0); i++;
7095     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7096                                    layout, args, i);
7097
7098     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7099                        (XtPointer) dialog);
7100     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7101                        (XtPointer) dialog);
7102
7103     XtRealizeWidget(popup);
7104     CatchDeleteWindow(popup, "AskQuestionPopDown");
7105
7106     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7107                   &x, &y, &win_x, &win_y, &mask);
7108
7109     XtSetArg(args[0], XtNx, x - 10);
7110     XtSetArg(args[1], XtNy, y - 30);
7111     XtSetValues(popup, args, 2);
7112
7113     XtPopup(popup, XtGrabExclusive);
7114     askQuestionUp = True;
7115
7116     edit = XtNameToWidget(dialog, "*value");
7117     XtSetKeyboardFocus(popup, edit);
7118 }
7119
7120
7121 void
7122 PlaySound(name)
7123      char *name;
7124 {
7125   if (*name == NULLCHAR) {
7126     return;
7127   } else if (strcmp(name, "$") == 0) {
7128     putc(BELLCHAR, stderr);
7129   } else {
7130     char buf[2048];
7131     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7132     system(buf);
7133   }
7134 }
7135
7136 void
7137 RingBell()
7138 {
7139   PlaySound(appData.soundMove);
7140 }
7141
7142 void
7143 PlayIcsWinSound()
7144 {
7145   PlaySound(appData.soundIcsWin);
7146 }
7147
7148 void
7149 PlayIcsLossSound()
7150 {
7151   PlaySound(appData.soundIcsLoss);
7152 }
7153
7154 void
7155 PlayIcsDrawSound()
7156 {
7157   PlaySound(appData.soundIcsDraw);
7158 }
7159
7160 void
7161 PlayIcsUnfinishedSound()
7162 {
7163   PlaySound(appData.soundIcsUnfinished);
7164 }
7165
7166 void
7167 PlayAlarmSound()
7168 {
7169   PlaySound(appData.soundIcsAlarm);
7170 }
7171
7172 void
7173 EchoOn()
7174 {
7175     system("stty echo");
7176 }
7177
7178 void
7179 EchoOff()
7180 {
7181     system("stty -echo");
7182 }
7183
7184 void
7185 Colorize(cc, continuation)
7186      ColorClass cc;
7187      int continuation;
7188 {
7189     char buf[MSG_SIZ];
7190     int count, outCount, error;
7191
7192     if (textColors[(int)cc].bg > 0) {
7193         if (textColors[(int)cc].fg > 0) {
7194             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7195                     textColors[(int)cc].fg, textColors[(int)cc].bg);
7196         } else {
7197             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7198                     textColors[(int)cc].bg);
7199         }
7200     } else {
7201         if (textColors[(int)cc].fg > 0) {
7202             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7203                     textColors[(int)cc].fg);
7204         } else {
7205             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
7206         }
7207     }
7208     count = strlen(buf);
7209     outCount = OutputToProcess(NoProc, buf, count, &error);
7210     if (outCount < count) {
7211         DisplayFatalError(_("Error writing to display"), error, 1);
7212     }
7213
7214     if (continuation) return;
7215     switch (cc) {
7216     case ColorShout:
7217       PlaySound(appData.soundShout);
7218       break;
7219     case ColorSShout:
7220       PlaySound(appData.soundSShout);
7221       break;
7222     case ColorChannel1:
7223       PlaySound(appData.soundChannel1);
7224       break;
7225     case ColorChannel:
7226       PlaySound(appData.soundChannel);
7227       break;
7228     case ColorKibitz:
7229       PlaySound(appData.soundKibitz);
7230       break;
7231     case ColorTell:
7232       PlaySound(appData.soundTell);
7233       break;
7234     case ColorChallenge:
7235       PlaySound(appData.soundChallenge);
7236       break;
7237     case ColorRequest:
7238       PlaySound(appData.soundRequest);
7239       break;
7240     case ColorSeek:
7241       PlaySound(appData.soundSeek);
7242       break;
7243     case ColorNormal:
7244     case ColorNone:
7245     default:
7246       break;
7247     }
7248 }
7249
7250 char *UserName()
7251 {
7252     return getpwuid(getuid())->pw_name;
7253 }
7254
7255 static char *ExpandPathName(path)
7256      char *path;
7257 {
7258     static char static_buf[2000];
7259     char *d, *s, buf[2000];
7260     struct passwd *pwd;
7261
7262     s = path;
7263     d = static_buf;
7264
7265     while (*s && isspace(*s))
7266       ++s;
7267
7268     if (!*s) {
7269         *d = 0;
7270         return static_buf;
7271     }
7272
7273     if (*s == '~') {
7274         if (*(s+1) == '/') {
7275             strcpy(d, getpwuid(getuid())->pw_dir);
7276             strcat(d, s+1);
7277         }
7278         else {
7279             strcpy(buf, s+1);
7280             *strchr(buf, '/') = 0;
7281             pwd = getpwnam(buf);
7282             if (!pwd)
7283               {
7284                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7285                           buf, path);
7286                   return NULL;
7287               }
7288             strcpy(d, pwd->pw_dir);
7289             strcat(d, strchr(s+1, '/'));
7290         }
7291     }
7292     else
7293       strcpy(d, s);
7294
7295     return static_buf;
7296 }
7297
7298 char *HostName()
7299 {
7300     static char host_name[MSG_SIZ];
7301
7302 #if HAVE_GETHOSTNAME
7303     gethostname(host_name, MSG_SIZ);
7304     return host_name;
7305 #else  /* not HAVE_GETHOSTNAME */
7306 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7307     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7308     return host_name;
7309 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7310     return "localhost";
7311 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7312 #endif /* not HAVE_GETHOSTNAME */
7313 }
7314
7315 XtIntervalId delayedEventTimerXID = 0;
7316 DelayedEventCallback delayedEventCallback = 0;
7317
7318 void
7319 FireDelayedEvent()
7320 {
7321     delayedEventTimerXID = 0;
7322     delayedEventCallback();
7323 }
7324
7325 void
7326 ScheduleDelayedEvent(cb, millisec)
7327      DelayedEventCallback cb; long millisec;
7328 {
7329     if(delayedEventTimerXID && delayedEventCallback == cb)
7330         // [HGM] alive: replace, rather than add or flush identical event
7331         XtRemoveTimeOut(delayedEventTimerXID);
7332     delayedEventCallback = cb;
7333     delayedEventTimerXID =
7334       XtAppAddTimeOut(appContext, millisec,
7335                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7336 }
7337
7338 DelayedEventCallback
7339 GetDelayedEvent()
7340 {
7341   if (delayedEventTimerXID) {
7342     return delayedEventCallback;
7343   } else {
7344     return NULL;
7345   }
7346 }
7347
7348 void
7349 CancelDelayedEvent()
7350 {
7351   if (delayedEventTimerXID) {
7352     XtRemoveTimeOut(delayedEventTimerXID);
7353     delayedEventTimerXID = 0;
7354   }
7355 }
7356
7357 XtIntervalId loadGameTimerXID = 0;
7358
7359 int LoadGameTimerRunning()
7360 {
7361     return loadGameTimerXID != 0;
7362 }
7363
7364 int StopLoadGameTimer()
7365 {
7366     if (loadGameTimerXID != 0) {
7367         XtRemoveTimeOut(loadGameTimerXID);
7368         loadGameTimerXID = 0;
7369         return TRUE;
7370     } else {
7371         return FALSE;
7372     }
7373 }
7374
7375 void
7376 LoadGameTimerCallback(arg, id)
7377      XtPointer arg;
7378      XtIntervalId *id;
7379 {
7380     loadGameTimerXID = 0;
7381     AutoPlayGameLoop();
7382 }
7383
7384 void
7385 StartLoadGameTimer(millisec)
7386      long millisec;
7387 {
7388     loadGameTimerXID =
7389       XtAppAddTimeOut(appContext, millisec,
7390                       (XtTimerCallbackProc) LoadGameTimerCallback,
7391                       (XtPointer) 0);
7392 }
7393
7394 XtIntervalId analysisClockXID = 0;
7395
7396 void
7397 AnalysisClockCallback(arg, id)
7398      XtPointer arg;
7399      XtIntervalId *id;
7400 {
7401     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7402          || appData.icsEngineAnalyze) { // [DM]
7403         AnalysisPeriodicEvent(0);
7404         StartAnalysisClock();
7405     }
7406 }
7407
7408 void
7409 StartAnalysisClock()
7410 {
7411     analysisClockXID =
7412       XtAppAddTimeOut(appContext, 2000,
7413                       (XtTimerCallbackProc) AnalysisClockCallback,
7414                       (XtPointer) 0);
7415 }
7416
7417 XtIntervalId clockTimerXID = 0;
7418
7419 int ClockTimerRunning()
7420 {
7421     return clockTimerXID != 0;
7422 }
7423
7424 int StopClockTimer()
7425 {
7426     if (clockTimerXID != 0) {
7427         XtRemoveTimeOut(clockTimerXID);
7428         clockTimerXID = 0;
7429         return TRUE;
7430     } else {
7431         return FALSE;
7432     }
7433 }
7434
7435 void
7436 ClockTimerCallback(arg, id)
7437      XtPointer arg;
7438      XtIntervalId *id;
7439 {
7440     clockTimerXID = 0;
7441     DecrementClocks();
7442 }
7443
7444 void
7445 StartClockTimer(millisec)
7446      long millisec;
7447 {
7448     clockTimerXID =
7449       XtAppAddTimeOut(appContext, millisec,
7450                       (XtTimerCallbackProc) ClockTimerCallback,
7451                       (XtPointer) 0);
7452 }
7453
7454 void
7455 DisplayTimerLabel(w, color, timer, highlight)
7456      Widget w;
7457      char *color;
7458      long timer;
7459      int highlight;
7460 {
7461     char buf[MSG_SIZ];
7462     Arg args[16];
7463
7464     /* check for low time warning */
7465     Pixel foregroundOrWarningColor = timerForegroundPixel;
7466
7467     if (timer > 0 &&
7468         appData.lowTimeWarning && 
7469         (timer / 1000) < appData.icsAlarmTime)
7470       foregroundOrWarningColor = lowTimeWarningColor;
7471
7472     if (appData.clockMode) {
7473         sprintf(buf, "%s: %s", color, TimeString(timer));
7474         XtSetArg(args[0], XtNlabel, buf);
7475     } else {
7476         sprintf(buf, "%s  ", color);
7477         XtSetArg(args[0], XtNlabel, buf);
7478     }
7479
7480     if (highlight) {
7481
7482         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7483         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7484     } else {
7485         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7486         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7487     }
7488
7489     XtSetValues(w, args, 3);
7490 }
7491
7492 void
7493 DisplayWhiteClock(timeRemaining, highlight)
7494      long timeRemaining;
7495      int highlight;
7496 {
7497     Arg args[16];
7498
7499     if(appData.noGUI) return;
7500     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7501     if (highlight && iconPixmap == bIconPixmap) {
7502         iconPixmap = wIconPixmap;
7503         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7504         XtSetValues(shellWidget, args, 1);
7505     }
7506 }
7507
7508 void
7509 DisplayBlackClock(timeRemaining, highlight)
7510      long timeRemaining;
7511      int highlight;
7512 {
7513     Arg args[16];
7514
7515     if(appData.noGUI) return;
7516     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7517     if (highlight && iconPixmap == wIconPixmap) {
7518         iconPixmap = bIconPixmap;
7519         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7520         XtSetValues(shellWidget, args, 1);
7521     }
7522 }
7523
7524 #define CPNone 0
7525 #define CPReal 1
7526 #define CPComm 2
7527 #define CPSock 3
7528 #define CPLoop 4
7529 typedef int CPKind;
7530
7531 typedef struct {
7532     CPKind kind;
7533     int pid;
7534     int fdTo, fdFrom;
7535 } ChildProc;
7536
7537
7538 int StartChildProcess(cmdLine, dir, pr)
7539      char *cmdLine;
7540      char *dir;
7541      ProcRef *pr;
7542 {
7543     char *argv[64], *p;
7544     int i, pid;
7545     int to_prog[2], from_prog[2];
7546     ChildProc *cp;
7547     char buf[MSG_SIZ];
7548
7549     if (appData.debugMode) {
7550         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7551     }
7552
7553     /* We do NOT feed the cmdLine to the shell; we just
7554        parse it into blank-separated arguments in the
7555        most simple-minded way possible.
7556        */
7557     i = 0;
7558     strcpy(buf, cmdLine);
7559     p = buf;
7560     for (;;) {
7561         argv[i++] = p;
7562         p = strchr(p, ' ');
7563         if (p == NULL) break;
7564         *p++ = NULLCHAR;
7565     }
7566     argv[i] = NULL;
7567
7568     SetUpChildIO(to_prog, from_prog);
7569
7570     if ((pid = fork()) == 0) {
7571         /* Child process */
7572         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7573         close(to_prog[1]);     // first close the unused pipe ends
7574         close(from_prog[0]);
7575         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7576         dup2(from_prog[1], 1);
7577         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7578         close(from_prog[1]);                   // and closing again loses one of the pipes!
7579         if(fileno(stderr) >= 2) // better safe than sorry...
7580                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7581
7582         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7583             perror(dir);
7584             exit(1);
7585         }
7586
7587         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7588
7589         execvp(argv[0], argv);
7590
7591         /* If we get here, exec failed */
7592         perror(argv[0]);
7593         exit(1);
7594     }
7595
7596     /* Parent process */
7597     close(to_prog[0]);
7598     close(from_prog[1]);
7599
7600     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7601     cp->kind = CPReal;
7602     cp->pid = pid;
7603     cp->fdFrom = from_prog[0];
7604     cp->fdTo = to_prog[1];
7605     *pr = (ProcRef) cp;
7606     return 0;
7607 }
7608
7609 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7610 static RETSIGTYPE AlarmCallBack(int n)
7611 {
7612     return;
7613 }
7614
7615 void
7616 DestroyChildProcess(pr, signalType)
7617      ProcRef pr;
7618      int signalType;
7619 {
7620     ChildProc *cp = (ChildProc *) pr;
7621
7622     if (cp->kind != CPReal) return;
7623     cp->kind = CPNone;
7624     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7625         signal(SIGALRM, AlarmCallBack);
7626         alarm(3);
7627         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7628             kill(cp->pid, SIGKILL); // kill it forcefully
7629             wait((int *) 0);        // and wait again
7630         }
7631     } else {
7632         if (signalType) {
7633             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7634         }
7635         /* Process is exiting either because of the kill or because of
7636            a quit command sent by the backend; either way, wait for it to die.
7637         */
7638         wait((int *) 0);
7639     }
7640     close(cp->fdFrom);
7641     close(cp->fdTo);
7642 }
7643
7644 void
7645 InterruptChildProcess(pr)
7646      ProcRef pr;
7647 {
7648     ChildProc *cp = (ChildProc *) pr;
7649
7650     if (cp->kind != CPReal) return;
7651     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7652 }
7653
7654 int OpenTelnet(host, port, pr)
7655      char *host;
7656      char *port;
7657      ProcRef *pr;
7658 {
7659     char cmdLine[MSG_SIZ];
7660
7661     if (port[0] == NULLCHAR) {
7662       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7663     } else {
7664       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7665     }
7666     return StartChildProcess(cmdLine, "", pr);
7667 }
7668
7669 int OpenTCP(host, port, pr)
7670      char *host;
7671      char *port;
7672      ProcRef *pr;
7673 {
7674 #if OMIT_SOCKETS
7675     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7676 #else  /* !OMIT_SOCKETS */
7677     int s;
7678     struct sockaddr_in sa;
7679     struct hostent     *hp;
7680     unsigned short uport;
7681     ChildProc *cp;
7682
7683     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7684         return errno;
7685     }
7686
7687     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7688     sa.sin_family = AF_INET;
7689     sa.sin_addr.s_addr = INADDR_ANY;
7690     uport = (unsigned short) 0;
7691     sa.sin_port = htons(uport);
7692     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7693         return errno;
7694     }
7695
7696     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7697     if (!(hp = gethostbyname(host))) {
7698         int b0, b1, b2, b3;
7699         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7700             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7701             hp->h_addrtype = AF_INET;
7702             hp->h_length = 4;
7703             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7704             hp->h_addr_list[0] = (char *) malloc(4);
7705             hp->h_addr_list[0][0] = b0;
7706             hp->h_addr_list[0][1] = b1;
7707             hp->h_addr_list[0][2] = b2;
7708             hp->h_addr_list[0][3] = b3;
7709         } else {
7710             return ENOENT;
7711         }
7712     }
7713     sa.sin_family = hp->h_addrtype;
7714     uport = (unsigned short) atoi(port);
7715     sa.sin_port = htons(uport);
7716     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7717
7718     if (connect(s, (struct sockaddr *) &sa,
7719                 sizeof(struct sockaddr_in)) < 0) {
7720         return errno;
7721     }
7722
7723     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7724     cp->kind = CPSock;
7725     cp->pid = 0;
7726     cp->fdFrom = s;
7727     cp->fdTo = s;
7728     *pr = (ProcRef) cp;
7729
7730 #endif /* !OMIT_SOCKETS */
7731
7732     return 0;
7733 }
7734
7735 int OpenCommPort(name, pr)
7736      char *name;
7737      ProcRef *pr;
7738 {
7739     int fd;
7740     ChildProc *cp;
7741
7742     fd = open(name, 2, 0);
7743     if (fd < 0) return errno;
7744
7745     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7746     cp->kind = CPComm;
7747     cp->pid = 0;
7748     cp->fdFrom = fd;
7749     cp->fdTo = fd;
7750     *pr = (ProcRef) cp;
7751
7752     return 0;
7753 }
7754
7755 int OpenLoopback(pr)
7756      ProcRef *pr;
7757 {
7758     ChildProc *cp;
7759     int to[2], from[2];
7760
7761     SetUpChildIO(to, from);
7762
7763     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7764     cp->kind = CPLoop;
7765     cp->pid = 0;
7766     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7767     cp->fdTo = to[1];
7768     *pr = (ProcRef) cp;
7769
7770     return 0;
7771 }
7772
7773 int OpenRcmd(host, user, cmd, pr)
7774      char *host, *user, *cmd;
7775      ProcRef *pr;
7776 {
7777     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7778     return -1;
7779 }
7780
7781 #define INPUT_SOURCE_BUF_SIZE 8192
7782
7783 typedef struct {
7784     CPKind kind;
7785     int fd;
7786     int lineByLine;
7787     char *unused;
7788     InputCallback func;
7789     XtInputId xid;
7790     char buf[INPUT_SOURCE_BUF_SIZE];
7791     VOIDSTAR closure;
7792 } InputSource;
7793
7794 void
7795 DoInputCallback(closure, source, xid)
7796      caddr_t closure;
7797      int *source;
7798      XtInputId *xid;
7799 {
7800     InputSource *is = (InputSource *) closure;
7801     int count;
7802     int error;
7803     char *p, *q;
7804
7805     if (is->lineByLine) {
7806         count = read(is->fd, is->unused,
7807                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7808         if (count <= 0) {
7809             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7810             return;
7811         }
7812         is->unused += count;
7813         p = is->buf;
7814         while (p < is->unused) {
7815             q = memchr(p, '\n', is->unused - p);
7816             if (q == NULL) break;
7817             q++;
7818             (is->func)(is, is->closure, p, q - p, 0);
7819             p = q;
7820         }
7821         q = is->buf;
7822         while (p < is->unused) {
7823             *q++ = *p++;
7824         }
7825         is->unused = q;
7826     } else {
7827         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7828         if (count == -1)
7829           error = errno;
7830         else
7831           error = 0;
7832         (is->func)(is, is->closure, is->buf, count, error);
7833     }
7834 }
7835
7836 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7837      ProcRef pr;
7838      int lineByLine;
7839      InputCallback func;
7840      VOIDSTAR closure;
7841 {
7842     InputSource *is;
7843     ChildProc *cp = (ChildProc *) pr;
7844
7845     is = (InputSource *) calloc(1, sizeof(InputSource));
7846     is->lineByLine = lineByLine;
7847     is->func = func;
7848     if (pr == NoProc) {
7849         is->kind = CPReal;
7850         is->fd = fileno(stdin);
7851     } else {
7852         is->kind = cp->kind;
7853         is->fd = cp->fdFrom;
7854     }
7855     if (lineByLine) {
7856         is->unused = is->buf;
7857     }
7858
7859     is->xid = XtAppAddInput(appContext, is->fd,
7860                             (XtPointer) (XtInputReadMask),
7861                             (XtInputCallbackProc) DoInputCallback,
7862                             (XtPointer) is);
7863     is->closure = closure;
7864     return (InputSourceRef) is;
7865 }
7866
7867 void
7868 RemoveInputSource(isr)
7869      InputSourceRef isr;
7870 {
7871     InputSource *is = (InputSource *) isr;
7872
7873     if (is->xid == 0) return;
7874     XtRemoveInput(is->xid);
7875     is->xid = 0;
7876 }
7877
7878 int OutputToProcess(pr, message, count, outError)
7879      ProcRef pr;
7880      char *message;
7881      int count;
7882      int *outError;
7883 {
7884     static int line = 0;
7885     ChildProc *cp = (ChildProc *) pr;
7886     int outCount;
7887
7888     if (pr == NoProc)
7889     {
7890         if (appData.noJoin || !appData.useInternalWrap)
7891             outCount = fwrite(message, 1, count, stdout);
7892         else
7893         {
7894             int width = get_term_width();
7895             int len = wrap(NULL, message, count, width, &line);
7896             char *msg = malloc(len);
7897             int dbgchk;
7898
7899             if (!msg)
7900                 outCount = fwrite(message, 1, count, stdout);
7901             else
7902             {
7903                 dbgchk = wrap(msg, message, count, width, &line);
7904                 if (dbgchk != len && appData.debugMode)
7905                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7906                 outCount = fwrite(msg, 1, dbgchk, stdout);
7907                 free(msg);
7908             }
7909         }
7910     }
7911     else
7912       outCount = write(cp->fdTo, message, count);
7913
7914     if (outCount == -1)
7915       *outError = errno;
7916     else
7917       *outError = 0;
7918
7919     return outCount;
7920 }
7921
7922 /* Output message to process, with "ms" milliseconds of delay
7923    between each character. This is needed when sending the logon
7924    script to ICC, which for some reason doesn't like the
7925    instantaneous send. */
7926 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7927      ProcRef pr;
7928      char *message;
7929      int count;
7930      int *outError;
7931      long msdelay;
7932 {
7933     ChildProc *cp = (ChildProc *) pr;
7934     int outCount = 0;
7935     int r;
7936
7937     while (count--) {
7938         r = write(cp->fdTo, message++, 1);
7939         if (r == -1) {
7940             *outError = errno;
7941             return outCount;
7942         }
7943         ++outCount;
7944         if (msdelay >= 0)
7945           TimeDelay(msdelay);
7946     }
7947
7948     return outCount;
7949 }
7950
7951 /****   Animation code by Hugh Fisher, DCS, ANU.
7952
7953         Known problem: if a window overlapping the board is
7954         moved away while a piece is being animated underneath,
7955         the newly exposed area won't be updated properly.
7956         I can live with this.
7957
7958         Known problem: if you look carefully at the animation
7959         of pieces in mono mode, they are being drawn as solid
7960         shapes without interior detail while moving. Fixing
7961         this would be a major complication for minimal return.
7962 ****/
7963
7964 /*      Masks for XPM pieces. Black and white pieces can have
7965         different shapes, but in the interest of retaining my
7966         sanity pieces must have the same outline on both light
7967         and dark squares, and all pieces must use the same
7968         background square colors/images.                */
7969
7970 static int xpmDone = 0;
7971
7972 static void
7973 CreateAnimMasks (pieceDepth)
7974      int pieceDepth;
7975 {
7976   ChessSquare   piece;
7977   Pixmap        buf;
7978   GC            bufGC, maskGC;
7979   int           kind, n;
7980   unsigned long plane;
7981   XGCValues     values;
7982
7983   /* Need a bitmap just to get a GC with right depth */
7984   buf = XCreatePixmap(xDisplay, xBoardWindow,
7985                         8, 8, 1);
7986   values.foreground = 1;
7987   values.background = 0;
7988   /* Don't use XtGetGC, not read only */
7989   maskGC = XCreateGC(xDisplay, buf,
7990                     GCForeground | GCBackground, &values);
7991   XFreePixmap(xDisplay, buf);
7992
7993   buf = XCreatePixmap(xDisplay, xBoardWindow,
7994                       squareSize, squareSize, pieceDepth);
7995   values.foreground = XBlackPixel(xDisplay, xScreen);
7996   values.background = XWhitePixel(xDisplay, xScreen);
7997   bufGC = XCreateGC(xDisplay, buf,
7998                     GCForeground | GCBackground, &values);
7999
8000   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8001     /* Begin with empty mask */
8002     if(!xpmDone) // [HGM] pieces: keep using existing
8003     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8004                                  squareSize, squareSize, 1);
8005     XSetFunction(xDisplay, maskGC, GXclear);
8006     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8007                    0, 0, squareSize, squareSize);
8008
8009     /* Take a copy of the piece */
8010     if (White(piece))
8011       kind = 0;
8012     else
8013       kind = 2;
8014     XSetFunction(xDisplay, bufGC, GXcopy);
8015     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8016               buf, bufGC,
8017               0, 0, squareSize, squareSize, 0, 0);
8018
8019     /* XOR the background (light) over the piece */
8020     XSetFunction(xDisplay, bufGC, GXxor);
8021     if (useImageSqs)
8022       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8023                 0, 0, squareSize, squareSize, 0, 0);
8024     else {
8025       XSetForeground(xDisplay, bufGC, lightSquareColor);
8026       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8027     }
8028
8029     /* We now have an inverted piece image with the background
8030        erased. Construct mask by just selecting all the non-zero
8031        pixels - no need to reconstruct the original image.      */
8032     XSetFunction(xDisplay, maskGC, GXor);
8033     plane = 1;
8034     /* Might be quicker to download an XImage and create bitmap
8035        data from it rather than this N copies per piece, but it
8036        only takes a fraction of a second and there is a much
8037        longer delay for loading the pieces.             */
8038     for (n = 0; n < pieceDepth; n ++) {
8039       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8040                  0, 0, squareSize, squareSize,
8041                  0, 0, plane);
8042       plane = plane << 1;
8043     }
8044   }
8045   /* Clean up */
8046   XFreePixmap(xDisplay, buf);
8047   XFreeGC(xDisplay, bufGC);
8048   XFreeGC(xDisplay, maskGC);
8049 }
8050
8051 static void
8052 InitAnimState (anim, info)
8053   AnimState * anim;
8054   XWindowAttributes * info;
8055 {
8056   XtGCMask  mask;
8057   XGCValues values;
8058
8059   /* Each buffer is square size, same depth as window */
8060   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8061                         squareSize, squareSize, info->depth);
8062   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8063                         squareSize, squareSize, info->depth);
8064
8065   /* Create a plain GC for blitting */
8066   mask = GCForeground | GCBackground | GCFunction |
8067          GCPlaneMask | GCGraphicsExposures;
8068   values.foreground = XBlackPixel(xDisplay, xScreen);
8069   values.background = XWhitePixel(xDisplay, xScreen);
8070   values.function   = GXcopy;
8071   values.plane_mask = AllPlanes;
8072   values.graphics_exposures = False;
8073   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8074
8075   /* Piece will be copied from an existing context at
8076      the start of each new animation/drag. */
8077   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8078
8079   /* Outline will be a read-only copy of an existing */
8080   anim->outlineGC = None;
8081 }
8082
8083 static void
8084 CreateAnimVars ()
8085 {
8086   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8087   XWindowAttributes info;
8088
8089   if (xpmDone && gameInfo.variant == old) return;
8090   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8091   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8092
8093   InitAnimState(&game, &info);
8094   InitAnimState(&player, &info);
8095
8096   /* For XPM pieces, we need bitmaps to use as masks. */
8097   if (useImages)
8098     CreateAnimMasks(info.depth);
8099    xpmDone = 1;
8100 }
8101
8102 #ifndef HAVE_USLEEP
8103
8104 static Boolean frameWaiting;
8105
8106 static RETSIGTYPE FrameAlarm (sig)
8107      int sig;
8108 {
8109   frameWaiting = False;
8110   /* In case System-V style signals.  Needed?? */
8111   signal(SIGALRM, FrameAlarm);
8112 }
8113
8114 static void
8115 FrameDelay (time)
8116      int time;
8117 {
8118   struct itimerval delay;
8119
8120   XSync(xDisplay, False);
8121
8122   if (time > 0) {
8123     frameWaiting = True;
8124     signal(SIGALRM, FrameAlarm);
8125     delay.it_interval.tv_sec =
8126       delay.it_value.tv_sec = time / 1000;
8127     delay.it_interval.tv_usec =
8128       delay.it_value.tv_usec = (time % 1000) * 1000;
8129     setitimer(ITIMER_REAL, &delay, NULL);
8130     while (frameWaiting) pause();
8131     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8132     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8133     setitimer(ITIMER_REAL, &delay, NULL);
8134   }
8135 }
8136
8137 #else
8138
8139 static void
8140 FrameDelay (time)
8141      int time;
8142 {
8143   XSync(xDisplay, False);
8144   if (time > 0)
8145     usleep(time * 1000);
8146 }
8147
8148 #endif
8149
8150 /*      Convert board position to corner of screen rect and color       */
8151
8152 static void
8153 ScreenSquare(column, row, pt, color)
8154      int column; int row; XPoint * pt; int * color;
8155 {
8156   if (flipView) {
8157     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8158     pt->y = lineGap + row * (squareSize + lineGap);
8159   } else {
8160     pt->x = lineGap + column * (squareSize + lineGap);
8161     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8162   }
8163   *color = SquareColor(row, column);
8164 }
8165
8166 /*      Convert window coords to square                 */
8167
8168 static void
8169 BoardSquare(x, y, column, row)
8170      int x; int y; int * column; int * row;
8171 {
8172   *column = EventToSquare(x, BOARD_WIDTH);
8173   if (flipView && *column >= 0)
8174     *column = BOARD_WIDTH - 1 - *column;
8175   *row = EventToSquare(y, BOARD_HEIGHT);
8176   if (!flipView && *row >= 0)
8177     *row = BOARD_HEIGHT - 1 - *row;
8178 }
8179
8180 /*   Utilities  */
8181
8182 #undef Max  /* just in case */
8183 #undef Min
8184 #define Max(a, b) ((a) > (b) ? (a) : (b))
8185 #define Min(a, b) ((a) < (b) ? (a) : (b))
8186
8187 static void
8188 SetRect(rect, x, y, width, height)
8189      XRectangle * rect; int x; int y; int width; int height;
8190 {
8191   rect->x = x;
8192   rect->y = y;
8193   rect->width  = width;
8194   rect->height = height;
8195 }
8196
8197 /*      Test if two frames overlap. If they do, return
8198         intersection rect within old and location of
8199         that rect within new. */
8200
8201 static Boolean
8202 Intersect(old, new, size, area, pt)
8203      XPoint * old; XPoint * new;
8204      int size; XRectangle * area; XPoint * pt;
8205 {
8206   if (old->x > new->x + size || new->x > old->x + size ||
8207       old->y > new->y + size || new->y > old->y + size) {
8208     return False;
8209   } else {
8210     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8211             size - abs(old->x - new->x), size - abs(old->y - new->y));
8212     pt->x = Max(old->x - new->x, 0);
8213     pt->y = Max(old->y - new->y, 0);
8214     return True;
8215   }
8216 }
8217
8218 /*      For two overlapping frames, return the rect(s)
8219         in the old that do not intersect with the new.   */
8220
8221 static void
8222 CalcUpdateRects(old, new, size, update, nUpdates)
8223      XPoint * old; XPoint * new; int size;
8224      XRectangle update[]; int * nUpdates;
8225 {
8226   int        count;
8227
8228   /* If old = new (shouldn't happen) then nothing to draw */
8229   if (old->x == new->x && old->y == new->y) {
8230     *nUpdates = 0;
8231     return;
8232   }
8233   /* Work out what bits overlap. Since we know the rects
8234      are the same size we don't need a full intersect calc. */
8235   count = 0;
8236   /* Top or bottom edge? */
8237   if (new->y > old->y) {
8238     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8239     count ++;
8240   } else if (old->y > new->y) {
8241     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8242                               size, old->y - new->y);
8243     count ++;
8244   }
8245   /* Left or right edge - don't overlap any update calculated above. */
8246   if (new->x > old->x) {
8247     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8248                               new->x - old->x, size - abs(new->y - old->y));
8249     count ++;
8250   } else if (old->x > new->x) {
8251     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8252                               old->x - new->x, size - abs(new->y - old->y));
8253     count ++;
8254   }
8255   /* Done */
8256   *nUpdates = count;
8257 }
8258
8259 /*      Generate a series of frame coords from start->mid->finish.
8260         The movement rate doubles until the half way point is
8261         reached, then halves back down to the final destination,
8262         which gives a nice slow in/out effect. The algorithmn
8263         may seem to generate too many intermediates for short
8264         moves, but remember that the purpose is to attract the
8265         viewers attention to the piece about to be moved and
8266         then to where it ends up. Too few frames would be less
8267         noticeable.                                             */
8268
8269 static void
8270 Tween(start, mid, finish, factor, frames, nFrames)
8271      XPoint * start; XPoint * mid;
8272      XPoint * finish; int factor;
8273      XPoint frames[]; int * nFrames;
8274 {
8275   int fraction, n, count;
8276
8277   count = 0;
8278
8279   /* Slow in, stepping 1/16th, then 1/8th, ... */
8280   fraction = 1;
8281   for (n = 0; n < factor; n++)
8282     fraction *= 2;
8283   for (n = 0; n < factor; n++) {
8284     frames[count].x = start->x + (mid->x - start->x) / fraction;
8285     frames[count].y = start->y + (mid->y - start->y) / fraction;
8286     count ++;
8287     fraction = fraction / 2;
8288   }
8289
8290   /* Midpoint */
8291   frames[count] = *mid;
8292   count ++;
8293
8294   /* Slow out, stepping 1/2, then 1/4, ... */
8295   fraction = 2;
8296   for (n = 0; n < factor; n++) {
8297     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8298     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8299     count ++;
8300     fraction = fraction * 2;
8301   }
8302   *nFrames = count;
8303 }
8304
8305 /*      Draw a piece on the screen without disturbing what's there      */
8306
8307 static void
8308 SelectGCMask(piece, clip, outline, mask)
8309      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8310 {
8311   GC source;
8312
8313   /* Bitmap for piece being moved. */
8314   if (appData.monoMode) {
8315       *mask = *pieceToSolid(piece);
8316   } else if (useImages) {
8317 #if HAVE_LIBXPM
8318       *mask = xpmMask[piece];
8319 #else
8320       *mask = ximMaskPm[piece];
8321 #endif
8322   } else {
8323       *mask = *pieceToSolid(piece);
8324   }
8325
8326   /* GC for piece being moved. Square color doesn't matter, but
8327      since it gets modified we make a copy of the original. */
8328   if (White(piece)) {
8329     if (appData.monoMode)
8330       source = bwPieceGC;
8331     else
8332       source = wlPieceGC;
8333   } else {
8334     if (appData.monoMode)
8335       source = wbPieceGC;
8336     else
8337       source = blPieceGC;
8338   }
8339   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8340
8341   /* Outline only used in mono mode and is not modified */
8342   if (White(piece))
8343     *outline = bwPieceGC;
8344   else
8345     *outline = wbPieceGC;
8346 }
8347
8348 static void
8349 OverlayPiece(piece, clip, outline,  dest)
8350      ChessSquare piece; GC clip; GC outline; Drawable dest;
8351 {
8352   int   kind;
8353
8354   if (!useImages) {
8355     /* Draw solid rectangle which will be clipped to shape of piece */
8356     XFillRectangle(xDisplay, dest, clip,
8357                    0, 0, squareSize, squareSize);
8358     if (appData.monoMode)
8359       /* Also draw outline in contrasting color for black
8360          on black / white on white cases                */
8361       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8362                  0, 0, squareSize, squareSize, 0, 0, 1);
8363   } else {
8364     /* Copy the piece */
8365     if (White(piece))
8366       kind = 0;
8367     else
8368       kind = 2;
8369     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8370               dest, clip,
8371               0, 0, squareSize, squareSize,
8372               0, 0);
8373   }
8374 }
8375
8376 /* Animate the movement of a single piece */
8377
8378 static void
8379 BeginAnimation(anim, piece, startColor, start)
8380      AnimState *anim;
8381      ChessSquare piece;
8382      int startColor;
8383      XPoint * start;
8384 {
8385   Pixmap mask;
8386
8387   /* The old buffer is initialised with the start square (empty) */
8388   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
8389   anim->prevFrame = *start;
8390
8391   /* The piece will be drawn using its own bitmap as a matte    */
8392   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8393   XSetClipMask(xDisplay, anim->pieceGC, mask);
8394 }
8395
8396 static void
8397 AnimationFrame(anim, frame, piece)
8398      AnimState *anim;
8399      XPoint *frame;
8400      ChessSquare piece;
8401 {
8402   XRectangle updates[4];
8403   XRectangle overlap;
8404   XPoint     pt;
8405   int        count, i;
8406
8407   /* Save what we are about to draw into the new buffer */
8408   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8409             frame->x, frame->y, squareSize, squareSize,
8410             0, 0);
8411
8412   /* Erase bits of the previous frame */
8413   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8414     /* Where the new frame overlapped the previous,
8415        the contents in newBuf are wrong. */
8416     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8417               overlap.x, overlap.y,
8418               overlap.width, overlap.height,
8419               pt.x, pt.y);
8420     /* Repaint the areas in the old that don't overlap new */
8421     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8422     for (i = 0; i < count; i++)
8423       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8424                 updates[i].x - anim->prevFrame.x,
8425                 updates[i].y - anim->prevFrame.y,
8426                 updates[i].width, updates[i].height,
8427                 updates[i].x, updates[i].y);
8428   } else {
8429     /* Easy when no overlap */
8430     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8431                   0, 0, squareSize, squareSize,
8432                   anim->prevFrame.x, anim->prevFrame.y);
8433   }
8434
8435   /* Save this frame for next time round */
8436   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8437                 0, 0, squareSize, squareSize,
8438                 0, 0);
8439   anim->prevFrame = *frame;
8440
8441   /* Draw piece over original screen contents, not current,
8442      and copy entire rect. Wipes out overlapping piece images. */
8443   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8444   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8445                 0, 0, squareSize, squareSize,
8446                 frame->x, frame->y);
8447 }
8448
8449 static void
8450 EndAnimation (anim, finish)
8451      AnimState *anim;
8452      XPoint *finish;
8453 {
8454   XRectangle updates[4];
8455   XRectangle overlap;
8456   XPoint     pt;
8457   int        count, i;
8458
8459   /* The main code will redraw the final square, so we
8460      only need to erase the bits that don't overlap.    */
8461   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8462     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8463     for (i = 0; i < count; i++)
8464       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8465                 updates[i].x - anim->prevFrame.x,
8466                 updates[i].y - anim->prevFrame.y,
8467                 updates[i].width, updates[i].height,
8468                 updates[i].x, updates[i].y);
8469   } else {
8470     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8471                 0, 0, squareSize, squareSize,
8472                 anim->prevFrame.x, anim->prevFrame.y);
8473   }
8474 }
8475
8476 static void
8477 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8478      AnimState *anim;
8479      ChessSquare piece; int startColor;
8480      XPoint * start; XPoint * finish;
8481      XPoint frames[]; int nFrames;
8482 {
8483   int n;
8484
8485   BeginAnimation(anim, piece, startColor, start);
8486   for (n = 0; n < nFrames; n++) {
8487     AnimationFrame(anim, &(frames[n]), piece);
8488     FrameDelay(appData.animSpeed);
8489   }
8490   EndAnimation(anim, finish);
8491 }
8492
8493 /* Main control logic for deciding what to animate and how */
8494
8495 void
8496 AnimateMove(board, fromX, fromY, toX, toY)
8497      Board board;
8498      int fromX;
8499      int fromY;
8500      int toX;
8501      int toY;
8502 {
8503   ChessSquare piece;
8504   int hop;
8505   XPoint      start, finish, mid;
8506   XPoint      frames[kFactor * 2 + 1];
8507   int         nFrames, startColor, endColor;
8508
8509   /* Are we animating? */
8510   if (!appData.animate || appData.blindfold)
8511     return;
8512
8513   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
8514      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
8515         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8516
8517   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8518   piece = board[fromY][fromX];
8519   if (piece >= EmptySquare) return;
8520
8521 #if DONT_HOP
8522   hop = FALSE;
8523 #else
8524   hop = (piece == WhiteKnight || piece == BlackKnight);
8525 #endif
8526
8527   if (appData.debugMode) {
8528       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8529                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8530              piece, fromX, fromY, toX, toY);  }
8531
8532   ScreenSquare(fromX, fromY, &start, &startColor);
8533   ScreenSquare(toX, toY, &finish, &endColor);
8534
8535   if (hop) {
8536     /* Knight: make diagonal movement then straight */
8537     if (abs(toY - fromY) < abs(toX - fromX)) {
8538        mid.x = start.x + (finish.x - start.x) / 2;
8539        mid.y = finish.y;
8540      } else {
8541        mid.x = finish.x;
8542        mid.y = start.y + (finish.y - start.y) / 2;
8543      }
8544   } else {
8545     mid.x = start.x + (finish.x - start.x) / 2;
8546     mid.y = start.y + (finish.y - start.y) / 2;
8547   }
8548
8549   /* Don't use as many frames for very short moves */
8550   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8551     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8552   else
8553     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8554   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8555
8556   /* Be sure end square is redrawn */
8557   damage[toY][toX] = True;
8558 }
8559
8560 void
8561 DragPieceBegin(x, y)
8562      int x; int y;
8563 {
8564     int  boardX, boardY, color;
8565     XPoint corner;
8566
8567     /* Are we animating? */
8568     if (!appData.animateDragging || appData.blindfold)
8569       return;
8570
8571     /* Figure out which square we start in and the
8572        mouse position relative to top left corner. */
8573     BoardSquare(x, y, &boardX, &boardY);
8574     player.startBoardX = boardX;
8575     player.startBoardY = boardY;
8576     ScreenSquare(boardX, boardY, &corner, &color);
8577     player.startSquare  = corner;
8578     player.startColor   = color;
8579     /* As soon as we start dragging, the piece will jump slightly to
8580        be centered over the mouse pointer. */
8581     player.mouseDelta.x = squareSize/2;
8582     player.mouseDelta.y = squareSize/2;
8583     /* Initialise animation */
8584     player.dragPiece = PieceForSquare(boardX, boardY);
8585     /* Sanity check */
8586     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8587         player.dragActive = True;
8588         BeginAnimation(&player, player.dragPiece, color, &corner);
8589         /* Mark this square as needing to be redrawn. Note that
8590            we don't remove the piece though, since logically (ie
8591            as seen by opponent) the move hasn't been made yet. */
8592            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8593               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8594            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8595                      corner.x, corner.y, squareSize, squareSize,
8596                      0, 0); // [HGM] zh: unstack in stead of grab
8597         damage[boardY][boardX] = True;
8598     } else {
8599         player.dragActive = False;
8600     }
8601 }
8602
8603 static void
8604 DragPieceMove(x, y)
8605      int x; int y;
8606 {
8607     XPoint corner;
8608
8609     /* Are we animating? */
8610     if (!appData.animateDragging || appData.blindfold)
8611       return;
8612
8613     /* Sanity check */
8614     if (! player.dragActive)
8615       return;
8616     /* Move piece, maintaining same relative position
8617        of mouse within square    */
8618     corner.x = x - player.mouseDelta.x;
8619     corner.y = y - player.mouseDelta.y;
8620     AnimationFrame(&player, &corner, player.dragPiece);
8621 #if HIGHDRAG
8622     if (appData.highlightDragging) {
8623         int boardX, boardY;
8624         BoardSquare(x, y, &boardX, &boardY);
8625         SetHighlights(fromX, fromY, boardX, boardY);
8626     }
8627 #endif
8628 }
8629
8630 void
8631 DragPieceEnd(x, y)
8632      int x; int y;
8633 {
8634     int boardX, boardY, color;
8635     XPoint corner;
8636
8637     /* Are we animating? */
8638     if (!appData.animateDragging || appData.blindfold)
8639       return;
8640
8641     /* Sanity check */
8642     if (! player.dragActive)
8643       return;
8644     /* Last frame in sequence is square piece is
8645        placed on, which may not match mouse exactly. */
8646     BoardSquare(x, y, &boardX, &boardY);
8647     ScreenSquare(boardX, boardY, &corner, &color);
8648     EndAnimation(&player, &corner);
8649
8650     /* Be sure end square is redrawn */
8651     damage[boardY][boardX] = True;
8652
8653     /* This prevents weird things happening with fast successive
8654        clicks which on my Sun at least can cause motion events
8655        without corresponding press/release. */
8656     player.dragActive = False;
8657 }
8658
8659 /* Handle expose event while piece being dragged */
8660
8661 static void
8662 DrawDragPiece ()
8663 {
8664   if (!player.dragActive || appData.blindfold)
8665     return;
8666
8667   /* What we're doing: logically, the move hasn't been made yet,
8668      so the piece is still in it's original square. But visually
8669      it's being dragged around the board. So we erase the square
8670      that the piece is on and draw it at the last known drag point. */
8671   BlankSquare(player.startSquare.x, player.startSquare.y,
8672                 player.startColor, EmptySquare, xBoardWindow);
8673   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8674   damage[player.startBoardY][player.startBoardX] = TRUE;
8675 }
8676
8677 #include <sys/ioctl.h>
8678 int get_term_width()
8679 {
8680     int fd, default_width;
8681
8682     fd = STDIN_FILENO;
8683     default_width = 79; // this is FICS default anyway...
8684
8685 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8686     struct ttysize win;
8687     if (!ioctl(fd, TIOCGSIZE, &win))
8688         default_width = win.ts_cols;
8689 #elif defined(TIOCGWINSZ)
8690     struct winsize win;
8691     if (!ioctl(fd, TIOCGWINSZ, &win))
8692         default_width = win.ws_col;
8693 #endif
8694     return default_width;
8695 }
8696
8697 void update_ics_width()
8698 {
8699     static int old_width = 0;
8700     int new_width = get_term_width();
8701
8702     if (old_width != new_width)
8703        ics_printf("set width %d\n", new_width);
8704     old_width = new_width;
8705 }
8706
8707 void NotifyFrontendLogin()
8708 {
8709     update_ics_width();
8710 }