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