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