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