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