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