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