improves the XBoard handling of the engine command line
[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         while(*p == ' ') p++;
7576         argv[i++] = p;
7577         if(*p == '"' || *p == '\'')
7578              p = strchr(++argv[i-1], *p);
7579         else p = strchr(p, ' ');
7580         if (p == NULL) break;
7581         *p++ = NULLCHAR;
7582     }
7583     argv[i] = NULL;
7584
7585     SetUpChildIO(to_prog, from_prog);
7586
7587     if ((pid = fork()) == 0) {
7588         /* Child process */
7589         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7590         close(to_prog[1]);     // first close the unused pipe ends
7591         close(from_prog[0]);
7592         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7593         dup2(from_prog[1], 1);
7594         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7595         close(from_prog[1]);                   // and closing again loses one of the pipes!
7596         if(fileno(stderr) >= 2) // better safe than sorry...
7597                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7598
7599         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7600             perror(dir);
7601             exit(1);
7602         }
7603
7604         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7605
7606         execvp(argv[0], argv);
7607
7608         /* If we get here, exec failed */
7609         perror(argv[0]);
7610         exit(1);
7611     }
7612
7613     /* Parent process */
7614     close(to_prog[0]);
7615     close(from_prog[1]);
7616
7617     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7618     cp->kind = CPReal;
7619     cp->pid = pid;
7620     cp->fdFrom = from_prog[0];
7621     cp->fdTo = to_prog[1];
7622     *pr = (ProcRef) cp;
7623     return 0;
7624 }
7625
7626 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7627 static RETSIGTYPE AlarmCallBack(int n)
7628 {
7629     return;
7630 }
7631
7632 void
7633 DestroyChildProcess(pr, signalType)
7634      ProcRef pr;
7635      int signalType;
7636 {
7637     ChildProc *cp = (ChildProc *) pr;
7638
7639     if (cp->kind != CPReal) return;
7640     cp->kind = CPNone;
7641     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7642         signal(SIGALRM, AlarmCallBack);
7643         alarm(3);
7644         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7645             kill(cp->pid, SIGKILL); // kill it forcefully
7646             wait((int *) 0);        // and wait again
7647         }
7648     } else {
7649         if (signalType) {
7650             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7651         }
7652         /* Process is exiting either because of the kill or because of
7653            a quit command sent by the backend; either way, wait for it to die.
7654         */
7655         wait((int *) 0);
7656     }
7657     close(cp->fdFrom);
7658     close(cp->fdTo);
7659 }
7660
7661 void
7662 InterruptChildProcess(pr)
7663      ProcRef pr;
7664 {
7665     ChildProc *cp = (ChildProc *) pr;
7666
7667     if (cp->kind != CPReal) return;
7668     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7669 }
7670
7671 int OpenTelnet(host, port, pr)
7672      char *host;
7673      char *port;
7674      ProcRef *pr;
7675 {
7676     char cmdLine[MSG_SIZ];
7677
7678     if (port[0] == NULLCHAR) {
7679       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7680     } else {
7681       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7682     }
7683     return StartChildProcess(cmdLine, "", pr);
7684 }
7685
7686 int OpenTCP(host, port, pr)
7687      char *host;
7688      char *port;
7689      ProcRef *pr;
7690 {
7691 #if OMIT_SOCKETS
7692     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7693 #else  /* !OMIT_SOCKETS */
7694     int s;
7695     struct sockaddr_in sa;
7696     struct hostent     *hp;
7697     unsigned short uport;
7698     ChildProc *cp;
7699
7700     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7701         return errno;
7702     }
7703
7704     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7705     sa.sin_family = AF_INET;
7706     sa.sin_addr.s_addr = INADDR_ANY;
7707     uport = (unsigned short) 0;
7708     sa.sin_port = htons(uport);
7709     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7710         return errno;
7711     }
7712
7713     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7714     if (!(hp = gethostbyname(host))) {
7715         int b0, b1, b2, b3;
7716         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7717             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7718             hp->h_addrtype = AF_INET;
7719             hp->h_length = 4;
7720             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7721             hp->h_addr_list[0] = (char *) malloc(4);
7722             hp->h_addr_list[0][0] = b0;
7723             hp->h_addr_list[0][1] = b1;
7724             hp->h_addr_list[0][2] = b2;
7725             hp->h_addr_list[0][3] = b3;
7726         } else {
7727             return ENOENT;
7728         }
7729     }
7730     sa.sin_family = hp->h_addrtype;
7731     uport = (unsigned short) atoi(port);
7732     sa.sin_port = htons(uport);
7733     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7734
7735     if (connect(s, (struct sockaddr *) &sa,
7736                 sizeof(struct sockaddr_in)) < 0) {
7737         return errno;
7738     }
7739
7740     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7741     cp->kind = CPSock;
7742     cp->pid = 0;
7743     cp->fdFrom = s;
7744     cp->fdTo = s;
7745     *pr = (ProcRef) cp;
7746
7747 #endif /* !OMIT_SOCKETS */
7748
7749     return 0;
7750 }
7751
7752 int OpenCommPort(name, pr)
7753      char *name;
7754      ProcRef *pr;
7755 {
7756     int fd;
7757     ChildProc *cp;
7758
7759     fd = open(name, 2, 0);
7760     if (fd < 0) return errno;
7761
7762     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7763     cp->kind = CPComm;
7764     cp->pid = 0;
7765     cp->fdFrom = fd;
7766     cp->fdTo = fd;
7767     *pr = (ProcRef) cp;
7768
7769     return 0;
7770 }
7771
7772 int OpenLoopback(pr)
7773      ProcRef *pr;
7774 {
7775     ChildProc *cp;
7776     int to[2], from[2];
7777
7778     SetUpChildIO(to, from);
7779
7780     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7781     cp->kind = CPLoop;
7782     cp->pid = 0;
7783     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7784     cp->fdTo = to[1];
7785     *pr = (ProcRef) cp;
7786
7787     return 0;
7788 }
7789
7790 int OpenRcmd(host, user, cmd, pr)
7791      char *host, *user, *cmd;
7792      ProcRef *pr;
7793 {
7794     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7795     return -1;
7796 }
7797
7798 #define INPUT_SOURCE_BUF_SIZE 8192
7799
7800 typedef struct {
7801     CPKind kind;
7802     int fd;
7803     int lineByLine;
7804     char *unused;
7805     InputCallback func;
7806     XtInputId xid;
7807     char buf[INPUT_SOURCE_BUF_SIZE];
7808     VOIDSTAR closure;
7809 } InputSource;
7810
7811 void
7812 DoInputCallback(closure, source, xid)
7813      caddr_t closure;
7814      int *source;
7815      XtInputId *xid;
7816 {
7817     InputSource *is = (InputSource *) closure;
7818     int count;
7819     int error;
7820     char *p, *q;
7821
7822     if (is->lineByLine) {
7823         count = read(is->fd, is->unused,
7824                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7825         if (count <= 0) {
7826             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7827             return;
7828         }
7829         is->unused += count;
7830         p = is->buf;
7831         while (p < is->unused) {
7832             q = memchr(p, '\n', is->unused - p);
7833             if (q == NULL) break;
7834             q++;
7835             (is->func)(is, is->closure, p, q - p, 0);
7836             p = q;
7837         }
7838         q = is->buf;
7839         while (p < is->unused) {
7840             *q++ = *p++;
7841         }
7842         is->unused = q;
7843     } else {
7844         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7845         if (count == -1)
7846           error = errno;
7847         else
7848           error = 0;
7849         (is->func)(is, is->closure, is->buf, count, error);
7850     }
7851 }
7852
7853 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7854      ProcRef pr;
7855      int lineByLine;
7856      InputCallback func;
7857      VOIDSTAR closure;
7858 {
7859     InputSource *is;
7860     ChildProc *cp = (ChildProc *) pr;
7861
7862     is = (InputSource *) calloc(1, sizeof(InputSource));
7863     is->lineByLine = lineByLine;
7864     is->func = func;
7865     if (pr == NoProc) {
7866         is->kind = CPReal;
7867         is->fd = fileno(stdin);
7868     } else {
7869         is->kind = cp->kind;
7870         is->fd = cp->fdFrom;
7871     }
7872     if (lineByLine) {
7873         is->unused = is->buf;
7874     }
7875
7876     is->xid = XtAppAddInput(appContext, is->fd,
7877                             (XtPointer) (XtInputReadMask),
7878                             (XtInputCallbackProc) DoInputCallback,
7879                             (XtPointer) is);
7880     is->closure = closure;
7881     return (InputSourceRef) is;
7882 }
7883
7884 void
7885 RemoveInputSource(isr)
7886      InputSourceRef isr;
7887 {
7888     InputSource *is = (InputSource *) isr;
7889
7890     if (is->xid == 0) return;
7891     XtRemoveInput(is->xid);
7892     is->xid = 0;
7893 }
7894
7895 int OutputToProcess(pr, message, count, outError)
7896      ProcRef pr;
7897      char *message;
7898      int count;
7899      int *outError;
7900 {
7901     static int line = 0;
7902     ChildProc *cp = (ChildProc *) pr;
7903     int outCount;
7904
7905     if (pr == NoProc)
7906     {
7907         if (appData.noJoin || !appData.useInternalWrap)
7908             outCount = fwrite(message, 1, count, stdout);
7909         else
7910         {
7911             int width = get_term_width();
7912             int len = wrap(NULL, message, count, width, &line);
7913             char *msg = malloc(len);
7914             int dbgchk;
7915
7916             if (!msg)
7917                 outCount = fwrite(message, 1, count, stdout);
7918             else
7919             {
7920                 dbgchk = wrap(msg, message, count, width, &line);
7921                 if (dbgchk != len && appData.debugMode)
7922                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7923                 outCount = fwrite(msg, 1, dbgchk, stdout);
7924                 free(msg);
7925             }
7926         }
7927     }
7928     else
7929       outCount = write(cp->fdTo, message, count);
7930
7931     if (outCount == -1)
7932       *outError = errno;
7933     else
7934       *outError = 0;
7935
7936     return outCount;
7937 }
7938
7939 /* Output message to process, with "ms" milliseconds of delay
7940    between each character. This is needed when sending the logon
7941    script to ICC, which for some reason doesn't like the
7942    instantaneous send. */
7943 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7944      ProcRef pr;
7945      char *message;
7946      int count;
7947      int *outError;
7948      long msdelay;
7949 {
7950     ChildProc *cp = (ChildProc *) pr;
7951     int outCount = 0;
7952     int r;
7953
7954     while (count--) {
7955         r = write(cp->fdTo, message++, 1);
7956         if (r == -1) {
7957             *outError = errno;
7958             return outCount;
7959         }
7960         ++outCount;
7961         if (msdelay >= 0)
7962           TimeDelay(msdelay);
7963     }
7964
7965     return outCount;
7966 }
7967
7968 /****   Animation code by Hugh Fisher, DCS, ANU.
7969
7970         Known problem: if a window overlapping the board is
7971         moved away while a piece is being animated underneath,
7972         the newly exposed area won't be updated properly.
7973         I can live with this.
7974
7975         Known problem: if you look carefully at the animation
7976         of pieces in mono mode, they are being drawn as solid
7977         shapes without interior detail while moving. Fixing
7978         this would be a major complication for minimal return.
7979 ****/
7980
7981 /*      Masks for XPM pieces. Black and white pieces can have
7982         different shapes, but in the interest of retaining my
7983         sanity pieces must have the same outline on both light
7984         and dark squares, and all pieces must use the same
7985         background square colors/images.                */
7986
7987 static int xpmDone = 0;
7988
7989 static void
7990 CreateAnimMasks (pieceDepth)
7991      int pieceDepth;
7992 {
7993   ChessSquare   piece;
7994   Pixmap        buf;
7995   GC            bufGC, maskGC;
7996   int           kind, n;
7997   unsigned long plane;
7998   XGCValues     values;
7999
8000   /* Need a bitmap just to get a GC with right depth */
8001   buf = XCreatePixmap(xDisplay, xBoardWindow,
8002                         8, 8, 1);
8003   values.foreground = 1;
8004   values.background = 0;
8005   /* Don't use XtGetGC, not read only */
8006   maskGC = XCreateGC(xDisplay, buf,
8007                     GCForeground | GCBackground, &values);
8008   XFreePixmap(xDisplay, buf);
8009
8010   buf = XCreatePixmap(xDisplay, xBoardWindow,
8011                       squareSize, squareSize, pieceDepth);
8012   values.foreground = XBlackPixel(xDisplay, xScreen);
8013   values.background = XWhitePixel(xDisplay, xScreen);
8014   bufGC = XCreateGC(xDisplay, buf,
8015                     GCForeground | GCBackground, &values);
8016
8017   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8018     /* Begin with empty mask */
8019     if(!xpmDone) // [HGM] pieces: keep using existing
8020     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8021                                  squareSize, squareSize, 1);
8022     XSetFunction(xDisplay, maskGC, GXclear);
8023     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8024                    0, 0, squareSize, squareSize);
8025
8026     /* Take a copy of the piece */
8027     if (White(piece))
8028       kind = 0;
8029     else
8030       kind = 2;
8031     XSetFunction(xDisplay, bufGC, GXcopy);
8032     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8033               buf, bufGC,
8034               0, 0, squareSize, squareSize, 0, 0);
8035
8036     /* XOR the background (light) over the piece */
8037     XSetFunction(xDisplay, bufGC, GXxor);
8038     if (useImageSqs)
8039       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8040                 0, 0, squareSize, squareSize, 0, 0);
8041     else {
8042       XSetForeground(xDisplay, bufGC, lightSquareColor);
8043       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8044     }
8045
8046     /* We now have an inverted piece image with the background
8047        erased. Construct mask by just selecting all the non-zero
8048        pixels - no need to reconstruct the original image.      */
8049     XSetFunction(xDisplay, maskGC, GXor);
8050     plane = 1;
8051     /* Might be quicker to download an XImage and create bitmap
8052        data from it rather than this N copies per piece, but it
8053        only takes a fraction of a second and there is a much
8054        longer delay for loading the pieces.             */
8055     for (n = 0; n < pieceDepth; n ++) {
8056       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8057                  0, 0, squareSize, squareSize,
8058                  0, 0, plane);
8059       plane = plane << 1;
8060     }
8061   }
8062   /* Clean up */
8063   XFreePixmap(xDisplay, buf);
8064   XFreeGC(xDisplay, bufGC);
8065   XFreeGC(xDisplay, maskGC);
8066 }
8067
8068 static void
8069 InitAnimState (anim, info)
8070   AnimState * anim;
8071   XWindowAttributes * info;
8072 {
8073   XtGCMask  mask;
8074   XGCValues values;
8075
8076   /* Each buffer is square size, same depth as window */
8077   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8078                         squareSize, squareSize, info->depth);
8079   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8080                         squareSize, squareSize, info->depth);
8081
8082   /* Create a plain GC for blitting */
8083   mask = GCForeground | GCBackground | GCFunction |
8084          GCPlaneMask | GCGraphicsExposures;
8085   values.foreground = XBlackPixel(xDisplay, xScreen);
8086   values.background = XWhitePixel(xDisplay, xScreen);
8087   values.function   = GXcopy;
8088   values.plane_mask = AllPlanes;
8089   values.graphics_exposures = False;
8090   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8091
8092   /* Piece will be copied from an existing context at
8093      the start of each new animation/drag. */
8094   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8095
8096   /* Outline will be a read-only copy of an existing */
8097   anim->outlineGC = None;
8098 }
8099
8100 static void
8101 CreateAnimVars ()
8102 {
8103   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8104   XWindowAttributes info;
8105
8106   if (xpmDone && gameInfo.variant == old) return;
8107   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8108   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8109
8110   InitAnimState(&game, &info);
8111   InitAnimState(&player, &info);
8112
8113   /* For XPM pieces, we need bitmaps to use as masks. */
8114   if (useImages)
8115     CreateAnimMasks(info.depth);
8116    xpmDone = 1;
8117 }
8118
8119 #ifndef HAVE_USLEEP
8120
8121 static Boolean frameWaiting;
8122
8123 static RETSIGTYPE FrameAlarm (sig)
8124      int sig;
8125 {
8126   frameWaiting = False;
8127   /* In case System-V style signals.  Needed?? */
8128   signal(SIGALRM, FrameAlarm);
8129 }
8130
8131 static void
8132 FrameDelay (time)
8133      int time;
8134 {
8135   struct itimerval delay;
8136
8137   XSync(xDisplay, False);
8138
8139   if (time > 0) {
8140     frameWaiting = True;
8141     signal(SIGALRM, FrameAlarm);
8142     delay.it_interval.tv_sec =
8143       delay.it_value.tv_sec = time / 1000;
8144     delay.it_interval.tv_usec =
8145       delay.it_value.tv_usec = (time % 1000) * 1000;
8146     setitimer(ITIMER_REAL, &delay, NULL);
8147     while (frameWaiting) pause();
8148     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8149     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8150     setitimer(ITIMER_REAL, &delay, NULL);
8151   }
8152 }
8153
8154 #else
8155
8156 static void
8157 FrameDelay (time)
8158      int time;
8159 {
8160   XSync(xDisplay, False);
8161   if (time > 0)
8162     usleep(time * 1000);
8163 }
8164
8165 #endif
8166
8167 /*      Convert board position to corner of screen rect and color       */
8168
8169 static void
8170 ScreenSquare(column, row, pt, color)
8171      int column; int row; XPoint * pt; int * color;
8172 {
8173   if (flipView) {
8174     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8175     pt->y = lineGap + row * (squareSize + lineGap);
8176   } else {
8177     pt->x = lineGap + column * (squareSize + lineGap);
8178     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8179   }
8180   *color = SquareColor(row, column);
8181 }
8182
8183 /*      Convert window coords to square                 */
8184
8185 static void
8186 BoardSquare(x, y, column, row)
8187      int x; int y; int * column; int * row;
8188 {
8189   *column = EventToSquare(x, BOARD_WIDTH);
8190   if (flipView && *column >= 0)
8191     *column = BOARD_WIDTH - 1 - *column;
8192   *row = EventToSquare(y, BOARD_HEIGHT);
8193   if (!flipView && *row >= 0)
8194     *row = BOARD_HEIGHT - 1 - *row;
8195 }
8196
8197 /*   Utilities  */
8198
8199 #undef Max  /* just in case */
8200 #undef Min
8201 #define Max(a, b) ((a) > (b) ? (a) : (b))
8202 #define Min(a, b) ((a) < (b) ? (a) : (b))
8203
8204 static void
8205 SetRect(rect, x, y, width, height)
8206      XRectangle * rect; int x; int y; int width; int height;
8207 {
8208   rect->x = x;
8209   rect->y = y;
8210   rect->width  = width;
8211   rect->height = height;
8212 }
8213
8214 /*      Test if two frames overlap. If they do, return
8215         intersection rect within old and location of
8216         that rect within new. */
8217
8218 static Boolean
8219 Intersect(old, new, size, area, pt)
8220      XPoint * old; XPoint * new;
8221      int size; XRectangle * area; XPoint * pt;
8222 {
8223   if (old->x > new->x + size || new->x > old->x + size ||
8224       old->y > new->y + size || new->y > old->y + size) {
8225     return False;
8226   } else {
8227     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8228             size - abs(old->x - new->x), size - abs(old->y - new->y));
8229     pt->x = Max(old->x - new->x, 0);
8230     pt->y = Max(old->y - new->y, 0);
8231     return True;
8232   }
8233 }
8234
8235 /*      For two overlapping frames, return the rect(s)
8236         in the old that do not intersect with the new.   */
8237
8238 static void
8239 CalcUpdateRects(old, new, size, update, nUpdates)
8240      XPoint * old; XPoint * new; int size;
8241      XRectangle update[]; int * nUpdates;
8242 {
8243   int        count;
8244
8245   /* If old = new (shouldn't happen) then nothing to draw */
8246   if (old->x == new->x && old->y == new->y) {
8247     *nUpdates = 0;
8248     return;
8249   }
8250   /* Work out what bits overlap. Since we know the rects
8251      are the same size we don't need a full intersect calc. */
8252   count = 0;
8253   /* Top or bottom edge? */
8254   if (new->y > old->y) {
8255     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8256     count ++;
8257   } else if (old->y > new->y) {
8258     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8259                               size, old->y - new->y);
8260     count ++;
8261   }
8262   /* Left or right edge - don't overlap any update calculated above. */
8263   if (new->x > old->x) {
8264     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8265                               new->x - old->x, size - abs(new->y - old->y));
8266     count ++;
8267   } else if (old->x > new->x) {
8268     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8269                               old->x - new->x, size - abs(new->y - old->y));
8270     count ++;
8271   }
8272   /* Done */
8273   *nUpdates = count;
8274 }
8275
8276 /*      Generate a series of frame coords from start->mid->finish.
8277         The movement rate doubles until the half way point is
8278         reached, then halves back down to the final destination,
8279         which gives a nice slow in/out effect. The algorithmn
8280         may seem to generate too many intermediates for short
8281         moves, but remember that the purpose is to attract the
8282         viewers attention to the piece about to be moved and
8283         then to where it ends up. Too few frames would be less
8284         noticeable.                                             */
8285
8286 static void
8287 Tween(start, mid, finish, factor, frames, nFrames)
8288      XPoint * start; XPoint * mid;
8289      XPoint * finish; int factor;
8290      XPoint frames[]; int * nFrames;
8291 {
8292   int fraction, n, count;
8293
8294   count = 0;
8295
8296   /* Slow in, stepping 1/16th, then 1/8th, ... */
8297   fraction = 1;
8298   for (n = 0; n < factor; n++)
8299     fraction *= 2;
8300   for (n = 0; n < factor; n++) {
8301     frames[count].x = start->x + (mid->x - start->x) / fraction;
8302     frames[count].y = start->y + (mid->y - start->y) / fraction;
8303     count ++;
8304     fraction = fraction / 2;
8305   }
8306
8307   /* Midpoint */
8308   frames[count] = *mid;
8309   count ++;
8310
8311   /* Slow out, stepping 1/2, then 1/4, ... */
8312   fraction = 2;
8313   for (n = 0; n < factor; n++) {
8314     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8315     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8316     count ++;
8317     fraction = fraction * 2;
8318   }
8319   *nFrames = count;
8320 }
8321
8322 /*      Draw a piece on the screen without disturbing what's there      */
8323
8324 static void
8325 SelectGCMask(piece, clip, outline, mask)
8326      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8327 {
8328   GC source;
8329
8330   /* Bitmap for piece being moved. */
8331   if (appData.monoMode) {
8332       *mask = *pieceToSolid(piece);
8333   } else if (useImages) {
8334 #if HAVE_LIBXPM
8335       *mask = xpmMask[piece];
8336 #else
8337       *mask = ximMaskPm[piece];
8338 #endif
8339   } else {
8340       *mask = *pieceToSolid(piece);
8341   }
8342
8343   /* GC for piece being moved. Square color doesn't matter, but
8344      since it gets modified we make a copy of the original. */
8345   if (White(piece)) {
8346     if (appData.monoMode)
8347       source = bwPieceGC;
8348     else
8349       source = wlPieceGC;
8350   } else {
8351     if (appData.monoMode)
8352       source = wbPieceGC;
8353     else
8354       source = blPieceGC;
8355   }
8356   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8357
8358   /* Outline only used in mono mode and is not modified */
8359   if (White(piece))
8360     *outline = bwPieceGC;
8361   else
8362     *outline = wbPieceGC;
8363 }
8364
8365 static void
8366 OverlayPiece(piece, clip, outline,  dest)
8367      ChessSquare piece; GC clip; GC outline; Drawable dest;
8368 {
8369   int   kind;
8370
8371   if (!useImages) {
8372     /* Draw solid rectangle which will be clipped to shape of piece */
8373     XFillRectangle(xDisplay, dest, clip,
8374                    0, 0, squareSize, squareSize);
8375     if (appData.monoMode)
8376       /* Also draw outline in contrasting color for black
8377          on black / white on white cases                */
8378       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8379                  0, 0, squareSize, squareSize, 0, 0, 1);
8380   } else {
8381     /* Copy the piece */
8382     if (White(piece))
8383       kind = 0;
8384     else
8385       kind = 2;
8386     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8387               dest, clip,
8388               0, 0, squareSize, squareSize,
8389               0, 0);
8390   }
8391 }
8392
8393 /* Animate the movement of a single piece */
8394
8395 static void
8396 BeginAnimation(anim, piece, startColor, start)
8397      AnimState *anim;
8398      ChessSquare piece;
8399      int startColor;
8400      XPoint * start;
8401 {
8402   Pixmap mask;
8403
8404   /* The old buffer is initialised with the start square (empty) */
8405   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
8406   anim->prevFrame = *start;
8407
8408   /* The piece will be drawn using its own bitmap as a matte    */
8409   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8410   XSetClipMask(xDisplay, anim->pieceGC, mask);
8411 }
8412
8413 static void
8414 AnimationFrame(anim, frame, piece)
8415      AnimState *anim;
8416      XPoint *frame;
8417      ChessSquare piece;
8418 {
8419   XRectangle updates[4];
8420   XRectangle overlap;
8421   XPoint     pt;
8422   int        count, i;
8423
8424   /* Save what we are about to draw into the new buffer */
8425   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8426             frame->x, frame->y, squareSize, squareSize,
8427             0, 0);
8428
8429   /* Erase bits of the previous frame */
8430   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8431     /* Where the new frame overlapped the previous,
8432        the contents in newBuf are wrong. */
8433     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8434               overlap.x, overlap.y,
8435               overlap.width, overlap.height,
8436               pt.x, pt.y);
8437     /* Repaint the areas in the old that don't overlap new */
8438     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8439     for (i = 0; i < count; i++)
8440       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8441                 updates[i].x - anim->prevFrame.x,
8442                 updates[i].y - anim->prevFrame.y,
8443                 updates[i].width, updates[i].height,
8444                 updates[i].x, updates[i].y);
8445   } else {
8446     /* Easy when no overlap */
8447     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8448                   0, 0, squareSize, squareSize,
8449                   anim->prevFrame.x, anim->prevFrame.y);
8450   }
8451
8452   /* Save this frame for next time round */
8453   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8454                 0, 0, squareSize, squareSize,
8455                 0, 0);
8456   anim->prevFrame = *frame;
8457
8458   /* Draw piece over original screen contents, not current,
8459      and copy entire rect. Wipes out overlapping piece images. */
8460   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8461   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8462                 0, 0, squareSize, squareSize,
8463                 frame->x, frame->y);
8464 }
8465
8466 static void
8467 EndAnimation (anim, finish)
8468      AnimState *anim;
8469      XPoint *finish;
8470 {
8471   XRectangle updates[4];
8472   XRectangle overlap;
8473   XPoint     pt;
8474   int        count, i;
8475
8476   /* The main code will redraw the final square, so we
8477      only need to erase the bits that don't overlap.    */
8478   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8479     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8480     for (i = 0; i < count; i++)
8481       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8482                 updates[i].x - anim->prevFrame.x,
8483                 updates[i].y - anim->prevFrame.y,
8484                 updates[i].width, updates[i].height,
8485                 updates[i].x, updates[i].y);
8486   } else {
8487     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8488                 0, 0, squareSize, squareSize,
8489                 anim->prevFrame.x, anim->prevFrame.y);
8490   }
8491 }
8492
8493 static void
8494 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8495      AnimState *anim;
8496      ChessSquare piece; int startColor;
8497      XPoint * start; XPoint * finish;
8498      XPoint frames[]; int nFrames;
8499 {
8500   int n;
8501
8502   BeginAnimation(anim, piece, startColor, start);
8503   for (n = 0; n < nFrames; n++) {
8504     AnimationFrame(anim, &(frames[n]), piece);
8505     FrameDelay(appData.animSpeed);
8506   }
8507   EndAnimation(anim, finish);
8508 }
8509
8510 /* Main control logic for deciding what to animate and how */
8511
8512 void
8513 AnimateMove(board, fromX, fromY, toX, toY)
8514      Board board;
8515      int fromX;
8516      int fromY;
8517      int toX;
8518      int toY;
8519 {
8520   ChessSquare piece;
8521   int hop;
8522   XPoint      start, finish, mid;
8523   XPoint      frames[kFactor * 2 + 1];
8524   int         nFrames, startColor, endColor;
8525
8526   /* Are we animating? */
8527   if (!appData.animate || appData.blindfold)
8528     return;
8529
8530   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
8531      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
8532         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8533
8534   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8535   piece = board[fromY][fromX];
8536   if (piece >= EmptySquare) return;
8537
8538 #if DONT_HOP
8539   hop = FALSE;
8540 #else
8541   hop = (piece == WhiteKnight || piece == BlackKnight);
8542 #endif
8543
8544   if (appData.debugMode) {
8545       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8546                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8547              piece, fromX, fromY, toX, toY);  }
8548
8549   ScreenSquare(fromX, fromY, &start, &startColor);
8550   ScreenSquare(toX, toY, &finish, &endColor);
8551
8552   if (hop) {
8553     /* Knight: make diagonal movement then straight */
8554     if (abs(toY - fromY) < abs(toX - fromX)) {
8555        mid.x = start.x + (finish.x - start.x) / 2;
8556        mid.y = finish.y;
8557      } else {
8558        mid.x = finish.x;
8559        mid.y = start.y + (finish.y - start.y) / 2;
8560      }
8561   } else {
8562     mid.x = start.x + (finish.x - start.x) / 2;
8563     mid.y = start.y + (finish.y - start.y) / 2;
8564   }
8565
8566   /* Don't use as many frames for very short moves */
8567   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8568     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8569   else
8570     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8571   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8572
8573   /* Be sure end square is redrawn */
8574   damage[toY][toX] = True;
8575 }
8576
8577 void
8578 DragPieceBegin(x, y)
8579      int x; int y;
8580 {
8581     int  boardX, boardY, color;
8582     XPoint corner;
8583
8584     /* Are we animating? */
8585     if (!appData.animateDragging || appData.blindfold)
8586       return;
8587
8588     /* Figure out which square we start in and the
8589        mouse position relative to top left corner. */
8590     BoardSquare(x, y, &boardX, &boardY);
8591     player.startBoardX = boardX;
8592     player.startBoardY = boardY;
8593     ScreenSquare(boardX, boardY, &corner, &color);
8594     player.startSquare  = corner;
8595     player.startColor   = color;
8596     /* As soon as we start dragging, the piece will jump slightly to
8597        be centered over the mouse pointer. */
8598     player.mouseDelta.x = squareSize/2;
8599     player.mouseDelta.y = squareSize/2;
8600     /* Initialise animation */
8601     player.dragPiece = PieceForSquare(boardX, boardY);
8602     /* Sanity check */
8603     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8604         player.dragActive = True;
8605         BeginAnimation(&player, player.dragPiece, color, &corner);
8606         /* Mark this square as needing to be redrawn. Note that
8607            we don't remove the piece though, since logically (ie
8608            as seen by opponent) the move hasn't been made yet. */
8609            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8610               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8611            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8612                      corner.x, corner.y, squareSize, squareSize,
8613                      0, 0); // [HGM] zh: unstack in stead of grab
8614         damage[boardY][boardX] = True;
8615     } else {
8616         player.dragActive = False;
8617     }
8618 }
8619
8620 static void
8621 DragPieceMove(x, y)
8622      int x; int y;
8623 {
8624     XPoint corner;
8625
8626     /* Are we animating? */
8627     if (!appData.animateDragging || appData.blindfold)
8628       return;
8629
8630     /* Sanity check */
8631     if (! player.dragActive)
8632       return;
8633     /* Move piece, maintaining same relative position
8634        of mouse within square    */
8635     corner.x = x - player.mouseDelta.x;
8636     corner.y = y - player.mouseDelta.y;
8637     AnimationFrame(&player, &corner, player.dragPiece);
8638 #if HIGHDRAG
8639     if (appData.highlightDragging) {
8640         int boardX, boardY;
8641         BoardSquare(x, y, &boardX, &boardY);
8642         SetHighlights(fromX, fromY, boardX, boardY);
8643     }
8644 #endif
8645 }
8646
8647 void
8648 DragPieceEnd(x, y)
8649      int x; int y;
8650 {
8651     int boardX, boardY, color;
8652     XPoint corner;
8653
8654     /* Are we animating? */
8655     if (!appData.animateDragging || appData.blindfold)
8656       return;
8657
8658     /* Sanity check */
8659     if (! player.dragActive)
8660       return;
8661     /* Last frame in sequence is square piece is
8662        placed on, which may not match mouse exactly. */
8663     BoardSquare(x, y, &boardX, &boardY);
8664     ScreenSquare(boardX, boardY, &corner, &color);
8665     EndAnimation(&player, &corner);
8666
8667     /* Be sure end square is redrawn */
8668     damage[boardY][boardX] = True;
8669
8670     /* This prevents weird things happening with fast successive
8671        clicks which on my Sun at least can cause motion events
8672        without corresponding press/release. */
8673     player.dragActive = False;
8674 }
8675
8676 /* Handle expose event while piece being dragged */
8677
8678 static void
8679 DrawDragPiece ()
8680 {
8681   if (!player.dragActive || appData.blindfold)
8682     return;
8683
8684   /* What we're doing: logically, the move hasn't been made yet,
8685      so the piece is still in it's original square. But visually
8686      it's being dragged around the board. So we erase the square
8687      that the piece is on and draw it at the last known drag point. */
8688   BlankSquare(player.startSquare.x, player.startSquare.y,
8689                 player.startColor, EmptySquare, xBoardWindow);
8690   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8691   damage[player.startBoardY][player.startBoardX] = TRUE;
8692 }
8693
8694 #include <sys/ioctl.h>
8695 int get_term_width()
8696 {
8697     int fd, default_width;
8698
8699     fd = STDIN_FILENO;
8700     default_width = 79; // this is FICS default anyway...
8701
8702 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8703     struct ttysize win;
8704     if (!ioctl(fd, TIOCGSIZE, &win))
8705         default_width = win.ts_cols;
8706 #elif defined(TIOCGWINSZ)
8707     struct winsize win;
8708     if (!ioctl(fd, TIOCGWINSZ, &win))
8709         default_width = win.ws_col;
8710 #endif
8711     return default_width;
8712 }
8713
8714 void update_ics_width()
8715 {
8716     static int old_width = 0;
8717     int new_width = get_term_width();
8718
8719     if (old_width != new_width)
8720        ics_printf("set width %d\n", new_width);
8721     old_width = new_width;
8722 }
8723
8724 void NotifyFrontendLogin()
8725 {
8726     update_ics_width();
8727 }