Refactor move type-in code
[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
2627     XtAppMainLoop(appContext);
2628     if (appData.debugMode) fclose(debugFP); // [DM] debug
2629     return 0;
2630 }
2631
2632 void
2633 ShutDownFrontEnd()
2634 {
2635     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2636         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2637     }
2638     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2639     unlink(gameCopyFilename);
2640     unlink(gamePasteFilename);
2641 }
2642
2643 RETSIGTYPE TermSizeSigHandler(int sig)
2644 {
2645     update_ics_width();
2646 }
2647
2648 RETSIGTYPE
2649 IntSigHandler(sig)
2650      int sig;
2651 {
2652     ExitEvent(sig);
2653 }
2654
2655 RETSIGTYPE
2656 CmailSigHandler(sig)
2657      int sig;
2658 {
2659     int dummy = 0;
2660     int error;
2661
2662     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2663
2664     /* Activate call-back function CmailSigHandlerCallBack()             */
2665     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2666
2667     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2668 }
2669
2670 void
2671 CmailSigHandlerCallBack(isr, closure, message, count, error)
2672      InputSourceRef isr;
2673      VOIDSTAR closure;
2674      char *message;
2675      int count;
2676      int error;
2677 {
2678     BoardToTop();
2679     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2680 }
2681 /**** end signal code ****/
2682
2683
2684 void
2685 ICSInitScript()
2686 {
2687   /* try to open the icsLogon script, either in the location given
2688    * or in the users HOME directory
2689    */
2690
2691   FILE *f;
2692   char buf[MSG_SIZ];
2693   char *homedir;
2694
2695   f = fopen(appData.icsLogon, "r");
2696   if (f == NULL)
2697     {
2698       homedir = getenv("HOME");
2699       if (homedir != NULL)
2700         {
2701           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2702           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2703           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
2704           f = fopen(buf, "r");
2705         }
2706     }
2707
2708   if (f != NULL)
2709     ProcessICSInitScript(f);
2710   else
2711     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2712
2713   return;
2714 }
2715
2716 void
2717 ResetFrontEnd()
2718 {
2719     CommentPopDown();
2720     TagsPopDown();
2721     return;
2722 }
2723
2724 typedef struct {
2725     char *name;
2726     Boolean value;
2727 } Enables;
2728
2729 void
2730 GreyRevert(grey)
2731      Boolean grey;
2732 {
2733     Widget w;
2734     if (!menuBarWidget) return;
2735     w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
2736     if (w == NULL) {
2737       DisplayError("menuEdit.Revert", 0);
2738     } else {
2739       XtSetSensitive(w, !grey);
2740     }
2741     w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
2742     if (w == NULL) {
2743       DisplayError("menuEdit.Annotate", 0);
2744     } else {
2745       XtSetSensitive(w, !grey);
2746     }
2747 }
2748
2749 void
2750 SetMenuEnables(enab)
2751      Enables *enab;
2752 {
2753   Widget w;
2754   if (!menuBarWidget) return;
2755   while (enab->name != NULL) {
2756     w = XtNameToWidget(menuBarWidget, enab->name);
2757     if (w == NULL) {
2758       DisplayError(enab->name, 0);
2759     } else {
2760       XtSetSensitive(w, enab->value);
2761     }
2762     enab++;
2763   }
2764 }
2765
2766 Enables icsEnables[] = {
2767     { "menuFile.Mail Move", False },
2768     { "menuFile.Reload CMail Message", False },
2769     { "menuMode.Machine Black", False },
2770     { "menuMode.Machine White", False },
2771     { "menuMode.Analysis Mode", False },
2772     { "menuMode.Analyze File", False },
2773     { "menuMode.Two Machines", False },
2774     { "menuMode.Machine Match", False },
2775 #ifndef ZIPPY
2776     { "menuEngine.Hint", False },
2777     { "menuEngine.Book", False },
2778     { "menuEngine.Move Now", False },
2779 #ifndef OPTIONSDIALOG
2780     { "menuOptions.Periodic Updates", False },
2781     { "menuOptions.Hide Thinking", False },
2782     { "menuOptions.Ponder Next Move", False },
2783 #endif
2784     { "menuEngine.Engine #1 Settings", False },
2785 #endif
2786     { "menuEngine.Engine #2 Settings", False },
2787     { "menuEdit.Annotate", False },
2788     { NULL, False }
2789 };
2790
2791 Enables ncpEnables[] = {
2792     { "menuFile.Mail Move", False },
2793     { "menuFile.Reload CMail Message", False },
2794     { "menuMode.Machine White", False },
2795     { "menuMode.Machine Black", False },
2796     { "menuMode.Analysis Mode", False },
2797     { "menuMode.Analyze File", False },
2798     { "menuMode.Two Machines", False },
2799     { "menuMode.Machine Match", False },
2800     { "menuMode.ICS Client", False },
2801     { "menuView.ICStex", False },
2802     { "menuView.ICS Input Box", False },
2803     { "Action", False },
2804     { "menuEdit.Revert", False },
2805     { "menuEdit.Annotate", False },
2806     { "menuEngine.Engine #1 Settings", False },
2807     { "menuEngine.Engine #2 Settings", False },
2808     { "menuEngine.Move Now", False },
2809     { "menuEngine.Retract Move", False },
2810     { "menuOptions.ICS", False },
2811 #ifndef OPTIONSDIALOG
2812     { "menuOptions.Auto Flag", False },
2813     { "menuOptions.Auto Flip View", False },
2814 //    { "menuOptions.ICS Alarm", False },
2815     { "menuOptions.Move Sound", False },
2816     { "menuOptions.Hide Thinking", False },
2817     { "menuOptions.Periodic Updates", False },
2818     { "menuOptions.Ponder Next Move", False },
2819 #endif
2820     { "menuEngine.Hint", False },
2821     { "menuEngine.Book", False },
2822     { NULL, False }
2823 };
2824
2825 Enables gnuEnables[] = {
2826     { "menuMode.ICS Client", False },
2827     { "menuView.ICStex", False },
2828     { "menuView.ICS Input Box", False },
2829     { "menuAction.Accept", False },
2830     { "menuAction.Decline", False },
2831     { "menuAction.Rematch", False },
2832     { "menuAction.Adjourn", False },
2833     { "menuAction.Stop Examining", False },
2834     { "menuAction.Stop Observing", False },
2835     { "menuAction.Upload to Examine", False },
2836     { "menuEdit.Revert", False },
2837     { "menuEdit.Annotate", False },
2838     { "menuOptions.ICS", False },
2839
2840     /* The next two options rely on SetCmailMode being called *after*    */
2841     /* SetGNUMode so that when GNU is being used to give hints these     */
2842     /* menu options are still available                                  */
2843
2844     { "menuFile.Mail Move", False },
2845     { "menuFile.Reload CMail Message", False },
2846     { NULL, False }
2847 };
2848
2849 Enables cmailEnables[] = {
2850     { "Action", True },
2851     { "menuAction.Call Flag", False },
2852     { "menuAction.Draw", True },
2853     { "menuAction.Adjourn", False },
2854     { "menuAction.Abort", False },
2855     { "menuAction.Stop Observing", False },
2856     { "menuAction.Stop Examining", False },
2857     { "menuFile.Mail Move", True },
2858     { "menuFile.Reload CMail Message", True },
2859     { NULL, False }
2860 };
2861
2862 Enables trainingOnEnables[] = {
2863   { "menuMode.Edit Comment", False },
2864   { "menuMode.Pause", False },
2865   { "menuEdit.Forward", False },
2866   { "menuEdit.Backward", False },
2867   { "menuEdit.Forward to End", False },
2868   { "menuEdit.Back to Start", False },
2869   { "menuEngine.Move Now", False },
2870   { "menuEdit.Truncate Game", False },
2871   { NULL, False }
2872 };
2873
2874 Enables trainingOffEnables[] = {
2875   { "menuMode.Edit Comment", True },
2876   { "menuMode.Pause", True },
2877   { "menuEdit.Forward", True },
2878   { "menuEdit.Backward", True },
2879   { "menuEdit.Forward to End", True },
2880   { "menuEdit.Back to Start", True },
2881   { "menuEngine.Move Now", True },
2882   { "menuEdit.Truncate Game", True },
2883   { NULL, False }
2884 };
2885
2886 Enables machineThinkingEnables[] = {
2887   { "menuFile.Load Game", False },
2888 //  { "menuFile.Load Next Game", False },
2889 //  { "menuFile.Load Previous Game", False },
2890 //  { "menuFile.Reload Same Game", False },
2891   { "menuEdit.Paste Game", False },
2892   { "menuFile.Load Position", False },
2893 //  { "menuFile.Load Next Position", False },
2894 //  { "menuFile.Load Previous Position", False },
2895 //  { "menuFile.Reload Same Position", False },
2896   { "menuEdit.Paste Position", False },
2897   { "menuMode.Machine White", False },
2898   { "menuMode.Machine Black", False },
2899   { "menuMode.Two Machines", False },
2900   { "menuMode.Machine Match", False },
2901   { "menuEngine.Retract Move", False },
2902   { NULL, False }
2903 };
2904
2905 Enables userThinkingEnables[] = {
2906   { "menuFile.Load Game", True },
2907 //  { "menuFile.Load Next Game", True },
2908 //  { "menuFile.Load Previous Game", True },
2909 //  { "menuFile.Reload Same Game", True },
2910   { "menuEdit.Paste Game", True },
2911   { "menuFile.Load Position", True },
2912 //  { "menuFile.Load Next Position", True },
2913 //  { "menuFile.Load Previous Position", True },
2914 //  { "menuFile.Reload Same Position", True },
2915   { "menuEdit.Paste Position", True },
2916   { "menuMode.Machine White", True },
2917   { "menuMode.Machine Black", True },
2918   { "menuMode.Two Machines", True },
2919   { "menuMode.Machine Match", True },
2920   { "menuEngine.Retract Move", True },
2921   { NULL, False }
2922 };
2923
2924 void SetICSMode()
2925 {
2926   SetMenuEnables(icsEnables);
2927
2928 #if ZIPPY
2929   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2930      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2931 #endif
2932 }
2933
2934 void
2935 SetNCPMode()
2936 {
2937   SetMenuEnables(ncpEnables);
2938 }
2939
2940 void
2941 SetGNUMode()
2942 {
2943   SetMenuEnables(gnuEnables);
2944 }
2945
2946 void
2947 SetCmailMode()
2948 {
2949   SetMenuEnables(cmailEnables);
2950 }
2951
2952 void
2953 SetTrainingModeOn()
2954 {
2955   SetMenuEnables(trainingOnEnables);
2956   if (appData.showButtonBar) {
2957     XtSetSensitive(buttonBarWidget, False);
2958   }
2959   CommentPopDown();
2960 }
2961
2962 void
2963 SetTrainingModeOff()
2964 {
2965   SetMenuEnables(trainingOffEnables);
2966   if (appData.showButtonBar) {
2967     XtSetSensitive(buttonBarWidget, True);
2968   }
2969 }
2970
2971 void
2972 SetUserThinkingEnables()
2973 {
2974   if (appData.noChessProgram) return;
2975   SetMenuEnables(userThinkingEnables);
2976 }
2977
2978 void
2979 SetMachineThinkingEnables()
2980 {
2981   if (appData.noChessProgram) return;
2982   SetMenuEnables(machineThinkingEnables);
2983   switch (gameMode) {
2984   case MachinePlaysBlack:
2985   case MachinePlaysWhite:
2986   case TwoMachinesPlay:
2987     XtSetSensitive(XtNameToWidget(menuBarWidget,
2988                                   ModeToWidgetName(gameMode)), True);
2989     break;
2990   default:
2991     break;
2992   }
2993 }
2994
2995 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2996 #define HISTORY_SIZE 64
2997 static char *history[HISTORY_SIZE];
2998 int histIn = 0, histP = 0;
2999
3000 void
3001 SaveInHistory(char *cmd)
3002 {
3003   if (history[histIn] != NULL) {
3004     free(history[histIn]);
3005     history[histIn] = NULL;
3006   }
3007   if (*cmd == NULLCHAR) return;
3008   history[histIn] = StrSave(cmd);
3009   histIn = (histIn + 1) % HISTORY_SIZE;
3010   if (history[histIn] != NULL) {
3011     free(history[histIn]);
3012     history[histIn] = NULL;
3013   }
3014   histP = histIn;
3015 }
3016
3017 char *
3018 PrevInHistory(char *cmd)
3019 {
3020   int newhp;
3021   if (histP == histIn) {
3022     if (history[histIn] != NULL) free(history[histIn]);
3023     history[histIn] = StrSave(cmd);
3024   }
3025   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3026   if (newhp == histIn || history[newhp] == NULL) return NULL;
3027   histP = newhp;
3028   return history[histP];
3029 }
3030
3031 char *
3032 NextInHistory()
3033 {
3034   if (histP == histIn) return NULL;
3035   histP = (histP + 1) % HISTORY_SIZE;
3036   return history[histP];   
3037 }
3038 // end of borrowed code
3039
3040 #define Abs(n) ((n)<0 ? -(n) : (n))
3041
3042 /*
3043  * Find a font that matches "pattern" that is as close as
3044  * possible to the targetPxlSize.  Prefer fonts that are k
3045  * pixels smaller to fonts that are k pixels larger.  The
3046  * pattern must be in the X Consortium standard format,
3047  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3048  * The return value should be freed with XtFree when no
3049  * longer needed.
3050  */
3051 char *
3052 FindFont(pattern, targetPxlSize)
3053      char *pattern;
3054      int targetPxlSize;
3055 {
3056     char **fonts, *p, *best, *scalable, *scalableTail;
3057     int i, j, nfonts, minerr, err, pxlSize;
3058
3059 #ifdef ENABLE_NLS
3060     char **missing_list;
3061     int missing_count;
3062     char *def_string, *base_fnt_lst, strInt[3];
3063     XFontSet fntSet;
3064     XFontStruct **fnt_list;
3065     base_fnt_lst = calloc(1, strlen(pattern) + 3);
3066     snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3067     p = strstr(pattern, "--");
3068     strncpy(base_fnt_lst, pattern, p - pattern + 2);
3069     strcat(base_fnt_lst, strInt);
3070     strcat(base_fnt_lst, strchr(p + 2, '-'));
3071
3072     if ((fntSet = XCreateFontSet(xDisplay,
3073                                  base_fnt_lst,
3074                                  &missing_list,
3075                                  &missing_count,
3076                                  &def_string)) == NULL) {
3077
3078        fprintf(stderr, _("Unable to create font set.\n"));
3079        exit (2);
3080     }
3081
3082     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3083 #else
3084     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3085     if (nfonts < 1) {
3086         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3087                 programName, pattern);
3088         exit(2);
3089     }
3090 #endif
3091
3092     best = fonts[0];
3093     scalable = NULL;
3094     minerr = 999999;
3095     for (i=0; i<nfonts; i++) {
3096         j = 0;
3097         p = fonts[i];
3098         if (*p != '-') continue;
3099         while (j < 7) {
3100             if (*p == NULLCHAR) break;
3101             if (*p++ == '-') j++;
3102         }
3103         if (j < 7) continue;
3104         pxlSize = atoi(p);
3105         if (pxlSize == 0) {
3106             scalable = fonts[i];
3107             scalableTail = p;
3108         } else {
3109             err = pxlSize - targetPxlSize;
3110             if (Abs(err) < Abs(minerr) ||
3111                 (minerr > 0 && err < 0 && -err == minerr)) {
3112                 best = fonts[i];
3113                 minerr = err;
3114             }
3115         }
3116     }
3117     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3118         /* If the error is too big and there is a scalable font,
3119            use the scalable font. */
3120         int headlen = scalableTail - scalable;
3121         p = (char *) XtMalloc(strlen(scalable) + 10);
3122         while (isdigit(*scalableTail)) scalableTail++;
3123         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3124     } else {
3125         p = (char *) XtMalloc(strlen(best) + 2);
3126         safeStrCpy(p, best, strlen(best)+1 );
3127     }
3128     if (appData.debugMode) {
3129         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
3130                 pattern, targetPxlSize, p);
3131     }
3132 #ifdef ENABLE_NLS
3133     if (missing_count > 0)
3134        XFreeStringList(missing_list);
3135     XFreeFontSet(xDisplay, fntSet);
3136 #else
3137      XFreeFontNames(fonts);
3138 #endif
3139     return p;
3140 }
3141
3142 void DeleteGCs()
3143 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3144     // must be called before all non-first callse to CreateGCs()
3145     XtReleaseGC(shellWidget, highlineGC);
3146     XtReleaseGC(shellWidget, lightSquareGC);
3147     XtReleaseGC(shellWidget, darkSquareGC);
3148     XtReleaseGC(shellWidget, lineGC);
3149     if (appData.monoMode) {
3150         if (DefaultDepth(xDisplay, xScreen) == 1) {
3151             XtReleaseGC(shellWidget, wbPieceGC);
3152         } else {
3153             XtReleaseGC(shellWidget, bwPieceGC);
3154         }
3155     } else {
3156         XtReleaseGC(shellWidget, prelineGC);
3157         XtReleaseGC(shellWidget, jailSquareGC);
3158         XtReleaseGC(shellWidget, wdPieceGC);
3159         XtReleaseGC(shellWidget, wlPieceGC);
3160         XtReleaseGC(shellWidget, wjPieceGC);
3161         XtReleaseGC(shellWidget, bdPieceGC);
3162         XtReleaseGC(shellWidget, blPieceGC);
3163         XtReleaseGC(shellWidget, bjPieceGC);
3164     }
3165 }
3166
3167 void CreateGCs(int redo)
3168 {
3169     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3170       | GCBackground | GCFunction | GCPlaneMask;
3171     XGCValues gc_values;
3172     GC copyInvertedGC;
3173
3174     gc_values.plane_mask = AllPlanes;
3175     gc_values.line_width = lineGap;
3176     gc_values.line_style = LineSolid;
3177     gc_values.function = GXcopy;
3178
3179   if(redo) {
3180     DeleteGCs(); // called a second time; clean up old GCs first
3181   } else { // [HGM] grid and font GCs created on first call only
3182     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3183     gc_values.background = XWhitePixel(xDisplay, xScreen);
3184     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3185     XSetFont(xDisplay, coordGC, coordFontID);
3186
3187     // [HGM] make font for holdings counts (white on black)
3188     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3189     gc_values.background = XBlackPixel(xDisplay, xScreen);
3190     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3191     XSetFont(xDisplay, countGC, countFontID);
3192   }
3193     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3194     gc_values.background = XBlackPixel(xDisplay, xScreen);
3195     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3196
3197     if (appData.monoMode) {
3198         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3199         gc_values.background = XWhitePixel(xDisplay, xScreen);
3200         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3201
3202         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3203         gc_values.background = XBlackPixel(xDisplay, xScreen);
3204         lightSquareGC = wbPieceGC
3205           = XtGetGC(shellWidget, value_mask, &gc_values);
3206
3207         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3208         gc_values.background = XWhitePixel(xDisplay, xScreen);
3209         darkSquareGC = bwPieceGC
3210           = XtGetGC(shellWidget, value_mask, &gc_values);
3211
3212         if (DefaultDepth(xDisplay, xScreen) == 1) {
3213             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3214             gc_values.function = GXcopyInverted;
3215             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3216             gc_values.function = GXcopy;
3217             if (XBlackPixel(xDisplay, xScreen) == 1) {
3218                 bwPieceGC = darkSquareGC;
3219                 wbPieceGC = copyInvertedGC;
3220             } else {
3221                 bwPieceGC = copyInvertedGC;
3222                 wbPieceGC = lightSquareGC;
3223             }
3224         }
3225     } else {
3226         gc_values.foreground = highlightSquareColor;
3227         gc_values.background = highlightSquareColor;
3228         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3229
3230         gc_values.foreground = premoveHighlightColor;
3231         gc_values.background = premoveHighlightColor;
3232         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3233
3234         gc_values.foreground = lightSquareColor;
3235         gc_values.background = darkSquareColor;
3236         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3237
3238         gc_values.foreground = darkSquareColor;
3239         gc_values.background = lightSquareColor;
3240         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3241
3242         gc_values.foreground = jailSquareColor;
3243         gc_values.background = jailSquareColor;
3244         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3245
3246         gc_values.foreground = whitePieceColor;
3247         gc_values.background = darkSquareColor;
3248         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3249
3250         gc_values.foreground = whitePieceColor;
3251         gc_values.background = lightSquareColor;
3252         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3253
3254         gc_values.foreground = whitePieceColor;
3255         gc_values.background = jailSquareColor;
3256         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3257
3258         gc_values.foreground = blackPieceColor;
3259         gc_values.background = darkSquareColor;
3260         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3261
3262         gc_values.foreground = blackPieceColor;
3263         gc_values.background = lightSquareColor;
3264         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3265
3266         gc_values.foreground = blackPieceColor;
3267         gc_values.background = jailSquareColor;
3268         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3269     }
3270 }
3271
3272 void loadXIM(xim, xmask, filename, dest, mask)
3273      XImage *xim;
3274      XImage *xmask;
3275      char *filename;
3276      Pixmap *dest;
3277      Pixmap *mask;
3278 {
3279     int x, y, w, h, p;
3280     FILE *fp;
3281     Pixmap temp;
3282     XGCValues   values;
3283     GC maskGC;
3284
3285     fp = fopen(filename, "rb");
3286     if (!fp) {
3287         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3288         exit(1);
3289     }
3290
3291     w = fgetc(fp);
3292     h = fgetc(fp);
3293
3294     for (y=0; y<h; ++y) {
3295         for (x=0; x<h; ++x) {
3296             p = fgetc(fp);
3297
3298             switch (p) {
3299               case 0:
3300                 XPutPixel(xim, x, y, blackPieceColor);
3301                 if (xmask)
3302                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3303                 break;
3304               case 1:
3305                 XPutPixel(xim, x, y, darkSquareColor);
3306                 if (xmask)
3307                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3308                 break;
3309               case 2:
3310                 XPutPixel(xim, x, y, whitePieceColor);
3311                 if (xmask)
3312                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3313                 break;
3314               case 3:
3315                 XPutPixel(xim, x, y, lightSquareColor);
3316                 if (xmask)
3317                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3318                 break;
3319             }
3320         }
3321     }
3322
3323     fclose(fp);
3324
3325     /* create Pixmap of piece */
3326     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3327                           w, h, xim->depth);
3328     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3329               0, 0, 0, 0, w, h);
3330
3331     /* create Pixmap of clipmask
3332        Note: We assume the white/black pieces have the same
3333              outline, so we make only 6 masks. This is okay
3334              since the XPM clipmask routines do the same. */
3335     if (xmask) {
3336       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3337                             w, h, xim->depth);
3338       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3339               0, 0, 0, 0, w, h);
3340
3341       /* now create the 1-bit version */
3342       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3343                           w, h, 1);
3344
3345       values.foreground = 1;
3346       values.background = 0;
3347
3348       /* Don't use XtGetGC, not read only */
3349       maskGC = XCreateGC(xDisplay, *mask,
3350                     GCForeground | GCBackground, &values);
3351       XCopyPlane(xDisplay, temp, *mask, maskGC,
3352                   0, 0, squareSize, squareSize, 0, 0, 1);
3353       XFreePixmap(xDisplay, temp);
3354     }
3355 }
3356
3357
3358 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3359
3360 void CreateXIMPieces()
3361 {
3362     int piece, kind;
3363     char buf[MSG_SIZ];
3364     u_int ss;
3365     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3366     XImage *ximtemp;
3367
3368     ss = squareSize;
3369
3370     /* The XSynchronize calls were copied from CreatePieces.
3371        Not sure if needed, but can't hurt */
3372     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3373                                      buffering bug */
3374
3375     /* temp needed by loadXIM() */
3376     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3377                  0, 0, ss, ss, AllPlanes, XYPixmap);
3378
3379     if (strlen(appData.pixmapDirectory) == 0) {
3380       useImages = 0;
3381     } else {
3382         useImages = 1;
3383         if (appData.monoMode) {
3384           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3385                             0, 2);
3386           ExitEvent(2);
3387         }
3388         fprintf(stderr, _("\nLoading XIMs...\n"));
3389         /* Load pieces */
3390         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3391             fprintf(stderr, "%d", piece+1);
3392             for (kind=0; kind<4; kind++) {
3393                 fprintf(stderr, ".");
3394                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3395                         ExpandPathName(appData.pixmapDirectory),
3396                         piece <= (int) WhiteKing ? "" : "w",
3397                         pieceBitmapNames[piece],
3398                         ximkind[kind], ss);
3399                 ximPieceBitmap[kind][piece] =
3400                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3401                             0, 0, ss, ss, AllPlanes, XYPixmap);
3402                 if (appData.debugMode)
3403                   fprintf(stderr, _("(File:%s:) "), buf);
3404                 loadXIM(ximPieceBitmap[kind][piece],
3405                         ximtemp, buf,
3406                         &(xpmPieceBitmap2[kind][piece]),
3407                         &(ximMaskPm2[piece]));
3408                 if(piece <= (int)WhiteKing)
3409                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3410             }
3411             fprintf(stderr," ");
3412         }
3413         /* Load light and dark squares */
3414         /* If the LSQ and DSQ pieces don't exist, we will
3415            draw them with solid squares. */
3416         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3417         if (access(buf, 0) != 0) {
3418             useImageSqs = 0;
3419         } else {
3420             useImageSqs = 1;
3421             fprintf(stderr, _("light square "));
3422             ximLightSquare=
3423               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3424                         0, 0, ss, ss, AllPlanes, XYPixmap);
3425             if (appData.debugMode)
3426               fprintf(stderr, _("(File:%s:) "), buf);
3427
3428             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3429             fprintf(stderr, _("dark square "));
3430             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3431                     ExpandPathName(appData.pixmapDirectory), ss);
3432             if (appData.debugMode)
3433               fprintf(stderr, _("(File:%s:) "), buf);
3434             ximDarkSquare=
3435               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3436                         0, 0, ss, ss, AllPlanes, XYPixmap);
3437             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3438             xpmJailSquare = xpmLightSquare;
3439         }
3440         fprintf(stderr, _("Done.\n"));
3441     }
3442     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3443 }
3444
3445 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3446
3447 #if HAVE_LIBXPM
3448 void CreateXPMBoard(char *s, int kind)
3449 {
3450     XpmAttributes attr;
3451     attr.valuemask = 0;
3452     if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3453     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3454         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3455     }
3456 }
3457
3458 void FreeXPMPieces()
3459 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3460     // thisroutine has to be called t free the old piece pixmaps
3461     int piece, kind;
3462     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3463         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3464     if(useImageSqs) {
3465         XFreePixmap(xDisplay, xpmLightSquare);
3466         XFreePixmap(xDisplay, xpmDarkSquare);
3467     }
3468 }
3469
3470 void CreateXPMPieces()
3471 {
3472     int piece, kind, r;
3473     char buf[MSG_SIZ];
3474     u_int ss = squareSize;
3475     XpmAttributes attr;
3476     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3477     XpmColorSymbol symbols[4];
3478     static int redo = False;
3479
3480     if(redo) FreeXPMPieces(); else redo = 1;
3481
3482     /* The XSynchronize calls were copied from CreatePieces.
3483        Not sure if needed, but can't hurt */
3484     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3485
3486     /* Setup translations so piece colors match square colors */
3487     symbols[0].name = "light_piece";
3488     symbols[0].value = appData.whitePieceColor;
3489     symbols[1].name = "dark_piece";
3490     symbols[1].value = appData.blackPieceColor;
3491     symbols[2].name = "light_square";
3492     symbols[2].value = appData.lightSquareColor;
3493     symbols[3].name = "dark_square";
3494     symbols[3].value = appData.darkSquareColor;
3495
3496     attr.valuemask = XpmColorSymbols;
3497     attr.colorsymbols = symbols;
3498     attr.numsymbols = 4;
3499
3500     if (appData.monoMode) {
3501       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3502                         0, 2);
3503       ExitEvent(2);
3504     }
3505     if (strlen(appData.pixmapDirectory) == 0) {
3506         XpmPieces* pieces = builtInXpms;
3507         useImages = 1;
3508         /* Load pieces */
3509         while (pieces->size != squareSize && pieces->size) pieces++;
3510         if (!pieces->size) {
3511           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3512           exit(1);
3513         }
3514         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3515             for (kind=0; kind<4; kind++) {
3516
3517                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3518                                                pieces->xpm[piece][kind],
3519                                                &(xpmPieceBitmap2[kind][piece]),
3520                                                NULL, &attr)) != 0) {
3521                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3522                           r, buf);
3523                   exit(1);
3524                 }
3525                 if(piece <= (int) WhiteKing)
3526                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3527             }
3528         }
3529         useImageSqs = 0;
3530         xpmJailSquare = xpmLightSquare;
3531     } else {
3532         useImages = 1;
3533
3534         fprintf(stderr, _("\nLoading XPMs...\n"));
3535
3536         /* Load pieces */
3537         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3538             fprintf(stderr, "%d ", piece+1);
3539             for (kind=0; kind<4; kind++) {
3540               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3541                         ExpandPathName(appData.pixmapDirectory),
3542                         piece > (int) WhiteKing ? "w" : "",
3543                         pieceBitmapNames[piece],
3544                         xpmkind[kind], ss);
3545                 if (appData.debugMode) {
3546                     fprintf(stderr, _("(File:%s:) "), buf);
3547                 }
3548                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3549                                            &(xpmPieceBitmap2[kind][piece]),
3550                                            NULL, &attr)) != 0) {
3551                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3552                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3553                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3554                                 ExpandPathName(appData.pixmapDirectory),
3555                                 xpmkind[kind], ss);
3556                         if (appData.debugMode) {
3557                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3558                         }
3559                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3560                                                 &(xpmPieceBitmap2[kind][piece]),
3561                                                 NULL, &attr);
3562                     }
3563                     if (r != 0) {
3564                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3565                                 r, buf);
3566                         exit(1);
3567                     }
3568                 }
3569                 if(piece <= (int) WhiteKing)
3570                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3571             }
3572         }
3573         /* Load light and dark squares */
3574         /* If the LSQ and DSQ pieces don't exist, we will
3575            draw them with solid squares. */
3576         fprintf(stderr, _("light square "));
3577         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3578         if (access(buf, 0) != 0) {
3579             useImageSqs = 0;
3580         } else {
3581             useImageSqs = 1;
3582             if (appData.debugMode)
3583               fprintf(stderr, _("(File:%s:) "), buf);
3584
3585             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3586                                        &xpmLightSquare, NULL, &attr)) != 0) {
3587                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3588                 exit(1);
3589             }
3590             fprintf(stderr, _("dark square "));
3591             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3592                     ExpandPathName(appData.pixmapDirectory), ss);
3593             if (appData.debugMode) {
3594                 fprintf(stderr, _("(File:%s:) "), buf);
3595             }
3596             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3597                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3598                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3599                 exit(1);
3600             }
3601         }
3602         xpmJailSquare = xpmLightSquare;
3603         fprintf(stderr, _("Done.\n"));
3604     }
3605     oldVariant = -1; // kludge to force re-makig of animation masks
3606     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3607                                       buffering bug */
3608 }
3609 #endif /* HAVE_LIBXPM */
3610
3611 #if HAVE_LIBXPM
3612 /* No built-in bitmaps */
3613 void CreatePieces()
3614 {
3615     int piece, kind;
3616     char buf[MSG_SIZ];
3617     u_int ss = squareSize;
3618
3619     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3620                                      buffering bug */
3621
3622     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3623         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3624           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3625                    pieceBitmapNames[piece],
3626                    ss, kind == SOLID ? 's' : 'o');
3627           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3628           if(piece <= (int)WhiteKing)
3629             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3630         }
3631     }
3632
3633     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3634                                       buffering bug */
3635 }
3636 #else
3637 /* With built-in bitmaps */
3638 void CreatePieces()
3639 {
3640     BuiltInBits* bib = builtInBits;
3641     int piece, kind;
3642     char buf[MSG_SIZ];
3643     u_int ss = squareSize;
3644
3645     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3646                                      buffering bug */
3647
3648     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3649
3650     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3651         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3652           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3653                    pieceBitmapNames[piece],
3654                    ss, kind == SOLID ? 's' : 'o');
3655           ReadBitmap(&pieceBitmap2[kind][piece], buf,
3656                      bib->bits[kind][piece], ss, ss);
3657           if(piece <= (int)WhiteKing)
3658             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3659         }
3660     }
3661
3662     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3663                                       buffering bug */
3664 }
3665 #endif
3666
3667 void ReadBitmap(pm, name, bits, wreq, hreq)
3668      Pixmap *pm;
3669      String name;
3670      unsigned char bits[];
3671      u_int wreq, hreq;
3672 {
3673     int x_hot, y_hot;
3674     u_int w, h;
3675     int errcode;
3676     char msg[MSG_SIZ], fullname[MSG_SIZ];
3677
3678     if (*appData.bitmapDirectory != NULLCHAR) {
3679       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3680       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3681       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3682       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3683                                 &w, &h, pm, &x_hot, &y_hot);
3684       fprintf(stderr, "load %s\n", name);
3685         if (errcode != BitmapSuccess) {
3686             switch (errcode) {
3687               case BitmapOpenFailed:
3688                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3689                 break;
3690               case BitmapFileInvalid:
3691                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3692                 break;
3693               case BitmapNoMemory:
3694                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3695                         fullname);
3696                 break;
3697               default:
3698                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3699                         errcode, fullname);
3700                 break;
3701             }
3702             fprintf(stderr, _("%s: %s...using built-in\n"),
3703                     programName, msg);
3704         } else if (w != wreq || h != hreq) {
3705             fprintf(stderr,
3706                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3707                     programName, fullname, w, h, wreq, hreq);
3708         } else {
3709             return;
3710         }
3711     }
3712     if (bits != NULL) {
3713         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3714                                     wreq, hreq);
3715     }
3716 }
3717
3718 void CreateGrid()
3719 {
3720     int i, j;
3721
3722     if (lineGap == 0) return;
3723
3724     /* [HR] Split this into 2 loops for non-square boards. */
3725
3726     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3727         gridSegments[i].x1 = 0;
3728         gridSegments[i].x2 =
3729           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3730         gridSegments[i].y1 = gridSegments[i].y2
3731           = lineGap / 2 + (i * (squareSize + lineGap));
3732     }
3733
3734     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3735         gridSegments[j + i].y1 = 0;
3736         gridSegments[j + i].y2 =
3737           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3738         gridSegments[j + i].x1 = gridSegments[j + i].x2
3739           = lineGap / 2 + (j * (squareSize + lineGap));
3740     }
3741 }
3742
3743 static void MenuBarSelect(w, addr, index)
3744      Widget w;
3745      caddr_t addr;
3746      caddr_t index;
3747 {
3748     XtActionProc proc = (XtActionProc) addr;
3749
3750     (proc)(NULL, NULL, NULL, NULL);
3751 }
3752
3753 void CreateMenuBarPopup(parent, name, mb)
3754      Widget parent;
3755      String name;
3756      Menu *mb;
3757 {
3758     int j;
3759     Widget menu, entry;
3760     MenuItem *mi;
3761     Arg args[16];
3762
3763     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3764                               parent, NULL, 0);
3765     j = 0;
3766     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3767     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3768     mi = mb->mi;
3769     while (mi->string != NULL) {
3770         if (strcmp(mi->string, "----") == 0) {
3771             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3772                                           menu, args, j);
3773         } else {
3774           XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3775             entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3776                                           menu, args, j+1);
3777             XtAddCallback(entry, XtNcallback,
3778                           (XtCallbackProc) MenuBarSelect,
3779                           (caddr_t) mi->proc);
3780         }
3781         mi++;
3782     }
3783 }
3784
3785 Widget CreateMenuBar(mb)
3786      Menu *mb;
3787 {
3788     int j;
3789     Widget anchor, menuBar;
3790     Arg args[16];
3791     char menuName[MSG_SIZ];
3792
3793     j = 0;
3794     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3795     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3796     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3797     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3798                              formWidget, args, j);
3799
3800     while (mb->name != NULL) {
3801         safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3802         strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3803         j = 0;
3804         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3805         if (tinyLayout) {
3806             char shortName[2];
3807             shortName[0] = mb->name[0];
3808             shortName[1] = NULLCHAR;
3809             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3810         }
3811       else {
3812           XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3813       }
3814
3815         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3816         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3817                                        menuBar, args, j);
3818         CreateMenuBarPopup(menuBar, menuName, mb);
3819         mb++;
3820     }
3821     return menuBar;
3822 }
3823
3824 Widget CreateButtonBar(mi)
3825      MenuItem *mi;
3826 {
3827     int j;
3828     Widget button, buttonBar;
3829     Arg args[16];
3830
3831     j = 0;
3832     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3833     if (tinyLayout) {
3834         XtSetArg(args[j], XtNhSpace, 0); j++;
3835     }
3836     XtSetArg(args[j], XtNborderWidth, 0); j++;
3837     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3838     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3839                                formWidget, args, j);
3840
3841     while (mi->string != NULL) {
3842         j = 0;
3843         if (tinyLayout) {
3844             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3845             XtSetArg(args[j], XtNborderWidth, 0); j++;
3846         }
3847       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3848         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3849                                        buttonBar, args, j);
3850         XtAddCallback(button, XtNcallback,
3851                       (XtCallbackProc) MenuBarSelect,
3852                       (caddr_t) mi->proc);
3853         mi++;
3854     }
3855     return buttonBar;
3856 }
3857
3858 Widget
3859 CreatePieceMenu(name, color)
3860      char *name;
3861      int color;
3862 {
3863     int i;
3864     Widget entry, menu;
3865     Arg args[16];
3866     ChessSquare selection;
3867
3868     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3869                               boardWidget, args, 0);
3870
3871     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3872         String item = pieceMenuStrings[color][i];
3873
3874         if (strcmp(item, "----") == 0) {
3875             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3876                                           menu, NULL, 0);
3877         } else {
3878           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3879             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3880                                 menu, args, 1);
3881             selection = pieceMenuTranslation[color][i];
3882             XtAddCallback(entry, XtNcallback,
3883                           (XtCallbackProc) PieceMenuSelect,
3884                           (caddr_t) selection);
3885             if (selection == WhitePawn || selection == BlackPawn) {
3886                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3887                 XtSetValues(menu, args, 1);
3888             }
3889         }
3890     }
3891     return menu;
3892 }
3893
3894 void
3895 CreatePieceMenus()
3896 {
3897     int i;
3898     Widget entry;
3899     Arg args[16];
3900     ChessSquare selection;
3901
3902     whitePieceMenu = CreatePieceMenu("menuW", 0);
3903     blackPieceMenu = CreatePieceMenu("menuB", 1);
3904
3905     XtRegisterGrabAction(PieceMenuPopup, True,
3906                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3907                          GrabModeAsync, GrabModeAsync);
3908
3909     XtSetArg(args[0], XtNlabel, _("Drop"));
3910     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3911                                   boardWidget, args, 1);
3912     for (i = 0; i < DROP_MENU_SIZE; i++) {
3913         String item = dropMenuStrings[i];
3914
3915         if (strcmp(item, "----") == 0) {
3916             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3917                                           dropMenu, NULL, 0);
3918         } else {
3919           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3920             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3921                                 dropMenu, args, 1);
3922             selection = dropMenuTranslation[i];
3923             XtAddCallback(entry, XtNcallback,
3924                           (XtCallbackProc) DropMenuSelect,
3925                           (caddr_t) selection);
3926         }
3927     }
3928 }
3929
3930 void SetupDropMenu()
3931 {
3932     int i, j, count;
3933     char label[32];
3934     Arg args[16];
3935     Widget entry;
3936     char* p;
3937
3938     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3939         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3940         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3941                    dmEnables[i].piece);
3942         XtSetSensitive(entry, p != NULL || !appData.testLegality
3943                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3944                                        && !appData.icsActive));
3945         count = 0;
3946         while (p && *p++ == dmEnables[i].piece) count++;
3947         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3948         j = 0;
3949         XtSetArg(args[j], XtNlabel, label); j++;
3950         XtSetValues(entry, args, j);
3951     }
3952 }
3953
3954 void PieceMenuPopup(w, event, params, num_params)
3955      Widget w;
3956      XEvent *event;
3957      String *params;
3958      Cardinal *num_params;
3959 {
3960     String whichMenu; int menuNr;
3961     shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3962     if (event->type == ButtonRelease)
3963         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3964     else if (event->type == ButtonPress)
3965         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3966     switch(menuNr) {
3967       case 0: whichMenu = params[0]; break;
3968       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3969       case 2:
3970       case -1: if (errorUp) ErrorPopDown();
3971       default: return;
3972     }
3973     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3974 }
3975
3976 static void PieceMenuSelect(w, piece, junk)
3977      Widget w;
3978      ChessSquare piece;
3979      caddr_t junk;
3980 {
3981     if (pmFromX < 0 || pmFromY < 0) return;
3982     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3983 }
3984
3985 static void DropMenuSelect(w, piece, junk)
3986      Widget w;
3987      ChessSquare piece;
3988      caddr_t junk;
3989 {
3990     if (pmFromX < 0 || pmFromY < 0) return;
3991     DropMenuEvent(piece, pmFromX, pmFromY);
3992 }
3993
3994 void WhiteClock(w, event, prms, nprms)
3995      Widget w;
3996      XEvent *event;
3997      String *prms;
3998      Cardinal *nprms;
3999 {
4000     ClockClick(0);
4001 }
4002
4003 void BlackClock(w, event, prms, nprms)
4004      Widget w;
4005      XEvent *event;
4006      String *prms;
4007      Cardinal *nprms;
4008 {
4009     ClockClick(1);
4010 }
4011
4012
4013 /*
4014  * If the user selects on a border boundary, return -1; if off the board,
4015  *   return -2.  Otherwise map the event coordinate to the square.
4016  */
4017 int EventToSquare(x, limit)
4018      int x;
4019 {
4020     if (x <= 0)
4021       return -2;
4022     if (x < lineGap)
4023       return -1;
4024     x -= lineGap;
4025     if ((x % (squareSize + lineGap)) >= squareSize)
4026       return -1;
4027     x /= (squareSize + lineGap);
4028     if (x >= limit)
4029       return -2;
4030     return x;
4031 }
4032
4033 static void do_flash_delay(msec)
4034      unsigned long msec;
4035 {
4036     TimeDelay(msec);
4037 }
4038
4039 static void drawHighlight(file, rank, gc)
4040      int file, rank;
4041      GC gc;
4042 {
4043     int x, y;
4044
4045     if (lineGap == 0) return;
4046
4047     if (flipView) {
4048         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4049           (squareSize + lineGap);
4050         y = lineGap/2 + rank * (squareSize + lineGap);
4051     } else {
4052         x = lineGap/2 + file * (squareSize + lineGap);
4053         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4054           (squareSize + lineGap);
4055     }
4056
4057     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4058                    squareSize+lineGap, squareSize+lineGap);
4059 }
4060
4061 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4062 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4063
4064 void
4065 SetHighlights(fromX, fromY, toX, toY)
4066      int fromX, fromY, toX, toY;
4067 {
4068     if (hi1X != fromX || hi1Y != fromY) {
4069         if (hi1X >= 0 && hi1Y >= 0) {
4070             drawHighlight(hi1X, hi1Y, lineGC);
4071         }
4072     } // [HGM] first erase both, then draw new!
4073     if (hi2X != toX || hi2Y != toY) {
4074         if (hi2X >= 0 && hi2Y >= 0) {
4075             drawHighlight(hi2X, hi2Y, lineGC);
4076         }
4077     }
4078     if (hi1X != fromX || hi1Y != fromY) {
4079         if (fromX >= 0 && fromY >= 0) {
4080             drawHighlight(fromX, fromY, highlineGC);
4081         }
4082     }
4083     if (hi2X != toX || hi2Y != toY) {
4084         if (toX >= 0 && toY >= 0) {
4085             drawHighlight(toX, toY, highlineGC);
4086         }
4087     }
4088     hi1X = fromX;
4089     hi1Y = fromY;
4090     hi2X = toX;
4091     hi2Y = toY;
4092 }
4093
4094 void
4095 ClearHighlights()
4096 {
4097     SetHighlights(-1, -1, -1, -1);
4098 }
4099
4100
4101 void
4102 SetPremoveHighlights(fromX, fromY, toX, toY)
4103      int fromX, fromY, toX, toY;
4104 {
4105     if (pm1X != fromX || pm1Y != fromY) {
4106         if (pm1X >= 0 && pm1Y >= 0) {
4107             drawHighlight(pm1X, pm1Y, lineGC);
4108         }
4109         if (fromX >= 0 && fromY >= 0) {
4110             drawHighlight(fromX, fromY, prelineGC);
4111         }
4112     }
4113     if (pm2X != toX || pm2Y != toY) {
4114         if (pm2X >= 0 && pm2Y >= 0) {
4115             drawHighlight(pm2X, pm2Y, lineGC);
4116         }
4117         if (toX >= 0 && toY >= 0) {
4118             drawHighlight(toX, toY, prelineGC);
4119         }
4120     }
4121     pm1X = fromX;
4122     pm1Y = fromY;
4123     pm2X = toX;
4124     pm2Y = toY;
4125 }
4126
4127 void
4128 ClearPremoveHighlights()
4129 {
4130   SetPremoveHighlights(-1, -1, -1, -1);
4131 }
4132
4133 static int CutOutSquare(x, y, x0, y0, kind)
4134      int x, y, *x0, *y0, kind;
4135 {
4136     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4137     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4138     *x0 = 0; *y0 = 0;
4139     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4140     if(textureW[kind] < W*squareSize)
4141         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4142     else
4143         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4144     if(textureH[kind] < H*squareSize)
4145         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4146     else
4147         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4148     return 1;
4149 }
4150
4151 static void BlankSquare(x, y, color, piece, dest, fac)
4152      int x, y, color, fac;
4153      ChessSquare piece;
4154      Drawable dest;
4155 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4156     int x0, y0;
4157     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4158         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4159                   squareSize, squareSize, x*fac, y*fac);
4160     } else
4161     if (useImages && useImageSqs) {
4162         Pixmap pm;
4163         switch (color) {
4164           case 1: /* light */
4165             pm = xpmLightSquare;
4166             break;
4167           case 0: /* dark */
4168             pm = xpmDarkSquare;
4169             break;
4170           case 2: /* neutral */
4171           default:
4172             pm = xpmJailSquare;
4173             break;
4174         }
4175         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4176                   squareSize, squareSize, x*fac, y*fac);
4177     } else {
4178         GC gc;
4179         switch (color) {
4180           case 1: /* light */
4181             gc = lightSquareGC;
4182             break;
4183           case 0: /* dark */
4184             gc = darkSquareGC;
4185             break;
4186           case 2: /* neutral */
4187           default:
4188             gc = jailSquareGC;
4189             break;
4190         }
4191         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4192     }
4193 }
4194
4195 /*
4196    I split out the routines to draw a piece so that I could
4197    make a generic flash routine.
4198 */
4199 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4200      ChessSquare piece;
4201      int square_color, x, y;
4202      Drawable dest;
4203 {
4204     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4205     switch (square_color) {
4206       case 1: /* light */
4207       case 2: /* neutral */
4208       default:
4209         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4210                   ? *pieceToOutline(piece)
4211                   : *pieceToSolid(piece),
4212                   dest, bwPieceGC, 0, 0,
4213                   squareSize, squareSize, x, y);
4214         break;
4215       case 0: /* dark */
4216         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4217                   ? *pieceToSolid(piece)
4218                   : *pieceToOutline(piece),
4219                   dest, wbPieceGC, 0, 0,
4220                   squareSize, squareSize, x, y);
4221         break;
4222     }
4223 }
4224
4225 static void monoDrawPiece(piece, square_color, x, y, dest)
4226      ChessSquare piece;
4227      int square_color, x, y;
4228      Drawable dest;
4229 {
4230     switch (square_color) {
4231       case 1: /* light */
4232       case 2: /* neutral */
4233       default:
4234         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4235                    ? *pieceToOutline(piece)
4236                    : *pieceToSolid(piece),
4237                    dest, bwPieceGC, 0, 0,
4238                    squareSize, squareSize, x, y, 1);
4239         break;
4240       case 0: /* dark */
4241         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4242                    ? *pieceToSolid(piece)
4243                    : *pieceToOutline(piece),
4244                    dest, wbPieceGC, 0, 0,
4245                    squareSize, squareSize, x, y, 1);
4246         break;
4247     }
4248 }
4249
4250 static void colorDrawPiece(piece, square_color, x, y, dest)
4251      ChessSquare piece;
4252      int square_color, x, y;
4253      Drawable dest;
4254 {
4255     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4256     switch (square_color) {
4257       case 1: /* light */
4258         XCopyPlane(xDisplay, *pieceToSolid(piece),
4259                    dest, (int) piece < (int) BlackPawn
4260                    ? wlPieceGC : blPieceGC, 0, 0,
4261                    squareSize, squareSize, x, y, 1);
4262         break;
4263       case 0: /* dark */
4264         XCopyPlane(xDisplay, *pieceToSolid(piece),
4265                    dest, (int) piece < (int) BlackPawn
4266                    ? wdPieceGC : bdPieceGC, 0, 0,
4267                    squareSize, squareSize, x, y, 1);
4268         break;
4269       case 2: /* neutral */
4270       default:
4271         XCopyPlane(xDisplay, *pieceToSolid(piece),
4272                    dest, (int) piece < (int) BlackPawn
4273                    ? wjPieceGC : bjPieceGC, 0, 0,
4274                    squareSize, squareSize, x, y, 1);
4275         break;
4276     }
4277 }
4278
4279 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4280      ChessSquare piece;
4281      int square_color, x, y;
4282      Drawable dest;
4283 {
4284     int kind, p = piece;
4285
4286     switch (square_color) {
4287       case 1: /* light */
4288       case 2: /* neutral */
4289       default:
4290         if ((int)piece < (int) BlackPawn) {
4291             kind = 0;
4292         } else {
4293             kind = 2;
4294             piece -= BlackPawn;
4295         }
4296         break;
4297       case 0: /* dark */
4298         if ((int)piece < (int) BlackPawn) {
4299             kind = 1;
4300         } else {
4301             kind = 3;
4302             piece -= BlackPawn;
4303         }
4304         break;
4305     }
4306     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4307     if(useTexture & square_color+1) {
4308         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4309         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4310         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4311         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4312         XSetClipMask(xDisplay, wlPieceGC, None);
4313         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4314     } else
4315     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4316               dest, wlPieceGC, 0, 0,
4317               squareSize, squareSize, x, y);
4318 }
4319
4320 typedef void (*DrawFunc)();
4321
4322 DrawFunc ChooseDrawFunc()
4323 {
4324     if (appData.monoMode) {
4325         if (DefaultDepth(xDisplay, xScreen) == 1) {
4326             return monoDrawPiece_1bit;
4327         } else {
4328             return monoDrawPiece;
4329         }
4330     } else {
4331         if (useImages)
4332           return colorDrawPieceImage;
4333         else
4334           return colorDrawPiece;
4335     }
4336 }
4337
4338 /* [HR] determine square color depending on chess variant. */
4339 static int SquareColor(row, column)
4340      int row, column;
4341 {
4342     int square_color;
4343
4344     if (gameInfo.variant == VariantXiangqi) {
4345         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4346             square_color = 1;
4347         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4348             square_color = 0;
4349         } else if (row <= 4) {
4350             square_color = 0;
4351         } else {
4352             square_color = 1;
4353         }
4354     } else {
4355         square_color = ((column + row) % 2) == 1;
4356     }
4357
4358     /* [hgm] holdings: next line makes all holdings squares light */
4359     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4360
4361     return square_color;
4362 }
4363
4364 void DrawSquare(row, column, piece, do_flash)
4365      int row, column, do_flash;
4366      ChessSquare piece;
4367 {
4368     int square_color, x, y, direction, font_ascent, font_descent;
4369     int i;
4370     char string[2];
4371     XCharStruct overall;
4372     DrawFunc drawfunc;
4373     int flash_delay;
4374
4375     /* Calculate delay in milliseconds (2-delays per complete flash) */
4376     flash_delay = 500 / appData.flashRate;
4377
4378     if (flipView) {
4379         x = lineGap + ((BOARD_WIDTH-1)-column) *
4380           (squareSize + lineGap);
4381         y = lineGap + row * (squareSize + lineGap);
4382     } else {
4383         x = lineGap + column * (squareSize + lineGap);
4384         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4385           (squareSize + lineGap);
4386     }
4387
4388     if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4389
4390     square_color = SquareColor(row, column);
4391
4392     if ( // [HGM] holdings: blank out area between board and holdings
4393                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4394               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4395                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4396                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4397
4398                         // [HGM] print piece counts next to holdings
4399                         string[1] = NULLCHAR;
4400                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4401                             string[0] = '0' + piece;
4402                             XTextExtents(countFontStruct, string, 1, &direction,
4403                                  &font_ascent, &font_descent, &overall);
4404                             if (appData.monoMode) {
4405                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4406                                                  x + squareSize - overall.width - 2,
4407                                                  y + font_ascent + 1, string, 1);
4408                             } else {
4409                                 XDrawString(xDisplay, xBoardWindow, countGC,
4410                                             x + squareSize - overall.width - 2,
4411                                             y + font_ascent + 1, string, 1);
4412                             }
4413                         }
4414                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4415                             string[0] = '0' + piece;
4416                             XTextExtents(countFontStruct, string, 1, &direction,
4417                                          &font_ascent, &font_descent, &overall);
4418                             if (appData.monoMode) {
4419                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4420                                                  x + 2, y + font_ascent + 1, string, 1);
4421                             } else {
4422                                 XDrawString(xDisplay, xBoardWindow, countGC,
4423                                             x + 2, y + font_ascent + 1, string, 1);
4424                             }
4425                         }
4426     } else {
4427             if (piece == EmptySquare || appData.blindfold) {
4428                         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4429             } else {
4430                         drawfunc = ChooseDrawFunc();
4431
4432                         if (do_flash && appData.flashCount > 0) {
4433                             for (i=0; i<appData.flashCount; ++i) {
4434                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4435                                         XSync(xDisplay, False);
4436                                         do_flash_delay(flash_delay);
4437
4438                                         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4439                                         XSync(xDisplay, False);
4440                                         do_flash_delay(flash_delay);
4441                             }
4442                         }
4443                         drawfunc(piece, square_color, x, y, xBoardWindow);
4444         }
4445         }
4446
4447     string[1] = NULLCHAR;
4448     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4449                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4450         string[0] = 'a' + column - BOARD_LEFT;
4451         XTextExtents(coordFontStruct, string, 1, &direction,
4452                      &font_ascent, &font_descent, &overall);
4453         if (appData.monoMode) {
4454             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4455                              x + squareSize - overall.width - 2,
4456                              y + squareSize - font_descent - 1, string, 1);
4457         } else {
4458             XDrawString(xDisplay, xBoardWindow, coordGC,
4459                         x + squareSize - overall.width - 2,
4460                         y + squareSize - font_descent - 1, string, 1);
4461         }
4462     }
4463     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4464         string[0] = ONE + row;
4465         XTextExtents(coordFontStruct, string, 1, &direction,
4466                      &font_ascent, &font_descent, &overall);
4467         if (appData.monoMode) {
4468             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4469                              x + 2, y + font_ascent + 1, string, 1);
4470         } else {
4471             XDrawString(xDisplay, xBoardWindow, coordGC,
4472                         x + 2, y + font_ascent + 1, string, 1);
4473         }
4474     }
4475     if(!partnerUp && marker[row][column]) {
4476         XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4477                 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4478     }
4479 }
4480
4481
4482 /* Why is this needed on some versions of X? */
4483 void EventProc(widget, unused, event)
4484      Widget widget;
4485      caddr_t unused;
4486      XEvent *event;
4487 {
4488     if (!XtIsRealized(widget))
4489       return;
4490
4491     switch (event->type) {
4492       case Expose:
4493         if (event->xexpose.count > 0) return;  /* no clipping is done */
4494         XDrawPosition(widget, True, NULL);
4495         if(twoBoards) { // [HGM] dual: draw other board in other orientation
4496             flipView = !flipView; partnerUp = !partnerUp;
4497             XDrawPosition(widget, True, NULL);
4498             flipView = !flipView; partnerUp = !partnerUp;
4499         }
4500         break;
4501       case MotionNotify:
4502         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4503       default:
4504         return;
4505     }
4506 }
4507 /* end why */
4508
4509 void DrawPosition(fullRedraw, board)
4510      /*Boolean*/int fullRedraw;
4511      Board board;
4512 {
4513     XDrawPosition(boardWidget, fullRedraw, board);
4514 }
4515
4516 /* Returns 1 if there are "too many" differences between b1 and b2
4517    (i.e. more than 1 move was made) */
4518 static int too_many_diffs(b1, b2)
4519      Board b1, b2;
4520 {
4521     int i, j;
4522     int c = 0;
4523
4524     for (i=0; i<BOARD_HEIGHT; ++i) {
4525         for (j=0; j<BOARD_WIDTH; ++j) {
4526             if (b1[i][j] != b2[i][j]) {
4527                 if (++c > 4)    /* Castling causes 4 diffs */
4528                   return 1;
4529             }
4530         }
4531     }
4532     return 0;
4533 }
4534
4535 /* Matrix describing castling maneuvers */
4536 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4537 static int castling_matrix[4][5] = {
4538     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4539     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4540     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4541     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4542 };
4543
4544 /* Checks whether castling occurred. If it did, *rrow and *rcol
4545    are set to the destination (row,col) of the rook that moved.
4546
4547    Returns 1 if castling occurred, 0 if not.
4548
4549    Note: Only handles a max of 1 castling move, so be sure
4550    to call too_many_diffs() first.
4551    */
4552 static int check_castle_draw(newb, oldb, rrow, rcol)
4553      Board newb, oldb;
4554      int *rrow, *rcol;
4555 {
4556     int i, *r, j;
4557     int match;
4558
4559     /* For each type of castling... */
4560     for (i=0; i<4; ++i) {
4561         r = castling_matrix[i];
4562
4563         /* Check the 4 squares involved in the castling move */
4564         match = 0;
4565         for (j=1; j<=4; ++j) {
4566             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4567                 match = 1;
4568                 break;
4569             }
4570         }
4571
4572         if (!match) {
4573             /* All 4 changed, so it must be a castling move */
4574             *rrow = r[0];
4575             *rcol = r[3];
4576             return 1;
4577         }
4578     }
4579     return 0;
4580 }
4581
4582 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4583 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4584 {
4585       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4586 }
4587
4588 void DrawSeekBackground( int left, int top, int right, int bottom )
4589 {
4590     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4591 }
4592
4593 void DrawSeekText(char *buf, int x, int y)
4594 {
4595     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4596 }
4597
4598 void DrawSeekDot(int x, int y, int colorNr)
4599 {
4600     int square = colorNr & 0x80;
4601     GC color;
4602     colorNr &= 0x7F;
4603     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4604     if(square)
4605         XFillRectangle(xDisplay, xBoardWindow, color,
4606                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4607     else
4608         XFillArc(xDisplay, xBoardWindow, color,
4609                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4610 }
4611
4612 static int damage[2][BOARD_RANKS][BOARD_FILES];
4613
4614 /*
4615  * event handler for redrawing the board
4616  */
4617 void XDrawPosition(w, repaint, board)
4618      Widget w;
4619      /*Boolean*/int repaint;
4620      Board board;
4621 {
4622     int i, j, do_flash;
4623     static int lastFlipView = 0;
4624     static int lastBoardValid[2] = {0, 0};
4625     static Board lastBoard[2];
4626     Arg args[16];
4627     int rrow, rcol;
4628     int nr = twoBoards*partnerUp;
4629
4630     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4631
4632     if (board == NULL) {
4633         if (!lastBoardValid[nr]) return;
4634         board = lastBoard[nr];
4635     }
4636     if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4637         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4638         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4639                     args, 1);
4640     }
4641
4642     /*
4643      * It would be simpler to clear the window with XClearWindow()
4644      * but this causes a very distracting flicker.
4645      */
4646
4647     if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4648
4649         if ( lineGap && IsDrawArrowEnabled())
4650             XDrawSegments(xDisplay, xBoardWindow, lineGC,
4651                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4652
4653         /* If too much changes (begin observing new game, etc.), don't
4654            do flashing */
4655         do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4656
4657         /* Special check for castling so we don't flash both the king
4658            and the rook (just flash the king). */
4659         if (do_flash) {
4660             if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4661                 /* Draw rook with NO flashing. King will be drawn flashing later */
4662                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4663                 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4664             }
4665         }
4666
4667         /* First pass -- Draw (newly) empty squares and repair damage.
4668            This prevents you from having a piece show up twice while it
4669            is flashing on its new square */
4670         for (i = 0; i < BOARD_HEIGHT; i++)
4671           for (j = 0; j < BOARD_WIDTH; j++)
4672             if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4673                 || damage[nr][i][j]) {
4674                 DrawSquare(i, j, board[i][j], 0);
4675                 damage[nr][i][j] = False;
4676             }
4677
4678         /* Second pass -- Draw piece(s) in new position and flash them */
4679         for (i = 0; i < BOARD_HEIGHT; i++)
4680           for (j = 0; j < BOARD_WIDTH; j++)
4681             if (board[i][j] != lastBoard[nr][i][j]) {
4682                 DrawSquare(i, j, board[i][j], do_flash);
4683             }
4684     } else {
4685         if (lineGap > 0)
4686           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4687                         twoBoards & partnerUp ? secondSegments : // [HGM] dual
4688                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4689
4690         for (i = 0; i < BOARD_HEIGHT; i++)
4691           for (j = 0; j < BOARD_WIDTH; j++) {
4692               DrawSquare(i, j, board[i][j], 0);
4693               damage[nr][i][j] = False;
4694           }
4695     }
4696
4697     CopyBoard(lastBoard[nr], board);
4698     lastBoardValid[nr] = 1;
4699   if(nr == 0) { // [HGM] dual: no highlights on second board yet
4700     lastFlipView = flipView;
4701
4702     /* Draw highlights */
4703     if (pm1X >= 0 && pm1Y >= 0) {
4704       drawHighlight(pm1X, pm1Y, prelineGC);
4705     }
4706     if (pm2X >= 0 && pm2Y >= 0) {
4707       drawHighlight(pm2X, pm2Y, prelineGC);
4708     }
4709     if (hi1X >= 0 && hi1Y >= 0) {
4710       drawHighlight(hi1X, hi1Y, highlineGC);
4711     }
4712     if (hi2X >= 0 && hi2Y >= 0) {
4713       drawHighlight(hi2X, hi2Y, highlineGC);
4714     }
4715     DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4716   }
4717     /* If piece being dragged around board, must redraw that too */
4718     DrawDragPiece();
4719
4720     XSync(xDisplay, False);
4721 }
4722
4723
4724 /*
4725  * event handler for redrawing the board
4726  */
4727 void DrawPositionProc(w, event, prms, nprms)
4728      Widget w;
4729      XEvent *event;
4730      String *prms;
4731      Cardinal *nprms;
4732 {
4733     XDrawPosition(w, True, NULL);
4734 }
4735
4736
4737 /*
4738  * event handler for parsing user moves
4739  */
4740 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4741 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4742 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4743 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
4744 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4745 //       and at the end FinishMove() to perform the move after optional promotion popups.
4746 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4747 void HandleUserMove(w, event, prms, nprms)
4748      Widget w;
4749      XEvent *event;
4750      String *prms;
4751      Cardinal *nprms;
4752 {
4753     if (w != boardWidget || errorExitStatus != -1) return;
4754     if(nprms) shiftKey = !strcmp(prms[0], "1");
4755
4756     if (promotionUp) {
4757         if (event->type == ButtonPress) {
4758             XtPopdown(promotionShell);
4759             XtDestroyWidget(promotionShell);
4760             promotionUp = False;
4761             ClearHighlights();
4762             fromX = fromY = -1;
4763         } else {
4764             return;
4765         }
4766     }
4767
4768     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4769     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
4770     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4771 }
4772
4773 void AnimateUserMove (Widget w, XEvent * event,
4774                       String * params, Cardinal * nParams)
4775 {
4776     if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4777     DragPieceMove(event->xmotion.x, event->xmotion.y);
4778 }
4779
4780 void HandlePV (Widget w, XEvent * event,
4781                       String * params, Cardinal * nParams)
4782 {   // [HGM] pv: walk PV
4783     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4784 }
4785
4786 static int savedIndex;  /* gross that this is global */
4787
4788 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4789 {
4790         String val;
4791         XawTextPosition index, dummy;
4792         Arg arg;
4793
4794         XawTextGetSelectionPos(w, &index, &dummy);
4795         XtSetArg(arg, XtNstring, &val);
4796         XtGetValues(w, &arg, 1);
4797         ReplaceComment(savedIndex, val);
4798         if(savedIndex != currentMove) ToNrEvent(savedIndex);
4799         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4800 }
4801
4802 void EditCommentPopUp(index, title, text)
4803      int index;
4804      char *title, *text;
4805 {
4806     savedIndex = index;
4807     if (text == NULL) text = "";
4808     NewCommentPopup(title, text, index);
4809 }
4810
4811 void ICSInputBoxPopUp()
4812 {
4813     InputBoxPopup();
4814 }
4815
4816 extern Option boxOptions[];
4817
4818 void ICSInputSendText()
4819 {
4820     Widget edit;
4821     int j;
4822     Arg args[16];
4823     String val;
4824
4825     edit = boxOptions[0].handle;
4826     j = 0;
4827     XtSetArg(args[j], XtNstring, &val); j++;
4828     XtGetValues(edit, args, j);
4829     SaveInHistory(val);
4830     SendMultiLineToICS(val);
4831     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4832     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4833 }
4834
4835 void ICSInputBoxPopDown()
4836 {
4837     PopDown(4);
4838 }
4839
4840 void CommentPopUp(title, text)
4841      char *title, *text;
4842 {
4843     savedIndex = currentMove; // [HGM] vari
4844     NewCommentPopup(title, text, currentMove);
4845 }
4846
4847 void CommentPopDown()
4848 {
4849     PopDown(1);
4850 }
4851
4852 void FileNamePopUp(label, def, filter, proc, openMode)
4853      char *label;
4854      char *def;
4855      char *filter;
4856      FileProc proc;
4857      char *openMode;
4858 {
4859     fileProc = proc;            /* I can't see a way not */
4860     fileOpenMode = openMode;    /*   to use globals here */
4861     {   // [HGM] use file-selector dialog stolen from Ghostview
4862         char *name;
4863         int index; // this is not supported yet
4864         FILE *f;
4865         if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4866                            (def[0] ? def : NULL), filter, openMode, NULL, &name))
4867           (void) (*fileProc)(f, index=0, name);
4868     }
4869 }
4870
4871 void FileNamePopDown()
4872 {
4873     if (!filenameUp) return;
4874     XtPopdown(fileNameShell);
4875     XtDestroyWidget(fileNameShell);
4876     filenameUp = False;
4877     ModeHighlight();
4878 }
4879
4880 void FileNameCallback(w, client_data, call_data)
4881      Widget w;
4882      XtPointer client_data, call_data;
4883 {
4884     String name;
4885     Arg args[16];
4886
4887     XtSetArg(args[0], XtNlabel, &name);
4888     XtGetValues(w, args, 1);
4889
4890     if (strcmp(name, _("cancel")) == 0) {
4891         FileNamePopDown();
4892         return;
4893     }
4894
4895     FileNameAction(w, NULL, NULL, NULL);
4896 }
4897
4898 void FileNameAction(w, event, prms, nprms)
4899      Widget w;
4900      XEvent *event;
4901      String *prms;
4902      Cardinal *nprms;
4903 {
4904     char buf[MSG_SIZ];
4905     String name;
4906     FILE *f;
4907     char *p, *fullname;
4908     int index;
4909
4910     name = XawDialogGetValueString(w = XtParent(w));
4911
4912     if ((name != NULL) && (*name != NULLCHAR)) {
4913         safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4914         XtPopdown(w = XtParent(XtParent(w)));
4915         XtDestroyWidget(w);
4916         filenameUp = False;
4917
4918         p = strrchr(buf, ' ');
4919         if (p == NULL) {
4920             index = 0;
4921         } else {
4922             *p++ = NULLCHAR;
4923             index = atoi(p);
4924         }
4925         fullname = ExpandPathName(buf);
4926         if (!fullname) {
4927             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4928         }
4929         else {
4930             f = fopen(fullname, fileOpenMode);
4931             if (f == NULL) {
4932                 DisplayError(_("Failed to open file"), errno);
4933             } else {
4934                 (void) (*fileProc)(f, index, buf);
4935             }
4936         }
4937         ModeHighlight();
4938         return;
4939     }
4940
4941     XtPopdown(w = XtParent(XtParent(w)));
4942     XtDestroyWidget(w);
4943     filenameUp = False;
4944     ModeHighlight();
4945 }
4946
4947 void PromotionPopUp()
4948 {
4949     Arg args[16];
4950     Widget dialog, layout;
4951     Position x, y;
4952     Dimension bw_width, pw_width;
4953     int j;
4954
4955     j = 0;
4956     XtSetArg(args[j], XtNwidth, &bw_width); j++;
4957     XtGetValues(boardWidget, args, j);
4958
4959     j = 0;
4960     XtSetArg(args[j], XtNresizable, True); j++;
4961     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4962     promotionShell =
4963       XtCreatePopupShell("Promotion", transientShellWidgetClass,
4964                          shellWidget, args, j);
4965     layout =
4966       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4967                             layoutArgs, XtNumber(layoutArgs));
4968
4969     j = 0;
4970     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4971     XtSetArg(args[j], XtNborderWidth, 0); j++;
4972     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4973                                    layout, args, j);
4974
4975   if(gameInfo.variant != VariantShogi) {
4976    if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4977       XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
4978                          (XtPointer) dialog);
4979       XawDialogAddButton(dialog, _("General"), PromotionCallback,
4980                          (XtPointer) dialog);
4981       XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
4982                          (XtPointer) dialog);
4983       XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
4984                          (XtPointer) dialog);
4985     } else {\r
4986     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
4987                        (XtPointer) dialog);
4988     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
4989                        (XtPointer) dialog);
4990     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
4991                        (XtPointer) dialog);
4992     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
4993                        (XtPointer) dialog);
4994     }
4995     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
4996         gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
4997         gameInfo.variant == VariantGiveaway) {
4998       XawDialogAddButton(dialog, _("King"), PromotionCallback,
4999                          (XtPointer) dialog);
5000     }
5001     if(gameInfo.variant == VariantCapablanca ||
5002        gameInfo.variant == VariantGothic ||
5003        gameInfo.variant == VariantCapaRandom) {
5004       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5005                          (XtPointer) dialog);
5006       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5007                          (XtPointer) dialog);
5008     }
5009   } else // [HGM] shogi
5010   {
5011       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5012                          (XtPointer) dialog);
5013       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5014                          (XtPointer) dialog);
5015   }
5016     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5017                        (XtPointer) dialog);
5018
5019     XtRealizeWidget(promotionShell);
5020     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5021
5022     j = 0;
5023     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5024     XtGetValues(promotionShell, args, j);
5025
5026     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5027                       lineGap + squareSize/3 +
5028                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5029                        0 : 6*(squareSize + lineGap)), &x, &y);
5030
5031     j = 0;
5032     XtSetArg(args[j], XtNx, x); j++;
5033     XtSetArg(args[j], XtNy, y); j++;
5034     XtSetValues(promotionShell, args, j);
5035
5036     XtPopup(promotionShell, XtGrabNone);
5037
5038     promotionUp = True;
5039 }
5040
5041 void PromotionPopDown()
5042 {
5043     if (!promotionUp) return;
5044     XtPopdown(promotionShell);
5045     XtDestroyWidget(promotionShell);
5046     promotionUp = False;
5047 }
5048
5049 void PromotionCallback(w, client_data, call_data)
5050      Widget w;
5051      XtPointer client_data, call_data;
5052 {
5053     String name;
5054     Arg args[16];
5055     int promoChar;
5056
5057     XtSetArg(args[0], XtNlabel, &name);
5058     XtGetValues(w, args, 1);
5059
5060     PromotionPopDown();
5061
5062     if (fromX == -1) return;
5063
5064     if (strcmp(name, _("cancel")) == 0) {
5065         fromX = fromY = -1;
5066         ClearHighlights();
5067         return;
5068     } else if (strcmp(name, _("Knight")) == 0) {
5069         promoChar = 'n';
5070     } else if (strcmp(name, _("Promote")) == 0) {
5071         promoChar = '+';
5072     } else if (strcmp(name, _("Defer")) == 0) {
5073         promoChar = '=';
5074     } else {
5075         promoChar = ToLower(name[0]);
5076     }
5077
5078     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5079
5080     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5081     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5082     fromX = fromY = -1;
5083 }
5084
5085
5086 void ErrorCallback(w, client_data, call_data)
5087      Widget w;
5088      XtPointer client_data, call_data;
5089 {
5090     errorUp = False;
5091     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5092     XtDestroyWidget(w);
5093     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5094 }
5095
5096
5097 void ErrorPopDown()
5098 {
5099     if (!errorUp) return;
5100     errorUp = False;
5101     XtPopdown(errorShell);
5102     XtDestroyWidget(errorShell);
5103     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5104 }
5105
5106 void ErrorPopUp(title, label, modal)
5107      char *title, *label;
5108      int modal;
5109 {
5110     Arg args[16];
5111     Widget dialog, layout;
5112     Position x, y;
5113     int xx, yy;
5114     Window junk;
5115     Dimension bw_width, pw_width;
5116     Dimension pw_height;
5117     int i;
5118
5119     i = 0;
5120     XtSetArg(args[i], XtNresizable, True);  i++;
5121     XtSetArg(args[i], XtNtitle, title); i++;
5122     errorShell =
5123       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5124                          shellWidget, args, i);
5125     layout =
5126       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5127                             layoutArgs, XtNumber(layoutArgs));
5128
5129     i = 0;
5130     XtSetArg(args[i], XtNlabel, label); i++;
5131     XtSetArg(args[i], XtNborderWidth, 0); i++;
5132     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5133                                    layout, args, i);
5134
5135     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5136
5137     XtRealizeWidget(errorShell);
5138     CatchDeleteWindow(errorShell, "ErrorPopDown");
5139
5140     i = 0;
5141     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5142     XtGetValues(boardWidget, args, i);
5143     i = 0;
5144     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5145     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5146     XtGetValues(errorShell, args, i);
5147
5148 #ifdef NOTDEF
5149     /* This code seems to tickle an X bug if it is executed too soon
5150        after xboard starts up.  The coordinates get transformed as if
5151        the main window was positioned at (0, 0).
5152        */
5153     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5154                       0 - pw_height + squareSize / 3, &x, &y);
5155 #else
5156     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5157                           RootWindowOfScreen(XtScreen(boardWidget)),
5158                           (bw_width - pw_width) / 2,
5159                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5160     x = xx;
5161     y = yy;
5162 #endif
5163     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5164
5165     i = 0;
5166     XtSetArg(args[i], XtNx, x);  i++;
5167     XtSetArg(args[i], XtNy, y);  i++;
5168     XtSetValues(errorShell, args, i);
5169
5170     errorUp = True;
5171     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5172 }
5173
5174 /* Disable all user input other than deleting the window */
5175 static int frozen = 0;
5176 void FreezeUI()
5177 {
5178   if (frozen) return;
5179   /* Grab by a widget that doesn't accept input */
5180   XtAddGrab(messageWidget, TRUE, FALSE);
5181   frozen = 1;
5182 }
5183
5184 /* Undo a FreezeUI */
5185 void ThawUI()
5186 {
5187   if (!frozen) return;
5188   XtRemoveGrab(messageWidget);
5189   frozen = 0;
5190 }
5191
5192 char *ModeToWidgetName(mode)
5193      GameMode mode;
5194 {
5195     switch (mode) {
5196       case BeginningOfGame:
5197         if (appData.icsActive)
5198           return "menuMode.ICS Client";
5199         else if (appData.noChessProgram ||
5200                  *appData.cmailGameName != NULLCHAR)
5201           return "menuMode.Edit Game";
5202         else
5203           return "menuMode.Machine Black";
5204       case MachinePlaysBlack:
5205         return "menuMode.Machine Black";
5206       case MachinePlaysWhite:
5207         return "menuMode.Machine White";
5208       case AnalyzeMode:
5209         return "menuMode.Analysis Mode";
5210       case AnalyzeFile:
5211         return "menuMode.Analyze File";
5212       case TwoMachinesPlay:
5213         return "menuMode.Two Machines";
5214       case EditGame:
5215         return "menuMode.Edit Game";
5216       case PlayFromGameFile:
5217         return "menuFile.Load Game";
5218       case EditPosition:
5219         return "menuMode.Edit Position";
5220       case Training:
5221         return "menuMode.Training";
5222       case IcsPlayingWhite:
5223       case IcsPlayingBlack:
5224       case IcsObserving:
5225       case IcsIdle:
5226       case IcsExamining:
5227         return "menuMode.ICS Client";
5228       default:
5229       case EndOfGame:
5230         return NULL;
5231     }
5232 }
5233
5234 void ModeHighlight()
5235 {
5236     Arg args[16];
5237     static int oldPausing = FALSE;
5238     static GameMode oldmode = (GameMode) -1;
5239     char *wname;
5240
5241     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5242
5243     if (pausing != oldPausing) {
5244         oldPausing = pausing;
5245         if (pausing) {
5246             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5247         } else {
5248             XtSetArg(args[0], XtNleftBitmap, None);
5249         }
5250         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5251                     args, 1);
5252
5253         if (appData.showButtonBar) {
5254           /* Always toggle, don't set.  Previous code messes up when
5255              invoked while the button is pressed, as releasing it
5256              toggles the state again. */
5257           {
5258             Pixel oldbg, oldfg;
5259             XtSetArg(args[0], XtNbackground, &oldbg);
5260             XtSetArg(args[1], XtNforeground, &oldfg);
5261             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5262                         args, 2);
5263             XtSetArg(args[0], XtNbackground, oldfg);
5264             XtSetArg(args[1], XtNforeground, oldbg);
5265           }
5266           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5267         }
5268     }
5269
5270     wname = ModeToWidgetName(oldmode);
5271     if (wname != NULL) {
5272         XtSetArg(args[0], XtNleftBitmap, None);
5273         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5274     }
5275     wname = ModeToWidgetName(gameMode);
5276     if (wname != NULL) {
5277         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5278         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5279     }
5280     oldmode = gameMode;
5281
5282     /* Maybe all the enables should be handled here, not just this one */
5283     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5284                    gameMode == Training || gameMode == PlayFromGameFile);
5285 }
5286
5287
5288 /*
5289  * Button/menu procedures
5290  */
5291 void ResetProc(w, event, prms, nprms)
5292      Widget w;
5293      XEvent *event;
5294      String *prms;
5295      Cardinal *nprms;
5296 {
5297     ResetGameEvent();
5298 }
5299
5300 int LoadGamePopUp(f, gameNumber, title)
5301      FILE *f;
5302      int gameNumber;
5303      char *title;
5304 {
5305     cmailMsgLoaded = FALSE;
5306     if (gameNumber == 0) {
5307         int error = GameListBuild(f);
5308         if (error) {
5309             DisplayError(_("Cannot build game list"), error);
5310         } else if (!ListEmpty(&gameList) &&
5311                    ((ListGame *) gameList.tailPred)->number > 1) {
5312             GameListPopUp(f, title);
5313             return TRUE;
5314         }
5315         GameListDestroy();
5316         gameNumber = 1;
5317     }
5318     return LoadGame(f, gameNumber, title, FALSE);
5319 }
5320
5321 void LoadGameProc(w, event, prms, nprms)
5322      Widget w;
5323      XEvent *event;
5324      String *prms;
5325      Cardinal *nprms;
5326 {
5327     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5328         Reset(FALSE, TRUE);
5329     }
5330     FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5331 }
5332
5333 void LoadNextGameProc(w, event, prms, nprms)
5334      Widget w;
5335      XEvent *event;
5336      String *prms;
5337      Cardinal *nprms;
5338 {
5339     ReloadGame(1);
5340 }
5341
5342 void LoadPrevGameProc(w, event, prms, nprms)
5343      Widget w;
5344      XEvent *event;
5345      String *prms;
5346      Cardinal *nprms;
5347 {
5348     ReloadGame(-1);
5349 }
5350
5351 void ReloadGameProc(w, event, prms, nprms)
5352      Widget w;
5353      XEvent *event;
5354      String *prms;
5355      Cardinal *nprms;
5356 {
5357     ReloadGame(0);
5358 }
5359
5360 void LoadNextPositionProc(w, event, prms, nprms)
5361      Widget w;
5362      XEvent *event;
5363      String *prms;
5364      Cardinal *nprms;
5365 {
5366     ReloadPosition(1);
5367 }
5368
5369 void LoadPrevPositionProc(w, event, prms, nprms)
5370      Widget w;
5371      XEvent *event;
5372      String *prms;
5373      Cardinal *nprms;
5374 {
5375     ReloadPosition(-1);
5376 }
5377
5378 void ReloadPositionProc(w, event, prms, nprms)
5379      Widget w;
5380      XEvent *event;
5381      String *prms;
5382      Cardinal *nprms;
5383 {
5384     ReloadPosition(0);
5385 }
5386
5387 void LoadPositionProc(w, event, prms, nprms)
5388      Widget w;
5389      XEvent *event;
5390      String *prms;
5391      Cardinal *nprms;
5392 {
5393     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5394         Reset(FALSE, TRUE);
5395     }
5396     FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5397 }
5398
5399 void SaveGameProc(w, event, prms, nprms)
5400      Widget w;
5401      XEvent *event;
5402      String *prms;
5403      Cardinal *nprms;
5404 {
5405     FileNamePopUp(_("Save game file name?"),
5406                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5407                   appData.oldSaveStyle ? ".game" : ".pgn",
5408                   SaveGame, "a");
5409 }
5410
5411 void SavePositionProc(w, event, prms, nprms)
5412      Widget w;
5413      XEvent *event;
5414      String *prms;
5415      Cardinal *nprms;
5416 {
5417     FileNamePopUp(_("Save position file name?"),
5418                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5419                   appData.oldSaveStyle ? ".pos" : ".fen",
5420                   SavePosition, "a");
5421 }
5422
5423 void ReloadCmailMsgProc(w, event, prms, nprms)
5424      Widget w;
5425      XEvent *event;
5426      String *prms;
5427      Cardinal *nprms;
5428 {
5429     ReloadCmailMsgEvent(FALSE);
5430 }
5431
5432 void MailMoveProc(w, event, prms, nprms)
5433      Widget w;
5434      XEvent *event;
5435      String *prms;
5436      Cardinal *nprms;
5437 {
5438     MailMoveEvent();
5439 }
5440
5441 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5442 char *selected_fen_position=NULL;
5443
5444 Boolean
5445 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5446                  Atom *type_return, XtPointer *value_return,
5447                  unsigned long *length_return, int *format_return)
5448 {
5449   char *selection_tmp;
5450
5451   if (!selected_fen_position) return False; /* should never happen */
5452   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5453     /* note: since no XtSelectionDoneProc was registered, Xt will
5454      * automatically call XtFree on the value returned.  So have to
5455      * make a copy of it allocated with XtMalloc */
5456     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5457     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5458
5459     *value_return=selection_tmp;
5460     *length_return=strlen(selection_tmp);
5461     *type_return=*target;
5462     *format_return = 8; /* bits per byte */
5463     return True;
5464   } else if (*target == XA_TARGETS(xDisplay)) {
5465     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5466     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5467     targets_tmp[1] = XA_STRING;
5468     *value_return = targets_tmp;
5469     *type_return = XA_ATOM;
5470     *length_return = 2;
5471     *format_return = 8 * sizeof(Atom);
5472     if (*format_return > 32) {
5473       *length_return *= *format_return / 32;
5474       *format_return = 32;
5475     }
5476     return True;
5477   } else {
5478     return False;
5479   }
5480 }
5481
5482 /* note: when called from menu all parameters are NULL, so no clue what the
5483  * Widget which was clicked on was, or what the click event was
5484  */
5485 void CopyPositionProc(w, event, prms, nprms)
5486   Widget w;
5487   XEvent *event;
5488   String *prms;
5489   Cardinal *nprms;
5490   {
5491     /*
5492      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5493      * have a notion of a position that is selected but not copied.
5494      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5495      */
5496     if(gameMode == EditPosition) EditPositionDone(TRUE);
5497     if (selected_fen_position) free(selected_fen_position);
5498     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5499     if (!selected_fen_position) return;
5500     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5501                    CurrentTime,
5502                    SendPositionSelection,
5503                    NULL/* lose_ownership_proc */ ,
5504                    NULL/* transfer_done_proc */);
5505     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5506                    CurrentTime,
5507                    SendPositionSelection,
5508                    NULL/* lose_ownership_proc */ ,
5509                    NULL/* transfer_done_proc */);
5510   }
5511
5512 /* function called when the data to Paste is ready */
5513 static void
5514 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5515            Atom *type, XtPointer value, unsigned long *len, int *format)
5516 {
5517   char *fenstr=value;
5518   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5519   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5520   EditPositionPasteFEN(fenstr);
5521   XtFree(value);
5522 }
5523
5524 /* called when Paste Position button is pressed,
5525  * all parameters will be NULL */
5526 void PastePositionProc(w, event, prms, nprms)
5527   Widget w;
5528   XEvent *event;
5529   String *prms;
5530   Cardinal *nprms;
5531 {
5532     XtGetSelectionValue(menuBarWidget,
5533       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5534       /* (XtSelectionCallbackProc) */ PastePositionCB,
5535       NULL, /* client_data passed to PastePositionCB */
5536
5537       /* better to use the time field from the event that triggered the
5538        * call to this function, but that isn't trivial to get
5539        */
5540       CurrentTime
5541     );
5542     return;
5543 }
5544
5545 static Boolean
5546 SendGameSelection(Widget w, Atom *selection, Atom *target,
5547                   Atom *type_return, XtPointer *value_return,
5548                   unsigned long *length_return, int *format_return)
5549 {
5550   char *selection_tmp;
5551
5552   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5553     FILE* f = fopen(gameCopyFilename, "r");
5554     long len;
5555     size_t count;
5556     if (f == NULL) return False;
5557     fseek(f, 0, 2);
5558     len = ftell(f);
5559     rewind(f);
5560     selection_tmp = XtMalloc(len + 1);
5561     count = fread(selection_tmp, 1, len, f);
5562     fclose(f);
5563     if (len != count) {
5564       XtFree(selection_tmp);
5565       return False;
5566     }
5567     selection_tmp[len] = NULLCHAR;
5568     *value_return = selection_tmp;
5569     *length_return = len;
5570     *type_return = *target;
5571     *format_return = 8; /* bits per byte */
5572     return True;
5573   } else if (*target == XA_TARGETS(xDisplay)) {
5574     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5575     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5576     targets_tmp[1] = XA_STRING;
5577     *value_return = targets_tmp;
5578     *type_return = XA_ATOM;
5579     *length_return = 2;
5580     *format_return = 8 * sizeof(Atom);
5581     if (*format_return > 32) {
5582       *length_return *= *format_return / 32;
5583       *format_return = 32;
5584     }
5585     return True;
5586   } else {
5587     return False;
5588   }
5589 }
5590
5591 void CopySomething()
5592 {
5593   int ret;
5594
5595   /*
5596    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5597    * have a notion of a game that is selected but not copied.
5598    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5599    */
5600   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5601                  CurrentTime,
5602                  SendGameSelection,
5603                  NULL/* lose_ownership_proc */ ,
5604                  NULL/* transfer_done_proc */);
5605   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5606                  CurrentTime,
5607                  SendGameSelection,
5608                  NULL/* lose_ownership_proc */ ,
5609                  NULL/* transfer_done_proc */);
5610 }
5611
5612 /* note: when called from menu all parameters are NULL, so no clue what the
5613  * Widget which was clicked on was, or what the click event was
5614  */
5615 void CopyGameProc(w, event, prms, nprms)
5616   Widget w;
5617   XEvent *event;
5618   String *prms;
5619   Cardinal *nprms;
5620 {
5621   int ret;
5622
5623   ret = SaveGameToFile(gameCopyFilename, FALSE);
5624   if (!ret) return;
5625
5626   CopySomething();
5627 }
5628
5629 void CopyGameListProc(w, event, prms, nprms)
5630   Widget w;
5631   XEvent *event;
5632   String *prms;
5633   Cardinal *nprms;
5634 {
5635   if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5636   CopySomething();
5637 }
5638
5639 /* function called when the data to Paste is ready */
5640 static void
5641 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5642             Atom *type, XtPointer value, unsigned long *len, int *format)
5643 {
5644   FILE* f;
5645   if (value == NULL || *len == 0) {
5646     return; /* nothing had been selected to copy */
5647   }
5648   f = fopen(gamePasteFilename, "w");
5649   if (f == NULL) {
5650     DisplayError(_("Can't open temp file"), errno);
5651     return;
5652   }
5653   fwrite(value, 1, *len, f);
5654   fclose(f);
5655   XtFree(value);
5656   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5657 }
5658
5659 /* called when Paste Game button is pressed,
5660  * all parameters will be NULL */
5661 void PasteGameProc(w, event, prms, nprms)
5662   Widget w;
5663   XEvent *event;
5664   String *prms;
5665   Cardinal *nprms;
5666 {
5667     XtGetSelectionValue(menuBarWidget,
5668       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5669       /* (XtSelectionCallbackProc) */ PasteGameCB,
5670       NULL, /* client_data passed to PasteGameCB */
5671
5672       /* better to use the time field from the event that triggered the
5673        * call to this function, but that isn't trivial to get
5674        */
5675       CurrentTime
5676     );
5677     return;
5678 }
5679
5680
5681 void AutoSaveGame()
5682 {
5683     SaveGameProc(NULL, NULL, NULL, NULL);
5684 }
5685
5686
5687 void QuitProc(w, event, prms, nprms)
5688      Widget w;
5689      XEvent *event;
5690      String *prms;
5691      Cardinal *nprms;
5692 {
5693     ExitEvent(0);
5694 }
5695
5696 void PauseProc(w, event, prms, nprms)
5697      Widget w;
5698      XEvent *event;
5699      String *prms;
5700      Cardinal *nprms;
5701 {
5702     PauseEvent();
5703 }
5704
5705
5706 void MachineBlackProc(w, event, prms, nprms)
5707      Widget w;
5708      XEvent *event;
5709      String *prms;
5710      Cardinal *nprms;
5711 {
5712     MachineBlackEvent();
5713 }
5714
5715 void MachineWhiteProc(w, event, prms, nprms)
5716      Widget w;
5717      XEvent *event;
5718      String *prms;
5719      Cardinal *nprms;
5720 {
5721     MachineWhiteEvent();
5722 }
5723
5724 void AnalyzeModeProc(w, event, prms, nprms)
5725      Widget w;
5726      XEvent *event;
5727      String *prms;
5728      Cardinal *nprms;
5729 {
5730     char buf[MSG_SIZ];
5731
5732     if (!first.analysisSupport) {
5733       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5734       DisplayError(buf, 0);
5735       return;
5736     }
5737     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5738     if (appData.icsActive) {
5739         if (gameMode != IcsObserving) {
5740           snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5741             DisplayError(buf, 0);
5742             /* secure check */
5743             if (appData.icsEngineAnalyze) {
5744                 if (appData.debugMode)
5745                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5746                 ExitAnalyzeMode();
5747                 ModeHighlight();
5748             }
5749             return;
5750         }
5751         /* if enable, use want disable icsEngineAnalyze */
5752         if (appData.icsEngineAnalyze) {
5753                 ExitAnalyzeMode();
5754                 ModeHighlight();
5755                 return;
5756         }
5757         appData.icsEngineAnalyze = TRUE;
5758         if (appData.debugMode)
5759             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5760     }
5761 #ifndef OPTIONSDIALOG
5762     if (!appData.showThinking)
5763       ShowThinkingProc(w,event,prms,nprms);
5764 #endif
5765
5766     AnalyzeModeEvent();
5767 }
5768
5769 void AnalyzeFileProc(w, event, prms, nprms)
5770      Widget w;
5771      XEvent *event;
5772      String *prms;
5773      Cardinal *nprms;
5774 {
5775     if (!first.analysisSupport) {
5776       char buf[MSG_SIZ];
5777       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5778       DisplayError(buf, 0);
5779       return;
5780     }
5781     Reset(FALSE, TRUE);
5782 #ifndef OPTIONSDIALOG
5783     if (!appData.showThinking)
5784       ShowThinkingProc(w,event,prms,nprms);
5785 #endif
5786     AnalyzeFileEvent();
5787     FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5788     AnalysisPeriodicEvent(1);
5789 }
5790
5791 void TwoMachinesProc(w, event, prms, nprms)
5792      Widget w;
5793      XEvent *event;
5794      String *prms;
5795      Cardinal *nprms;
5796 {
5797     TwoMachinesEvent();
5798 }
5799
5800 void MatchProc(w, event, prms, nprms)
5801      Widget w;
5802      XEvent *event;
5803      String *prms;
5804      Cardinal *nprms;
5805 {
5806     if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
5807     appData.matchGames = appData.defaultMatchGames;
5808     MatchEvent(2);\r
5809 }
5810
5811 void IcsClientProc(w, event, prms, nprms)
5812      Widget w;
5813      XEvent *event;
5814      String *prms;
5815      Cardinal *nprms;
5816 {
5817     IcsClientEvent();
5818 }
5819
5820 void EditGameProc(w, event, prms, nprms)
5821      Widget w;
5822      XEvent *event;
5823      String *prms;
5824      Cardinal *nprms;
5825 {
5826     EditGameEvent();
5827 }
5828
5829 void EditPositionProc(w, event, prms, nprms)
5830      Widget w;
5831      XEvent *event;
5832      String *prms;
5833      Cardinal *nprms;
5834 {
5835     EditPositionEvent();
5836 }
5837
5838 void TrainingProc(w, event, prms, nprms)
5839      Widget w;
5840      XEvent *event;
5841      String *prms;
5842      Cardinal *nprms;
5843 {
5844     TrainingEvent();
5845 }
5846
5847 void EditCommentProc(w, event, prms, nprms)
5848      Widget w;
5849      XEvent *event;
5850      String *prms;
5851      Cardinal *nprms;
5852 {
5853     Arg args[5];
5854     int j;
5855     if (PopDown(1)) { // popdown succesful
5856         j = 0;
5857         XtSetArg(args[j], XtNleftBitmap, None); j++;
5858         XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5859         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5860     } else // was not up
5861         EditCommentEvent();
5862 }
5863
5864 void IcsInputBoxProc(w, event, prms, nprms)
5865      Widget w;
5866      XEvent *event;
5867      String *prms;
5868      Cardinal *nprms;
5869 {
5870     if (!PopDown(4)) ICSInputBoxPopUp();
5871 }
5872
5873 void AcceptProc(w, event, prms, nprms)
5874      Widget w;
5875      XEvent *event;
5876      String *prms;
5877      Cardinal *nprms;
5878 {
5879     AcceptEvent();
5880 }
5881
5882 void DeclineProc(w, event, prms, nprms)
5883      Widget w;
5884      XEvent *event;
5885      String *prms;
5886      Cardinal *nprms;
5887 {
5888     DeclineEvent();
5889 }
5890
5891 void RematchProc(w, event, prms, nprms)
5892      Widget w;
5893      XEvent *event;
5894      String *prms;
5895      Cardinal *nprms;
5896 {
5897     RematchEvent();
5898 }
5899
5900 void CallFlagProc(w, event, prms, nprms)
5901      Widget w;
5902      XEvent *event;
5903      String *prms;
5904      Cardinal *nprms;
5905 {
5906     CallFlagEvent();
5907 }
5908
5909 void DrawProc(w, event, prms, nprms)
5910      Widget w;
5911      XEvent *event;
5912      String *prms;
5913      Cardinal *nprms;
5914 {
5915     DrawEvent();
5916 }
5917
5918 void AbortProc(w, event, prms, nprms)
5919      Widget w;
5920      XEvent *event;
5921      String *prms;
5922      Cardinal *nprms;
5923 {
5924     AbortEvent();
5925 }
5926
5927 void AdjournProc(w, event, prms, nprms)
5928      Widget w;
5929      XEvent *event;
5930      String *prms;
5931      Cardinal *nprms;
5932 {
5933     AdjournEvent();
5934 }
5935
5936 void ResignProc(w, event, prms, nprms)
5937      Widget w;
5938      XEvent *event;
5939      String *prms;
5940      Cardinal *nprms;
5941 {
5942     ResignEvent();
5943 }
5944
5945 void AdjuWhiteProc(w, event, prms, nprms)
5946      Widget w;
5947      XEvent *event;
5948      String *prms;
5949      Cardinal *nprms;
5950 {
5951     UserAdjudicationEvent(+1);
5952 }
5953
5954 void AdjuBlackProc(w, event, prms, nprms)
5955      Widget w;
5956      XEvent *event;
5957      String *prms;
5958      Cardinal *nprms;
5959 {
5960     UserAdjudicationEvent(-1);
5961 }
5962
5963 void AdjuDrawProc(w, event, prms, nprms)
5964      Widget w;
5965      XEvent *event;
5966      String *prms;
5967      Cardinal *nprms;
5968 {
5969     UserAdjudicationEvent(0);
5970 }
5971
5972 void EnterKeyProc(w, event, prms, nprms)
5973      Widget w;
5974      XEvent *event;
5975      String *prms;
5976      Cardinal *nprms;
5977 {
5978     if (shellUp[4] == True)
5979       ICSInputSendText();
5980 }
5981
5982 void UpKeyProc(w, event, prms, nprms)
5983      Widget w;
5984      XEvent *event;
5985      String *prms;
5986      Cardinal *nprms;
5987 {   // [HGM] input: let up-arrow recall previous line from history
5988     Widget edit;
5989     int j;
5990     Arg args[16];
5991     String val;
5992     XawTextBlock t;
5993
5994     if (!shellUp[4]) return;
5995     edit = boxOptions[0].handle;
5996     j = 0;
5997     XtSetArg(args[j], XtNstring, &val); j++;
5998     XtGetValues(edit, args, j);
5999     val = PrevInHistory(val);
6000     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6001     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6002     if(val) {
6003         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6004         XawTextReplace(edit, 0, 0, &t);
6005         XawTextSetInsertionPoint(edit, 9999);
6006     }
6007 }
6008
6009 void DownKeyProc(w, event, prms, nprms)
6010      Widget w;
6011      XEvent *event;
6012      String *prms;
6013      Cardinal *nprms;
6014 {   // [HGM] input: let down-arrow recall next line from history
6015     Widget edit;
6016     String val;
6017     XawTextBlock t;
6018
6019     if (!shellUp[4]) return;
6020     edit = boxOptions[0].handle;
6021     val = NextInHistory();
6022     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6023     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6024     if(val) {
6025         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6026         XawTextReplace(edit, 0, 0, &t);
6027         XawTextSetInsertionPoint(edit, 9999);
6028     }
6029 }
6030
6031 void StopObservingProc(w, event, prms, nprms)
6032      Widget w;
6033      XEvent *event;
6034      String *prms;
6035      Cardinal *nprms;
6036 {
6037     StopObservingEvent();
6038 }
6039
6040 void StopExaminingProc(w, event, prms, nprms)
6041      Widget w;
6042      XEvent *event;
6043      String *prms;
6044      Cardinal *nprms;
6045 {
6046     StopExaminingEvent();
6047 }
6048
6049 void UploadProc(w, event, prms, nprms)
6050      Widget w;
6051      XEvent *event;
6052      String *prms;
6053      Cardinal *nprms;
6054 {
6055     UploadGameEvent();
6056 }
6057
6058
6059 void ForwardProc(w, event, prms, nprms)
6060      Widget w;
6061      XEvent *event;
6062      String *prms;
6063      Cardinal *nprms;
6064 {
6065     ForwardEvent();
6066 }
6067
6068
6069 void BackwardProc(w, event, prms, nprms)
6070      Widget w;
6071      XEvent *event;
6072      String *prms;
6073      Cardinal *nprms;
6074 {
6075     BackwardEvent();
6076 }
6077
6078 void ToStartProc(w, event, prms, nprms)
6079      Widget w;
6080      XEvent *event;
6081      String *prms;
6082      Cardinal *nprms;
6083 {
6084     ToStartEvent();
6085 }
6086
6087 void ToEndProc(w, event, prms, nprms)
6088      Widget w;
6089      XEvent *event;
6090      String *prms;
6091      Cardinal *nprms;
6092 {
6093     ToEndEvent();
6094 }
6095
6096 void RevertProc(w, event, prms, nprms)
6097      Widget w;
6098      XEvent *event;
6099      String *prms;
6100      Cardinal *nprms;
6101 {
6102     RevertEvent(False);
6103 }
6104
6105 void AnnotateProc(w, event, prms, nprms)
6106      Widget w;
6107      XEvent *event;
6108      String *prms;
6109      Cardinal *nprms;
6110 {
6111     RevertEvent(True);
6112 }
6113
6114 void TruncateGameProc(w, event, prms, nprms)
6115      Widget w;
6116      XEvent *event;
6117      String *prms;
6118      Cardinal *nprms;
6119 {
6120     TruncateGameEvent();
6121 }
6122 void RetractMoveProc(w, event, prms, nprms)
6123      Widget w;
6124      XEvent *event;
6125      String *prms;
6126      Cardinal *nprms;
6127 {
6128     RetractMoveEvent();
6129 }
6130
6131 void MoveNowProc(w, event, prms, nprms)
6132      Widget w;
6133      XEvent *event;
6134      String *prms;
6135      Cardinal *nprms;
6136 {
6137     MoveNowEvent();
6138 }
6139
6140 void FlipViewProc(w, event, prms, nprms)
6141      Widget w;
6142      XEvent *event;
6143      String *prms;
6144      Cardinal *nprms;
6145 {
6146     flipView = !flipView;
6147     DrawPosition(True, NULL);
6148 }
6149
6150 void PonderNextMoveProc(w, event, prms, nprms)
6151      Widget w;
6152      XEvent *event;
6153      String *prms;
6154      Cardinal *nprms;
6155 {
6156     Arg args[16];
6157
6158     PonderNextMoveEvent(!appData.ponderNextMove);
6159 #ifndef OPTIONSDIALOG
6160     if (appData.ponderNextMove) {
6161         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6162     } else {
6163         XtSetArg(args[0], XtNleftBitmap, None);
6164     }
6165     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6166                 args, 1);
6167 #endif
6168 }
6169
6170 #ifndef OPTIONSDIALOG
6171 void AlwaysQueenProc(w, event, prms, nprms)
6172      Widget w;
6173      XEvent *event;
6174      String *prms;
6175      Cardinal *nprms;
6176 {
6177     Arg args[16];
6178
6179     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6180
6181     if (appData.alwaysPromoteToQueen) {
6182         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6183     } else {
6184         XtSetArg(args[0], XtNleftBitmap, None);
6185     }
6186     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6187                 args, 1);
6188 }
6189
6190 void AnimateDraggingProc(w, event, prms, nprms)
6191      Widget w;
6192      XEvent *event;
6193      String *prms;
6194      Cardinal *nprms;
6195 {
6196     Arg args[16];
6197
6198     appData.animateDragging = !appData.animateDragging;
6199
6200     if (appData.animateDragging) {
6201         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6202         CreateAnimVars();
6203     } else {
6204         XtSetArg(args[0], XtNleftBitmap, None);
6205     }
6206     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6207                 args, 1);
6208 }
6209
6210 void AnimateMovingProc(w, event, prms, nprms)
6211      Widget w;
6212      XEvent *event;
6213      String *prms;
6214      Cardinal *nprms;
6215 {
6216     Arg args[16];
6217
6218     appData.animate = !appData.animate;
6219
6220     if (appData.animate) {
6221         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6222         CreateAnimVars();
6223     } else {
6224         XtSetArg(args[0], XtNleftBitmap, None);
6225     }
6226     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6227                 args, 1);
6228 }
6229
6230 void AutoflagProc(w, event, prms, nprms)
6231      Widget w;
6232      XEvent *event;
6233      String *prms;
6234      Cardinal *nprms;
6235 {
6236     Arg args[16];
6237
6238     appData.autoCallFlag = !appData.autoCallFlag;
6239
6240     if (appData.autoCallFlag) {
6241         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6242     } else {
6243         XtSetArg(args[0], XtNleftBitmap, None);
6244     }
6245     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6246                 args, 1);
6247 }
6248
6249 void AutoflipProc(w, event, prms, nprms)
6250      Widget w;
6251      XEvent *event;
6252      String *prms;
6253      Cardinal *nprms;
6254 {
6255     Arg args[16];
6256
6257     appData.autoFlipView = !appData.autoFlipView;
6258
6259     if (appData.autoFlipView) {
6260         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6261     } else {
6262         XtSetArg(args[0], XtNleftBitmap, None);
6263     }
6264     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6265                 args, 1);
6266 }
6267
6268 void BlindfoldProc(w, event, prms, nprms)
6269      Widget w;
6270      XEvent *event;
6271      String *prms;
6272      Cardinal *nprms;
6273 {
6274     Arg args[16];
6275
6276     appData.blindfold = !appData.blindfold;
6277
6278     if (appData.blindfold) {
6279         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6280     } else {
6281         XtSetArg(args[0], XtNleftBitmap, None);
6282     }
6283     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6284                 args, 1);
6285
6286     DrawPosition(True, NULL);
6287 }
6288
6289 void TestLegalityProc(w, event, prms, nprms)
6290      Widget w;
6291      XEvent *event;
6292      String *prms;
6293      Cardinal *nprms;
6294 {
6295     Arg args[16];
6296
6297     appData.testLegality = !appData.testLegality;
6298
6299     if (appData.testLegality) {
6300         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6301     } else {
6302         XtSetArg(args[0], XtNleftBitmap, None);
6303     }
6304     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6305                 args, 1);
6306 }
6307
6308
6309 void FlashMovesProc(w, event, prms, nprms)
6310      Widget w;
6311      XEvent *event;
6312      String *prms;
6313      Cardinal *nprms;
6314 {
6315     Arg args[16];
6316
6317     if (appData.flashCount == 0) {
6318         appData.flashCount = 3;
6319     } else {
6320         appData.flashCount = -appData.flashCount;
6321     }
6322
6323     if (appData.flashCount > 0) {
6324         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6325     } else {
6326         XtSetArg(args[0], XtNleftBitmap, None);
6327     }
6328     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6329                 args, 1);
6330 }
6331
6332 #if HIGHDRAG
6333 void HighlightDraggingProc(w, event, prms, nprms)
6334      Widget w;
6335      XEvent *event;
6336      String *prms;
6337      Cardinal *nprms;
6338 {
6339     Arg args[16];
6340
6341     appData.highlightDragging = !appData.highlightDragging;
6342
6343     if (appData.highlightDragging) {
6344         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6345     } else {
6346         XtSetArg(args[0], XtNleftBitmap, None);
6347     }
6348     XtSetValues(XtNameToWidget(menuBarWidget,
6349                                "menuOptions.Highlight Dragging"), args, 1);
6350 }
6351 #endif
6352
6353 void HighlightLastMoveProc(w, event, prms, nprms)
6354      Widget w;
6355      XEvent *event;
6356      String *prms;
6357      Cardinal *nprms;
6358 {
6359     Arg args[16];
6360
6361     appData.highlightLastMove = !appData.highlightLastMove;
6362
6363     if (appData.highlightLastMove) {
6364         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6365     } else {
6366         XtSetArg(args[0], XtNleftBitmap, None);
6367     }
6368     XtSetValues(XtNameToWidget(menuBarWidget,
6369                                "menuOptions.Highlight Last Move"), args, 1);
6370 }
6371
6372 void HighlightArrowProc(w, event, prms, nprms)
6373      Widget w;
6374      XEvent *event;
6375      String *prms;
6376      Cardinal *nprms;
6377 {
6378     Arg args[16];
6379
6380     appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6381
6382     if (appData.highlightMoveWithArrow) {
6383         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6384     } else {
6385         XtSetArg(args[0], XtNleftBitmap, None);
6386     }
6387     XtSetValues(XtNameToWidget(menuBarWidget,
6388                                "menuOptions.Arrow"), args, 1);
6389 }
6390
6391 #if 0
6392 void IcsAlarmProc(w, event, prms, nprms)
6393      Widget w;
6394      XEvent *event;
6395      String *prms;
6396      Cardinal *nprms;
6397 {
6398     Arg args[16];
6399
6400     appData.icsAlarm = !appData.icsAlarm;
6401
6402     if (appData.icsAlarm) {
6403         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6404     } else {
6405         XtSetArg(args[0], XtNleftBitmap, None);
6406     }
6407     XtSetValues(XtNameToWidget(menuBarWidget,
6408                                "menuOptions.ICS Alarm"), args, 1);
6409 }
6410 #endif
6411
6412 void MoveSoundProc(w, event, prms, nprms)
6413      Widget w;
6414      XEvent *event;
6415      String *prms;
6416      Cardinal *nprms;
6417 {
6418     Arg args[16];
6419
6420     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6421
6422     if (appData.ringBellAfterMoves) {
6423         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6424     } else {
6425         XtSetArg(args[0], XtNleftBitmap, None);
6426     }
6427     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6428                 args, 1);
6429 }
6430
6431 void OneClickProc(w, event, prms, nprms)
6432      Widget w;
6433      XEvent *event;
6434      String *prms;
6435      Cardinal *nprms;
6436 {
6437     Arg args[16];
6438
6439     appData.oneClick = !appData.oneClick;
6440
6441     if (appData.oneClick) {
6442         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6443     } else {
6444         XtSetArg(args[0], XtNleftBitmap, None);
6445     }
6446     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6447                 args, 1);
6448 }
6449
6450 void PeriodicUpdatesProc(w, event, prms, nprms)
6451      Widget w;
6452      XEvent *event;
6453      String *prms;
6454      Cardinal *nprms;
6455 {
6456     Arg args[16];
6457
6458     PeriodicUpdatesEvent(!appData.periodicUpdates);
6459
6460     if (appData.periodicUpdates) {
6461         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6462     } else {
6463         XtSetArg(args[0], XtNleftBitmap, None);
6464     }
6465     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6466                 args, 1);
6467 }
6468
6469 void PopupExitMessageProc(w, event, prms, nprms)
6470      Widget w;
6471      XEvent *event;
6472      String *prms;
6473      Cardinal *nprms;
6474 {
6475     Arg args[16];
6476
6477     appData.popupExitMessage = !appData.popupExitMessage;
6478
6479     if (appData.popupExitMessage) {
6480         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6481     } else {
6482         XtSetArg(args[0], XtNleftBitmap, None);
6483     }
6484     XtSetValues(XtNameToWidget(menuBarWidget,
6485                                "menuOptions.Popup Exit Message"), args, 1);
6486 }
6487
6488 void PopupMoveErrorsProc(w, event, prms, nprms)
6489      Widget w;
6490      XEvent *event;
6491      String *prms;
6492      Cardinal *nprms;
6493 {
6494     Arg args[16];
6495
6496     appData.popupMoveErrors = !appData.popupMoveErrors;
6497
6498     if (appData.popupMoveErrors) {
6499         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6500     } else {
6501         XtSetArg(args[0], XtNleftBitmap, None);
6502     }
6503     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6504                 args, 1);
6505 }
6506
6507 #if 0
6508 void PremoveProc(w, event, prms, nprms)
6509      Widget w;
6510      XEvent *event;
6511      String *prms;
6512      Cardinal *nprms;
6513 {
6514     Arg args[16];
6515
6516     appData.premove = !appData.premove;
6517
6518     if (appData.premove) {
6519         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6520     } else {
6521         XtSetArg(args[0], XtNleftBitmap, None);
6522     }
6523     XtSetValues(XtNameToWidget(menuBarWidget,
6524                                "menuOptions.Premove"), args, 1);
6525 }
6526 #endif
6527
6528 void ShowCoordsProc(w, event, prms, nprms)
6529      Widget w;
6530      XEvent *event;
6531      String *prms;
6532      Cardinal *nprms;
6533 {
6534     Arg args[16];
6535
6536     appData.showCoords = !appData.showCoords;
6537
6538     if (appData.showCoords) {
6539         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6540     } else {
6541         XtSetArg(args[0], XtNleftBitmap, None);
6542     }
6543     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6544                 args, 1);
6545
6546     DrawPosition(True, NULL);
6547 }
6548
6549 void ShowThinkingProc(w, event, prms, nprms)
6550      Widget w;
6551      XEvent *event;
6552      String *prms;
6553      Cardinal *nprms;
6554 {
6555     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6556     ShowThinkingEvent();
6557 }
6558
6559 void HideThinkingProc(w, event, prms, nprms)
6560      Widget w;
6561      XEvent *event;
6562      String *prms;
6563      Cardinal *nprms;
6564 {
6565     Arg args[16];
6566
6567     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6568     ShowThinkingEvent();
6569
6570     if (appData.hideThinkingFromHuman) {
6571         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6572     } else {
6573         XtSetArg(args[0], XtNleftBitmap, None);
6574     }
6575     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6576                 args, 1);
6577 }
6578 #endif
6579
6580 void SaveOnExitProc(w, event, prms, nprms)
6581      Widget w;
6582      XEvent *event;
6583      String *prms;
6584      Cardinal *nprms;
6585 {
6586     Arg args[16];
6587
6588     saveSettingsOnExit = !saveSettingsOnExit;
6589
6590     if (saveSettingsOnExit) {
6591         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6592     } else {
6593         XtSetArg(args[0], XtNleftBitmap, None);
6594     }
6595     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6596                 args, 1);
6597 }
6598
6599 void SaveSettingsProc(w, event, prms, nprms)
6600      Widget w;
6601      XEvent *event;
6602      String *prms;
6603      Cardinal *nprms;
6604 {
6605      SaveSettings(settingsFileName);
6606 }
6607
6608 void InfoProc(w, event, prms, nprms)
6609      Widget w;
6610      XEvent *event;
6611      String *prms;
6612      Cardinal *nprms;
6613 {
6614     char buf[MSG_SIZ];
6615     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6616             INFODIR, INFOFILE);
6617     system(buf);
6618 }
6619
6620 void ManProc(w, event, prms, nprms)
6621      Widget w;
6622      XEvent *event;
6623      String *prms;
6624      Cardinal *nprms;
6625 {
6626     char buf[MSG_SIZ];
6627     String name;
6628     if (nprms && *nprms > 0)
6629       name = prms[0];
6630     else
6631       name = "xboard";
6632     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6633     system(buf);
6634 }
6635
6636 void HintProc(w, event, prms, nprms)
6637      Widget w;
6638      XEvent *event;
6639      String *prms;
6640      Cardinal *nprms;
6641 {
6642     HintEvent();
6643 }
6644
6645 void BookProc(w, event, prms, nprms)
6646      Widget w;
6647      XEvent *event;
6648      String *prms;
6649      Cardinal *nprms;
6650 {
6651     BookEvent();
6652 }
6653
6654 void AboutProc(w, event, prms, nprms)
6655      Widget w;
6656      XEvent *event;
6657      String *prms;
6658      Cardinal *nprms;
6659 {
6660     char buf[MSG_SIZ];
6661 #if ZIPPY
6662     char *zippy = " (with Zippy code)";
6663 #else
6664     char *zippy = "";
6665 #endif
6666     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6667             programVersion, zippy,
6668             "Copyright 1991 Digital Equipment Corporation",
6669             "Enhancements Copyright 1992-2009 Free Software Foundation",
6670             "Enhancements Copyright 2005 Alessandro Scotti",
6671             PACKAGE, " is free software and carries NO WARRANTY;",
6672             "see the file COPYING for more information.");
6673     ErrorPopUp(_("About XBoard"), buf, FALSE);
6674 }
6675
6676 void DebugProc(w, event, prms, nprms)
6677      Widget w;
6678      XEvent *event;
6679      String *prms;
6680      Cardinal *nprms;
6681 {
6682     appData.debugMode = !appData.debugMode;
6683 }
6684
6685 void AboutGameProc(w, event, prms, nprms)
6686      Widget w;
6687      XEvent *event;
6688      String *prms;
6689      Cardinal *nprms;
6690 {
6691     AboutGameEvent();
6692 }
6693
6694 void NothingProc(w, event, prms, nprms)
6695      Widget w;
6696      XEvent *event;
6697      String *prms;
6698      Cardinal *nprms;
6699 {
6700     return;
6701 }
6702
6703 void Iconify(w, event, prms, nprms)
6704      Widget w;
6705      XEvent *event;
6706      String *prms;
6707      Cardinal *nprms;
6708 {
6709     Arg args[16];
6710
6711     fromX = fromY = -1;
6712     XtSetArg(args[0], XtNiconic, True);
6713     XtSetValues(shellWidget, args, 1);
6714 }
6715
6716 void DisplayMessage(message, extMessage)
6717      char *message, *extMessage;
6718 {
6719   /* display a message in the message widget */
6720
6721   char buf[MSG_SIZ];
6722   Arg arg;
6723
6724   if (extMessage)
6725     {
6726       if (*message)
6727         {
6728           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
6729           message = buf;
6730         }
6731       else
6732         {
6733           message = extMessage;
6734         };
6735     };
6736
6737   /* need to test if messageWidget already exists, since this function
6738      can also be called during the startup, if for example a Xresource
6739      is not set up correctly */
6740   if(messageWidget)
6741     {
6742       XtSetArg(arg, XtNlabel, message);
6743       XtSetValues(messageWidget, &arg, 1);
6744     };
6745
6746   return;
6747 }
6748
6749 void DisplayTitle(text)
6750      char *text;
6751 {
6752     Arg args[16];
6753     int i;
6754     char title[MSG_SIZ];
6755     char icon[MSG_SIZ];
6756
6757     if (text == NULL) text = "";
6758
6759     if (appData.titleInWindow) {
6760         i = 0;
6761         XtSetArg(args[i], XtNlabel, text);   i++;
6762         XtSetValues(titleWidget, args, i);
6763     }
6764
6765     if (*text != NULLCHAR) {
6766       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6767       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6768     } else if (appData.icsActive) {
6769         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6770         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6771     } else if (appData.cmailGameName[0] != NULLCHAR) {
6772         snprintf(icon, sizeof(icon), "%s", "CMail");
6773         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6774 #ifdef GOTHIC
6775     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6776     } else if (gameInfo.variant == VariantGothic) {
6777       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
6778       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
6779 #endif
6780 #ifdef FALCON
6781     } else if (gameInfo.variant == VariantFalcon) {
6782       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6783       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6784 #endif
6785     } else if (appData.noChessProgram) {
6786       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6787       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6788     } else {
6789       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6790         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6791     }
6792     i = 0;
6793     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6794     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6795     XtSetValues(shellWidget, args, i);
6796 }
6797
6798
6799 void
6800 DisplayError(message, error)
6801      String message;
6802      int error;
6803 {
6804     char buf[MSG_SIZ];
6805
6806     if (error == 0) {
6807         if (appData.debugMode || appData.matchMode) {
6808             fprintf(stderr, "%s: %s\n", programName, message);
6809         }
6810     } else {
6811         if (appData.debugMode || appData.matchMode) {
6812             fprintf(stderr, "%s: %s: %s\n",
6813                     programName, message, strerror(error));
6814         }
6815         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6816         message = buf;
6817     }
6818     ErrorPopUp(_("Error"), message, FALSE);
6819 }
6820
6821
6822 void DisplayMoveError(message)
6823      String message;
6824 {
6825     fromX = fromY = -1;
6826     ClearHighlights();
6827     DrawPosition(FALSE, NULL);
6828     if (appData.debugMode || appData.matchMode) {
6829         fprintf(stderr, "%s: %s\n", programName, message);
6830     }
6831     if (appData.popupMoveErrors) {
6832         ErrorPopUp(_("Error"), message, FALSE);
6833     } else {
6834         DisplayMessage(message, "");
6835     }
6836 }
6837
6838
6839 void DisplayFatalError(message, error, status)
6840      String message;
6841      int error, status;
6842 {
6843     char buf[MSG_SIZ];
6844
6845     errorExitStatus = status;
6846     if (error == 0) {
6847         fprintf(stderr, "%s: %s\n", programName, message);
6848     } else {
6849         fprintf(stderr, "%s: %s: %s\n",
6850                 programName, message, strerror(error));
6851         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6852         message = buf;
6853     }
6854     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6855       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6856     } else {
6857       ExitEvent(status);
6858     }
6859 }
6860
6861 void DisplayInformation(message)
6862      String message;
6863 {
6864     ErrorPopDown();
6865     ErrorPopUp(_("Information"), message, TRUE);
6866 }
6867
6868 void DisplayNote(message)
6869      String message;
6870 {
6871     ErrorPopDown();
6872     ErrorPopUp(_("Note"), message, FALSE);
6873 }
6874
6875 static int
6876 NullXErrorCheck(dpy, error_event)
6877      Display *dpy;
6878      XErrorEvent *error_event;
6879 {
6880     return 0;
6881 }
6882
6883 void DisplayIcsInteractionTitle(message)
6884      String message;
6885 {
6886   if (oldICSInteractionTitle == NULL) {
6887     /* Magic to find the old window title, adapted from vim */
6888     char *wina = getenv("WINDOWID");
6889     if (wina != NULL) {
6890       Window win = (Window) atoi(wina);
6891       Window root, parent, *children;
6892       unsigned int nchildren;
6893       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6894       for (;;) {
6895         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6896         if (!XQueryTree(xDisplay, win, &root, &parent,
6897                         &children, &nchildren)) break;
6898         if (children) XFree((void *)children);
6899         if (parent == root || parent == 0) break;
6900         win = parent;
6901       }
6902       XSetErrorHandler(oldHandler);
6903     }
6904     if (oldICSInteractionTitle == NULL) {
6905       oldICSInteractionTitle = "xterm";
6906     }
6907   }
6908   printf("\033]0;%s\007", message);
6909   fflush(stdout);
6910 }
6911
6912 char pendingReplyPrefix[MSG_SIZ];
6913 ProcRef pendingReplyPR;
6914
6915 void AskQuestionProc(w, event, prms, nprms)
6916      Widget w;
6917      XEvent *event;
6918      String *prms;
6919      Cardinal *nprms;
6920 {
6921     if (*nprms != 4) {
6922         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6923                 *nprms);
6924         return;
6925     }
6926     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6927 }
6928
6929 void AskQuestionPopDown()
6930 {
6931     if (!askQuestionUp) return;
6932     XtPopdown(askQuestionShell);
6933     XtDestroyWidget(askQuestionShell);
6934     askQuestionUp = False;
6935 }
6936
6937 void AskQuestionReplyAction(w, event, prms, nprms)
6938      Widget w;
6939      XEvent *event;
6940      String *prms;
6941      Cardinal *nprms;
6942 {
6943     char buf[MSG_SIZ];
6944     int err;
6945     String reply;
6946
6947     reply = XawDialogGetValueString(w = XtParent(w));
6948     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6949     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6950     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6951     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
6952     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6953     AskQuestionPopDown();
6954
6955     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6956 }
6957
6958 void AskQuestionCallback(w, client_data, call_data)
6959      Widget w;
6960      XtPointer client_data, call_data;
6961 {
6962     String name;
6963     Arg args[16];
6964
6965     XtSetArg(args[0], XtNlabel, &name);
6966     XtGetValues(w, args, 1);
6967
6968     if (strcmp(name, _("cancel")) == 0) {
6969         AskQuestionPopDown();
6970     } else {
6971         AskQuestionReplyAction(w, NULL, NULL, NULL);
6972     }
6973 }
6974
6975 void AskQuestion(title, question, replyPrefix, pr)
6976      char *title, *question, *replyPrefix;
6977      ProcRef pr;
6978 {
6979     Arg args[16];
6980     Widget popup, layout, dialog, edit;
6981     Window root, child;
6982     int x, y, i;
6983     int win_x, win_y;
6984     unsigned int mask;
6985
6986     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6987     pendingReplyPR = pr;
6988
6989     i = 0;
6990     XtSetArg(args[i], XtNresizable, True); i++;
6991     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6992     askQuestionShell = popup =
6993       XtCreatePopupShell(title, transientShellWidgetClass,
6994                          shellWidget, args, i);
6995
6996     layout =
6997       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6998                             layoutArgs, XtNumber(layoutArgs));
6999
7000     i = 0;
7001     XtSetArg(args[i], XtNlabel, question); i++;
7002     XtSetArg(args[i], XtNvalue, ""); i++;
7003     XtSetArg(args[i], XtNborderWidth, 0); i++;
7004     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7005                                    layout, args, i);
7006
7007     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7008                        (XtPointer) dialog);
7009     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7010                        (XtPointer) dialog);
7011
7012     XtRealizeWidget(popup);
7013     CatchDeleteWindow(popup, "AskQuestionPopDown");
7014
7015     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7016                   &x, &y, &win_x, &win_y, &mask);
7017
7018     XtSetArg(args[0], XtNx, x - 10);
7019     XtSetArg(args[1], XtNy, y - 30);
7020     XtSetValues(popup, args, 2);
7021
7022     XtPopup(popup, XtGrabExclusive);
7023     askQuestionUp = True;
7024
7025     edit = XtNameToWidget(dialog, "*value");
7026     XtSetKeyboardFocus(popup, edit);
7027 }
7028
7029
7030 void
7031 PlaySound(name)
7032      char *name;
7033 {
7034   if (*name == NULLCHAR) {
7035     return;
7036   } else if (strcmp(name, "$") == 0) {
7037     putc(BELLCHAR, stderr);
7038   } else {
7039     char buf[2048];
7040     char *prefix = "", *sep = "";
7041     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7042     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7043     system(buf);
7044   }
7045 }
7046
7047 void
7048 RingBell()
7049 {
7050   PlaySound(appData.soundMove);
7051 }
7052
7053 void
7054 PlayIcsWinSound()
7055 {
7056   PlaySound(appData.soundIcsWin);
7057 }
7058
7059 void
7060 PlayIcsLossSound()
7061 {
7062   PlaySound(appData.soundIcsLoss);
7063 }
7064
7065 void
7066 PlayIcsDrawSound()
7067 {
7068   PlaySound(appData.soundIcsDraw);
7069 }
7070
7071 void
7072 PlayIcsUnfinishedSound()
7073 {
7074   PlaySound(appData.soundIcsUnfinished);
7075 }
7076
7077 void
7078 PlayAlarmSound()
7079 {
7080   PlaySound(appData.soundIcsAlarm);
7081 }
7082
7083 void
7084 EchoOn()
7085 {
7086     system("stty echo");
7087 }
7088
7089 void
7090 EchoOff()
7091 {
7092     system("stty -echo");
7093 }
7094
7095 void
7096 Colorize(cc, continuation)
7097      ColorClass cc;
7098      int continuation;
7099 {
7100     char buf[MSG_SIZ];
7101     int count, outCount, error;
7102
7103     if (textColors[(int)cc].bg > 0) {
7104         if (textColors[(int)cc].fg > 0) {
7105           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7106                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7107         } else {
7108           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7109                    textColors[(int)cc].bg);
7110         }
7111     } else {
7112         if (textColors[(int)cc].fg > 0) {
7113           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7114                     textColors[(int)cc].fg);
7115         } else {
7116           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7117         }
7118     }
7119     count = strlen(buf);
7120     outCount = OutputToProcess(NoProc, buf, count, &error);
7121     if (outCount < count) {
7122         DisplayFatalError(_("Error writing to display"), error, 1);
7123     }
7124
7125     if (continuation) return;
7126     switch (cc) {
7127     case ColorShout:
7128       PlaySound(appData.soundShout);
7129       break;
7130     case ColorSShout:
7131       PlaySound(appData.soundSShout);
7132       break;
7133     case ColorChannel1:
7134       PlaySound(appData.soundChannel1);
7135       break;
7136     case ColorChannel:
7137       PlaySound(appData.soundChannel);
7138       break;
7139     case ColorKibitz:
7140       PlaySound(appData.soundKibitz);
7141       break;
7142     case ColorTell:
7143       PlaySound(appData.soundTell);
7144       break;
7145     case ColorChallenge:
7146       PlaySound(appData.soundChallenge);
7147       break;
7148     case ColorRequest:
7149       PlaySound(appData.soundRequest);
7150       break;
7151     case ColorSeek:
7152       PlaySound(appData.soundSeek);
7153       break;
7154     case ColorNormal:
7155     case ColorNone:
7156     default:
7157       break;
7158     }
7159 }
7160
7161 char *UserName()
7162 {
7163     return getpwuid(getuid())->pw_name;
7164 }
7165
7166 static char *
7167 ExpandPathName(path)
7168      char *path;
7169 {
7170     static char static_buf[4*MSG_SIZ];
7171     char *d, *s, buf[4*MSG_SIZ];
7172     struct passwd *pwd;
7173
7174     s = path;
7175     d = static_buf;
7176
7177     while (*s && isspace(*s))
7178       ++s;
7179
7180     if (!*s) {
7181         *d = 0;
7182         return static_buf;
7183     }
7184
7185     if (*s == '~') {
7186         if (*(s+1) == '/') {
7187           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7188           strcat(d, s+1);
7189         }
7190         else {
7191           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7192           { char *p; if(p = strchr(buf, '/')) *p = 0; }
7193           pwd = getpwnam(buf);
7194           if (!pwd)
7195             {
7196               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7197                       buf, path);
7198               return NULL;
7199             }
7200           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7201           strcat(d, strchr(s+1, '/'));
7202         }
7203     }
7204     else
7205       safeStrCpy(d, s, 4*MSG_SIZ );
7206
7207     return static_buf;
7208 }
7209
7210 char *HostName()
7211 {
7212     static char host_name[MSG_SIZ];
7213
7214 #if HAVE_GETHOSTNAME
7215     gethostname(host_name, MSG_SIZ);
7216     return host_name;
7217 #else  /* not HAVE_GETHOSTNAME */
7218 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7219     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7220     return host_name;
7221 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7222     return "localhost";
7223 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7224 #endif /* not HAVE_GETHOSTNAME */
7225 }
7226
7227 XtIntervalId delayedEventTimerXID = 0;
7228 DelayedEventCallback delayedEventCallback = 0;
7229
7230 void
7231 FireDelayedEvent()
7232 {
7233     delayedEventTimerXID = 0;
7234     delayedEventCallback();
7235 }
7236
7237 void
7238 ScheduleDelayedEvent(cb, millisec)
7239      DelayedEventCallback cb; long millisec;
7240 {
7241     if(delayedEventTimerXID && delayedEventCallback == cb)
7242         // [HGM] alive: replace, rather than add or flush identical event
7243         XtRemoveTimeOut(delayedEventTimerXID);
7244     delayedEventCallback = cb;
7245     delayedEventTimerXID =
7246       XtAppAddTimeOut(appContext, millisec,
7247                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7248 }
7249
7250 DelayedEventCallback
7251 GetDelayedEvent()
7252 {
7253   if (delayedEventTimerXID) {
7254     return delayedEventCallback;
7255   } else {
7256     return NULL;
7257   }
7258 }
7259
7260 void
7261 CancelDelayedEvent()
7262 {
7263   if (delayedEventTimerXID) {
7264     XtRemoveTimeOut(delayedEventTimerXID);
7265     delayedEventTimerXID = 0;
7266   }
7267 }
7268
7269 XtIntervalId loadGameTimerXID = 0;
7270
7271 int LoadGameTimerRunning()
7272 {
7273     return loadGameTimerXID != 0;
7274 }
7275
7276 int StopLoadGameTimer()
7277 {
7278     if (loadGameTimerXID != 0) {
7279         XtRemoveTimeOut(loadGameTimerXID);
7280         loadGameTimerXID = 0;
7281         return TRUE;
7282     } else {
7283         return FALSE;
7284     }
7285 }
7286
7287 void
7288 LoadGameTimerCallback(arg, id)
7289      XtPointer arg;
7290      XtIntervalId *id;
7291 {
7292     loadGameTimerXID = 0;
7293     AutoPlayGameLoop();
7294 }
7295
7296 void
7297 StartLoadGameTimer(millisec)
7298      long millisec;
7299 {
7300     loadGameTimerXID =
7301       XtAppAddTimeOut(appContext, millisec,
7302                       (XtTimerCallbackProc) LoadGameTimerCallback,
7303                       (XtPointer) 0);
7304 }
7305
7306 XtIntervalId analysisClockXID = 0;
7307
7308 void
7309 AnalysisClockCallback(arg, id)
7310      XtPointer arg;
7311      XtIntervalId *id;
7312 {
7313     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7314          || appData.icsEngineAnalyze) { // [DM]
7315         AnalysisPeriodicEvent(0);
7316         StartAnalysisClock();
7317     }
7318 }
7319
7320 void
7321 StartAnalysisClock()
7322 {
7323     analysisClockXID =
7324       XtAppAddTimeOut(appContext, 2000,
7325                       (XtTimerCallbackProc) AnalysisClockCallback,
7326                       (XtPointer) 0);
7327 }
7328
7329 XtIntervalId clockTimerXID = 0;
7330
7331 int ClockTimerRunning()
7332 {
7333     return clockTimerXID != 0;
7334 }
7335
7336 int StopClockTimer()
7337 {
7338     if (clockTimerXID != 0) {
7339         XtRemoveTimeOut(clockTimerXID);
7340         clockTimerXID = 0;
7341         return TRUE;
7342     } else {
7343         return FALSE;
7344     }
7345 }
7346
7347 void
7348 ClockTimerCallback(arg, id)
7349      XtPointer arg;
7350      XtIntervalId *id;
7351 {
7352     clockTimerXID = 0;
7353     DecrementClocks();
7354 }
7355
7356 void
7357 StartClockTimer(millisec)
7358      long millisec;
7359 {
7360     clockTimerXID =
7361       XtAppAddTimeOut(appContext, millisec,
7362                       (XtTimerCallbackProc) ClockTimerCallback,
7363                       (XtPointer) 0);
7364 }
7365
7366 void
7367 DisplayTimerLabel(w, color, timer, highlight)
7368      Widget w;
7369      char *color;
7370      long timer;
7371      int highlight;
7372 {
7373     char buf[MSG_SIZ];
7374     Arg args[16];
7375
7376     /* check for low time warning */
7377     Pixel foregroundOrWarningColor = timerForegroundPixel;
7378
7379     if (timer > 0 &&
7380         appData.lowTimeWarning &&
7381         (timer / 1000) < appData.icsAlarmTime)
7382       foregroundOrWarningColor = lowTimeWarningColor;
7383
7384     if (appData.clockMode) {
7385       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7386       XtSetArg(args[0], XtNlabel, buf);
7387     } else {
7388       snprintf(buf, MSG_SIZ, "%s  ", color);
7389       XtSetArg(args[0], XtNlabel, buf);
7390     }
7391
7392     if (highlight) {
7393
7394         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7395         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7396     } else {
7397         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7398         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7399     }
7400
7401     XtSetValues(w, args, 3);
7402 }
7403
7404 void
7405 DisplayWhiteClock(timeRemaining, highlight)
7406      long timeRemaining;
7407      int highlight;
7408 {
7409     Arg args[16];
7410
7411     if(appData.noGUI) return;
7412     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7413     if (highlight && iconPixmap == bIconPixmap) {
7414         iconPixmap = wIconPixmap;
7415         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7416         XtSetValues(shellWidget, args, 1);
7417     }
7418 }
7419
7420 void
7421 DisplayBlackClock(timeRemaining, highlight)
7422      long timeRemaining;
7423      int highlight;
7424 {
7425     Arg args[16];
7426
7427     if(appData.noGUI) return;
7428     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7429     if (highlight && iconPixmap == wIconPixmap) {
7430         iconPixmap = bIconPixmap;
7431         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7432         XtSetValues(shellWidget, args, 1);
7433     }
7434 }
7435
7436 #define CPNone 0
7437 #define CPReal 1
7438 #define CPComm 2
7439 #define CPSock 3
7440 #define CPLoop 4
7441 typedef int CPKind;
7442
7443 typedef struct {
7444     CPKind kind;
7445     int pid;
7446     int fdTo, fdFrom;
7447 } ChildProc;
7448
7449
7450 int StartChildProcess(cmdLine, dir, pr)
7451      char *cmdLine;
7452      char *dir;
7453      ProcRef *pr;
7454 {
7455     char *argv[64], *p;
7456     int i, pid;
7457     int to_prog[2], from_prog[2];
7458     ChildProc *cp;
7459     char buf[MSG_SIZ];
7460
7461     if (appData.debugMode) {
7462         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7463     }
7464
7465     /* We do NOT feed the cmdLine to the shell; we just
7466        parse it into blank-separated arguments in the
7467        most simple-minded way possible.
7468        */
7469     i = 0;
7470     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7471     p = buf;
7472     for (;;) {
7473         while(*p == ' ') p++;
7474         argv[i++] = p;
7475         if(*p == '"' || *p == '\'')
7476              p = strchr(++argv[i-1], *p);
7477         else p = strchr(p, ' ');
7478         if (p == NULL) break;
7479         *p++ = NULLCHAR;
7480     }
7481     argv[i] = NULL;
7482
7483     SetUpChildIO(to_prog, from_prog);
7484
7485     if ((pid = fork()) == 0) {
7486         /* Child process */
7487         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7488         close(to_prog[1]);     // first close the unused pipe ends
7489         close(from_prog[0]);
7490         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7491         dup2(from_prog[1], 1);
7492         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7493         close(from_prog[1]);                   // and closing again loses one of the pipes!
7494         if(fileno(stderr) >= 2) // better safe than sorry...
7495                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7496
7497         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7498             perror(dir);
7499             exit(1);
7500         }
7501
7502         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7503
7504         execvp(argv[0], argv);
7505
7506         /* If we get here, exec failed */
7507         perror(argv[0]);
7508         exit(1);
7509     }
7510
7511     /* Parent process */
7512     close(to_prog[0]);
7513     close(from_prog[1]);
7514
7515     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7516     cp->kind = CPReal;
7517     cp->pid = pid;
7518     cp->fdFrom = from_prog[0];
7519     cp->fdTo = to_prog[1];
7520     *pr = (ProcRef) cp;
7521     return 0;
7522 }
7523
7524 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7525 static RETSIGTYPE AlarmCallBack(int n)
7526 {
7527     return;
7528 }
7529
7530 void
7531 DestroyChildProcess(pr, signalType)
7532      ProcRef pr;
7533      int signalType;
7534 {
7535     ChildProc *cp = (ChildProc *) pr;
7536
7537     if (cp->kind != CPReal) return;
7538     cp->kind = CPNone;
7539     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7540         signal(SIGALRM, AlarmCallBack);
7541         alarm(3);
7542         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7543             kill(cp->pid, SIGKILL); // kill it forcefully
7544             wait((int *) 0);        // and wait again
7545         }
7546     } else {
7547         if (signalType) {
7548             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7549         }
7550         /* Process is exiting either because of the kill or because of
7551            a quit command sent by the backend; either way, wait for it to die.
7552         */
7553         wait((int *) 0);
7554     }
7555     close(cp->fdFrom);
7556     close(cp->fdTo);
7557 }
7558
7559 void
7560 InterruptChildProcess(pr)
7561      ProcRef pr;
7562 {
7563     ChildProc *cp = (ChildProc *) pr;
7564
7565     if (cp->kind != CPReal) return;
7566     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7567 }
7568
7569 int OpenTelnet(host, port, pr)
7570      char *host;
7571      char *port;
7572      ProcRef *pr;
7573 {
7574     char cmdLine[MSG_SIZ];
7575
7576     if (port[0] == NULLCHAR) {
7577       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7578     } else {
7579       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7580     }
7581     return StartChildProcess(cmdLine, "", pr);
7582 }
7583
7584 int OpenTCP(host, port, pr)
7585      char *host;
7586      char *port;
7587      ProcRef *pr;
7588 {
7589 #if OMIT_SOCKETS
7590     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7591 #else  /* !OMIT_SOCKETS */
7592     int s;
7593     struct sockaddr_in sa;
7594     struct hostent     *hp;
7595     unsigned short uport;
7596     ChildProc *cp;
7597
7598     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7599         return errno;
7600     }
7601
7602     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7603     sa.sin_family = AF_INET;
7604     sa.sin_addr.s_addr = INADDR_ANY;
7605     uport = (unsigned short) 0;
7606     sa.sin_port = htons(uport);
7607     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7608         return errno;
7609     }
7610
7611     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7612     if (!(hp = gethostbyname(host))) {
7613         int b0, b1, b2, b3;
7614         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7615             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7616             hp->h_addrtype = AF_INET;
7617             hp->h_length = 4;
7618             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7619             hp->h_addr_list[0] = (char *) malloc(4);
7620             hp->h_addr_list[0][0] = b0;
7621             hp->h_addr_list[0][1] = b1;
7622             hp->h_addr_list[0][2] = b2;
7623             hp->h_addr_list[0][3] = b3;
7624         } else {
7625             return ENOENT;
7626         }
7627     }
7628     sa.sin_family = hp->h_addrtype;
7629     uport = (unsigned short) atoi(port);
7630     sa.sin_port = htons(uport);
7631     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7632
7633     if (connect(s, (struct sockaddr *) &sa,
7634                 sizeof(struct sockaddr_in)) < 0) {
7635         return errno;
7636     }
7637
7638     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7639     cp->kind = CPSock;
7640     cp->pid = 0;
7641     cp->fdFrom = s;
7642     cp->fdTo = s;
7643     *pr = (ProcRef) cp;
7644
7645 #endif /* !OMIT_SOCKETS */
7646
7647     return 0;
7648 }
7649
7650 int OpenCommPort(name, pr)
7651      char *name;
7652      ProcRef *pr;
7653 {
7654     int fd;
7655     ChildProc *cp;
7656
7657     fd = open(name, 2, 0);
7658     if (fd < 0) return errno;
7659
7660     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7661     cp->kind = CPComm;
7662     cp->pid = 0;
7663     cp->fdFrom = fd;
7664     cp->fdTo = fd;
7665     *pr = (ProcRef) cp;
7666
7667     return 0;
7668 }
7669
7670 int OpenLoopback(pr)
7671      ProcRef *pr;
7672 {
7673     ChildProc *cp;
7674     int to[2], from[2];
7675
7676     SetUpChildIO(to, from);
7677
7678     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7679     cp->kind = CPLoop;
7680     cp->pid = 0;
7681     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7682     cp->fdTo = to[1];
7683     *pr = (ProcRef) cp;
7684
7685     return 0;
7686 }
7687
7688 int OpenRcmd(host, user, cmd, pr)
7689      char *host, *user, *cmd;
7690      ProcRef *pr;
7691 {
7692     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7693     return -1;
7694 }
7695
7696 #define INPUT_SOURCE_BUF_SIZE 8192
7697
7698 typedef struct {
7699     CPKind kind;
7700     int fd;
7701     int lineByLine;
7702     char *unused;
7703     InputCallback func;
7704     XtInputId xid;
7705     char buf[INPUT_SOURCE_BUF_SIZE];
7706     VOIDSTAR closure;
7707 } InputSource;
7708
7709 void
7710 DoInputCallback(closure, source, xid)
7711      caddr_t closure;
7712      int *source;
7713      XtInputId *xid;
7714 {
7715     InputSource *is = (InputSource *) closure;
7716     int count;
7717     int error;
7718     char *p, *q;
7719
7720     if (is->lineByLine) {
7721         count = read(is->fd, is->unused,
7722                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7723         if (count <= 0) {
7724             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7725             return;
7726         }
7727         is->unused += count;
7728         p = is->buf;
7729         while (p < is->unused) {
7730             q = memchr(p, '\n', is->unused - p);
7731             if (q == NULL) break;
7732             q++;
7733             (is->func)(is, is->closure, p, q - p, 0);
7734             p = q;
7735         }
7736         q = is->buf;
7737         while (p < is->unused) {
7738             *q++ = *p++;
7739         }
7740         is->unused = q;
7741     } else {
7742         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7743         if (count == -1)
7744           error = errno;
7745         else
7746           error = 0;
7747         (is->func)(is, is->closure, is->buf, count, error);
7748     }
7749 }
7750
7751 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7752      ProcRef pr;
7753      int lineByLine;
7754      InputCallback func;
7755      VOIDSTAR closure;
7756 {
7757     InputSource *is;
7758     ChildProc *cp = (ChildProc *) pr;
7759
7760     is = (InputSource *) calloc(1, sizeof(InputSource));
7761     is->lineByLine = lineByLine;
7762     is->func = func;
7763     if (pr == NoProc) {
7764         is->kind = CPReal;
7765         is->fd = fileno(stdin);
7766     } else {
7767         is->kind = cp->kind;
7768         is->fd = cp->fdFrom;
7769     }
7770     if (lineByLine) {
7771         is->unused = is->buf;
7772     }
7773
7774     is->xid = XtAppAddInput(appContext, is->fd,
7775                             (XtPointer) (XtInputReadMask),
7776                             (XtInputCallbackProc) DoInputCallback,
7777                             (XtPointer) is);
7778     is->closure = closure;
7779     return (InputSourceRef) is;
7780 }
7781
7782 void
7783 RemoveInputSource(isr)
7784      InputSourceRef isr;
7785 {
7786     InputSource *is = (InputSource *) isr;
7787
7788     if (is->xid == 0) return;
7789     XtRemoveInput(is->xid);
7790     is->xid = 0;
7791 }
7792
7793 int OutputToProcess(pr, message, count, outError)
7794      ProcRef pr;
7795      char *message;
7796      int count;
7797      int *outError;
7798 {
7799     static int line = 0;
7800     ChildProc *cp = (ChildProc *) pr;
7801     int outCount;
7802
7803     if (pr == NoProc)
7804     {
7805         if (appData.noJoin || !appData.useInternalWrap)
7806             outCount = fwrite(message, 1, count, stdout);
7807         else
7808         {
7809             int width = get_term_width();
7810             int len = wrap(NULL, message, count, width, &line);
7811             char *msg = malloc(len);
7812             int dbgchk;
7813
7814             if (!msg)
7815                 outCount = fwrite(message, 1, count, stdout);
7816             else
7817             {
7818                 dbgchk = wrap(msg, message, count, width, &line);
7819                 if (dbgchk != len && appData.debugMode)
7820                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7821                 outCount = fwrite(msg, 1, dbgchk, stdout);
7822                 free(msg);
7823             }
7824         }
7825     }
7826     else
7827       outCount = write(cp->fdTo, message, count);
7828
7829     if (outCount == -1)
7830       *outError = errno;
7831     else
7832       *outError = 0;
7833
7834     return outCount;
7835 }
7836
7837 /* Output message to process, with "ms" milliseconds of delay
7838    between each character. This is needed when sending the logon
7839    script to ICC, which for some reason doesn't like the
7840    instantaneous send. */
7841 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7842      ProcRef pr;
7843      char *message;
7844      int count;
7845      int *outError;
7846      long msdelay;
7847 {
7848     ChildProc *cp = (ChildProc *) pr;
7849     int outCount = 0;
7850     int r;
7851
7852     while (count--) {
7853         r = write(cp->fdTo, message++, 1);
7854         if (r == -1) {
7855             *outError = errno;
7856             return outCount;
7857         }
7858         ++outCount;
7859         if (msdelay >= 0)
7860           TimeDelay(msdelay);
7861     }
7862
7863     return outCount;
7864 }
7865
7866 /****   Animation code by Hugh Fisher, DCS, ANU.
7867
7868         Known problem: if a window overlapping the board is
7869         moved away while a piece is being animated underneath,
7870         the newly exposed area won't be updated properly.
7871         I can live with this.
7872
7873         Known problem: if you look carefully at the animation
7874         of pieces in mono mode, they are being drawn as solid
7875         shapes without interior detail while moving. Fixing
7876         this would be a major complication for minimal return.
7877 ****/
7878
7879 /*      Masks for XPM pieces. Black and white pieces can have
7880         different shapes, but in the interest of retaining my
7881         sanity pieces must have the same outline on both light
7882         and dark squares, and all pieces must use the same
7883         background square colors/images.                */
7884
7885 static int xpmDone = 0;
7886
7887 static void
7888 CreateAnimMasks (pieceDepth)
7889      int pieceDepth;
7890 {
7891   ChessSquare   piece;
7892   Pixmap        buf;
7893   GC            bufGC, maskGC;
7894   int           kind, n;
7895   unsigned long plane;
7896   XGCValues     values;
7897
7898   /* Need a bitmap just to get a GC with right depth */
7899   buf = XCreatePixmap(xDisplay, xBoardWindow,
7900                         8, 8, 1);
7901   values.foreground = 1;
7902   values.background = 0;
7903   /* Don't use XtGetGC, not read only */
7904   maskGC = XCreateGC(xDisplay, buf,
7905                     GCForeground | GCBackground, &values);
7906   XFreePixmap(xDisplay, buf);
7907
7908   buf = XCreatePixmap(xDisplay, xBoardWindow,
7909                       squareSize, squareSize, pieceDepth);
7910   values.foreground = XBlackPixel(xDisplay, xScreen);
7911   values.background = XWhitePixel(xDisplay, xScreen);
7912   bufGC = XCreateGC(xDisplay, buf,
7913                     GCForeground | GCBackground, &values);
7914
7915   for (piece = WhitePawn; piece <= BlackKing; piece++) {
7916     /* Begin with empty mask */
7917     if(!xpmDone) // [HGM] pieces: keep using existing
7918     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7919                                  squareSize, squareSize, 1);
7920     XSetFunction(xDisplay, maskGC, GXclear);
7921     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7922                    0, 0, squareSize, squareSize);
7923
7924     /* Take a copy of the piece */
7925     if (White(piece))
7926       kind = 0;
7927     else
7928       kind = 2;
7929     XSetFunction(xDisplay, bufGC, GXcopy);
7930     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7931               buf, bufGC,
7932               0, 0, squareSize, squareSize, 0, 0);
7933
7934     /* XOR the background (light) over the piece */
7935     XSetFunction(xDisplay, bufGC, GXxor);
7936     if (useImageSqs)
7937       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7938                 0, 0, squareSize, squareSize, 0, 0);
7939     else {
7940       XSetForeground(xDisplay, bufGC, lightSquareColor);
7941       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7942     }
7943
7944     /* We now have an inverted piece image with the background
7945        erased. Construct mask by just selecting all the non-zero
7946        pixels - no need to reconstruct the original image.      */
7947     XSetFunction(xDisplay, maskGC, GXor);
7948     plane = 1;
7949     /* Might be quicker to download an XImage and create bitmap
7950        data from it rather than this N copies per piece, but it
7951        only takes a fraction of a second and there is a much
7952        longer delay for loading the pieces.             */
7953     for (n = 0; n < pieceDepth; n ++) {
7954       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7955                  0, 0, squareSize, squareSize,
7956                  0, 0, plane);
7957       plane = plane << 1;
7958     }
7959   }
7960   /* Clean up */
7961   XFreePixmap(xDisplay, buf);
7962   XFreeGC(xDisplay, bufGC);
7963   XFreeGC(xDisplay, maskGC);
7964 }
7965
7966 static void
7967 InitAnimState (anim, info)
7968   AnimState * anim;
7969   XWindowAttributes * info;
7970 {
7971   XtGCMask  mask;
7972   XGCValues values;
7973
7974   /* Each buffer is square size, same depth as window */
7975   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7976                         squareSize, squareSize, info->depth);
7977   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7978                         squareSize, squareSize, info->depth);
7979
7980   /* Create a plain GC for blitting */
7981   mask = GCForeground | GCBackground | GCFunction |
7982          GCPlaneMask | GCGraphicsExposures;
7983   values.foreground = XBlackPixel(xDisplay, xScreen);
7984   values.background = XWhitePixel(xDisplay, xScreen);
7985   values.function   = GXcopy;
7986   values.plane_mask = AllPlanes;
7987   values.graphics_exposures = False;
7988   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7989
7990   /* Piece will be copied from an existing context at
7991      the start of each new animation/drag. */
7992   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7993
7994   /* Outline will be a read-only copy of an existing */
7995   anim->outlineGC = None;
7996 }
7997
7998 static void
7999 CreateAnimVars ()
8000 {
8001   XWindowAttributes info;
8002
8003   if (xpmDone && gameInfo.variant == oldVariant) return;
8004   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8005   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8006
8007   InitAnimState(&game, &info);
8008   InitAnimState(&player, &info);
8009
8010   /* For XPM pieces, we need bitmaps to use as masks. */
8011   if (useImages)
8012     CreateAnimMasks(info.depth);
8013    xpmDone = 1;
8014 }
8015
8016 #ifndef HAVE_USLEEP
8017
8018 static Boolean frameWaiting;
8019
8020 static RETSIGTYPE FrameAlarm (sig)
8021      int sig;
8022 {
8023   frameWaiting = False;
8024   /* In case System-V style signals.  Needed?? */
8025   signal(SIGALRM, FrameAlarm);
8026 }
8027
8028 static void
8029 FrameDelay (time)
8030      int time;
8031 {
8032   struct itimerval delay;
8033
8034   XSync(xDisplay, False);
8035
8036   if (time > 0) {
8037     frameWaiting = True;
8038     signal(SIGALRM, FrameAlarm);
8039     delay.it_interval.tv_sec =
8040       delay.it_value.tv_sec = time / 1000;
8041     delay.it_interval.tv_usec =
8042       delay.it_value.tv_usec = (time % 1000) * 1000;
8043     setitimer(ITIMER_REAL, &delay, NULL);
8044     while (frameWaiting) pause();
8045     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8046     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8047     setitimer(ITIMER_REAL, &delay, NULL);
8048   }
8049 }
8050
8051 #else
8052
8053 static void
8054 FrameDelay (time)
8055      int time;
8056 {
8057   XSync(xDisplay, False);
8058   if (time > 0)
8059     usleep(time * 1000);
8060 }
8061
8062 #endif
8063
8064 /*      Convert board position to corner of screen rect and color       */
8065
8066 static void
8067 ScreenSquare(column, row, pt, color)
8068      int column; int row; XPoint * pt; int * color;
8069 {
8070   if (flipView) {
8071     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8072     pt->y = lineGap + row * (squareSize + lineGap);
8073   } else {
8074     pt->x = lineGap + column * (squareSize + lineGap);
8075     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8076   }
8077   *color = SquareColor(row, column);
8078 }
8079
8080 /*      Convert window coords to square                 */
8081
8082 static void
8083 BoardSquare(x, y, column, row)
8084      int x; int y; int * column; int * row;
8085 {
8086   *column = EventToSquare(x, BOARD_WIDTH);
8087   if (flipView && *column >= 0)
8088     *column = BOARD_WIDTH - 1 - *column;
8089   *row = EventToSquare(y, BOARD_HEIGHT);
8090   if (!flipView && *row >= 0)
8091     *row = BOARD_HEIGHT - 1 - *row;
8092 }
8093
8094 /*   Utilities  */
8095
8096 #undef Max  /* just in case */
8097 #undef Min
8098 #define Max(a, b) ((a) > (b) ? (a) : (b))
8099 #define Min(a, b) ((a) < (b) ? (a) : (b))
8100
8101 static void
8102 SetRect(rect, x, y, width, height)
8103      XRectangle * rect; int x; int y; int width; int height;
8104 {
8105   rect->x = x;
8106   rect->y = y;
8107   rect->width  = width;
8108   rect->height = height;
8109 }
8110
8111 /*      Test if two frames overlap. If they do, return
8112         intersection rect within old and location of
8113         that rect within new. */
8114
8115 static Boolean
8116 Intersect(old, new, size, area, pt)
8117      XPoint * old; XPoint * new;
8118      int size; XRectangle * area; XPoint * pt;
8119 {
8120   if (old->x > new->x + size || new->x > old->x + size ||
8121       old->y > new->y + size || new->y > old->y + size) {
8122     return False;
8123   } else {
8124     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8125             size - abs(old->x - new->x), size - abs(old->y - new->y));
8126     pt->x = Max(old->x - new->x, 0);
8127     pt->y = Max(old->y - new->y, 0);
8128     return True;
8129   }
8130 }
8131
8132 /*      For two overlapping frames, return the rect(s)
8133         in the old that do not intersect with the new.   */
8134
8135 static void
8136 CalcUpdateRects(old, new, size, update, nUpdates)
8137      XPoint * old; XPoint * new; int size;
8138      XRectangle update[]; int * nUpdates;
8139 {
8140   int        count;
8141
8142   /* If old = new (shouldn't happen) then nothing to draw */
8143   if (old->x == new->x && old->y == new->y) {
8144     *nUpdates = 0;
8145     return;
8146   }
8147   /* Work out what bits overlap. Since we know the rects
8148      are the same size we don't need a full intersect calc. */
8149   count = 0;
8150   /* Top or bottom edge? */
8151   if (new->y > old->y) {
8152     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8153     count ++;
8154   } else if (old->y > new->y) {
8155     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8156                               size, old->y - new->y);
8157     count ++;
8158   }
8159   /* Left or right edge - don't overlap any update calculated above. */
8160   if (new->x > old->x) {
8161     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8162                               new->x - old->x, size - abs(new->y - old->y));
8163     count ++;
8164   } else if (old->x > new->x) {
8165     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8166                               old->x - new->x, size - abs(new->y - old->y));
8167     count ++;
8168   }
8169   /* Done */
8170   *nUpdates = count;
8171 }
8172
8173 /*      Generate a series of frame coords from start->mid->finish.
8174         The movement rate doubles until the half way point is
8175         reached, then halves back down to the final destination,
8176         which gives a nice slow in/out effect. The algorithmn
8177         may seem to generate too many intermediates for short
8178         moves, but remember that the purpose is to attract the
8179         viewers attention to the piece about to be moved and
8180         then to where it ends up. Too few frames would be less
8181         noticeable.                                             */
8182
8183 static void
8184 Tween(start, mid, finish, factor, frames, nFrames)
8185      XPoint * start; XPoint * mid;
8186      XPoint * finish; int factor;
8187      XPoint frames[]; int * nFrames;
8188 {
8189   int fraction, n, count;
8190
8191   count = 0;
8192
8193   /* Slow in, stepping 1/16th, then 1/8th, ... */
8194   fraction = 1;
8195   for (n = 0; n < factor; n++)
8196     fraction *= 2;
8197   for (n = 0; n < factor; n++) {
8198     frames[count].x = start->x + (mid->x - start->x) / fraction;
8199     frames[count].y = start->y + (mid->y - start->y) / fraction;
8200     count ++;
8201     fraction = fraction / 2;
8202   }
8203
8204   /* Midpoint */
8205   frames[count] = *mid;
8206   count ++;
8207
8208   /* Slow out, stepping 1/2, then 1/4, ... */
8209   fraction = 2;
8210   for (n = 0; n < factor; n++) {
8211     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8212     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8213     count ++;
8214     fraction = fraction * 2;
8215   }
8216   *nFrames = count;
8217 }
8218
8219 /*      Draw a piece on the screen without disturbing what's there      */
8220
8221 static void
8222 SelectGCMask(piece, clip, outline, mask)
8223      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8224 {
8225   GC source;
8226
8227   /* Bitmap for piece being moved. */
8228   if (appData.monoMode) {
8229       *mask = *pieceToSolid(piece);
8230   } else if (useImages) {
8231 #if HAVE_LIBXPM
8232       *mask = xpmMask[piece];
8233 #else
8234       *mask = ximMaskPm[piece];
8235 #endif
8236   } else {
8237       *mask = *pieceToSolid(piece);
8238   }
8239
8240   /* GC for piece being moved. Square color doesn't matter, but
8241      since it gets modified we make a copy of the original. */
8242   if (White(piece)) {
8243     if (appData.monoMode)
8244       source = bwPieceGC;
8245     else
8246       source = wlPieceGC;
8247   } else {
8248     if (appData.monoMode)
8249       source = wbPieceGC;
8250     else
8251       source = blPieceGC;
8252   }
8253   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8254
8255   /* Outline only used in mono mode and is not modified */
8256   if (White(piece))
8257     *outline = bwPieceGC;
8258   else
8259     *outline = wbPieceGC;
8260 }
8261
8262 static void
8263 OverlayPiece(piece, clip, outline,  dest)
8264      ChessSquare piece; GC clip; GC outline; Drawable dest;
8265 {
8266   int   kind;
8267
8268   if (!useImages) {
8269     /* Draw solid rectangle which will be clipped to shape of piece */
8270     XFillRectangle(xDisplay, dest, clip,
8271                    0, 0, squareSize, squareSize);
8272     if (appData.monoMode)
8273       /* Also draw outline in contrasting color for black
8274          on black / white on white cases                */
8275       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8276                  0, 0, squareSize, squareSize, 0, 0, 1);
8277   } else {
8278     /* Copy the piece */
8279     if (White(piece))
8280       kind = 0;
8281     else
8282       kind = 2;
8283     if(appData.upsideDown && flipView) kind ^= 2;
8284     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8285               dest, clip,
8286               0, 0, squareSize, squareSize,
8287               0, 0);
8288   }
8289 }
8290
8291 /* Animate the movement of a single piece */
8292
8293 static void
8294 BeginAnimation(anim, piece, startColor, start)
8295      AnimState *anim;
8296      ChessSquare piece;
8297      int startColor;
8298      XPoint * start;
8299 {
8300   Pixmap mask;
8301
8302   if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8303   /* The old buffer is initialised with the start square (empty) */
8304   BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8305   anim->prevFrame = *start;
8306
8307   /* The piece will be drawn using its own bitmap as a matte    */
8308   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8309   XSetClipMask(xDisplay, anim->pieceGC, mask);
8310 }
8311
8312 static void
8313 AnimationFrame(anim, frame, piece)
8314      AnimState *anim;
8315      XPoint *frame;
8316      ChessSquare piece;
8317 {
8318   XRectangle updates[4];
8319   XRectangle overlap;
8320   XPoint     pt;
8321   int        count, i;
8322
8323   /* Save what we are about to draw into the new buffer */
8324   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8325             frame->x, frame->y, squareSize, squareSize,
8326             0, 0);
8327
8328   /* Erase bits of the previous frame */
8329   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8330     /* Where the new frame overlapped the previous,
8331        the contents in newBuf are wrong. */
8332     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8333               overlap.x, overlap.y,
8334               overlap.width, overlap.height,
8335               pt.x, pt.y);
8336     /* Repaint the areas in the old that don't overlap new */
8337     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8338     for (i = 0; i < count; i++)
8339       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8340                 updates[i].x - anim->prevFrame.x,
8341                 updates[i].y - anim->prevFrame.y,
8342                 updates[i].width, updates[i].height,
8343                 updates[i].x, updates[i].y);
8344   } else {
8345     /* Easy when no overlap */
8346     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8347                   0, 0, squareSize, squareSize,
8348                   anim->prevFrame.x, anim->prevFrame.y);
8349   }
8350
8351   /* Save this frame for next time round */
8352   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8353                 0, 0, squareSize, squareSize,
8354                 0, 0);
8355   anim->prevFrame = *frame;
8356
8357   /* Draw piece over original screen contents, not current,
8358      and copy entire rect. Wipes out overlapping piece images. */
8359   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8360   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8361                 0, 0, squareSize, squareSize,
8362                 frame->x, frame->y);
8363 }
8364
8365 static void
8366 EndAnimation (anim, finish)
8367      AnimState *anim;
8368      XPoint *finish;
8369 {
8370   XRectangle updates[4];
8371   XRectangle overlap;
8372   XPoint     pt;
8373   int        count, i;
8374
8375   /* The main code will redraw the final square, so we
8376      only need to erase the bits that don't overlap.    */
8377   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8378     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8379     for (i = 0; i < count; i++)
8380       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8381                 updates[i].x - anim->prevFrame.x,
8382                 updates[i].y - anim->prevFrame.y,
8383                 updates[i].width, updates[i].height,
8384                 updates[i].x, updates[i].y);
8385   } else {
8386     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8387                 0, 0, squareSize, squareSize,
8388                 anim->prevFrame.x, anim->prevFrame.y);
8389   }
8390 }
8391
8392 static void
8393 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8394      AnimState *anim;
8395      ChessSquare piece; int startColor;
8396      XPoint * start; XPoint * finish;
8397      XPoint frames[]; int nFrames;
8398 {
8399   int n;
8400
8401   BeginAnimation(anim, piece, startColor, start);
8402   for (n = 0; n < nFrames; n++) {
8403     AnimationFrame(anim, &(frames[n]), piece);
8404     FrameDelay(appData.animSpeed);
8405   }
8406   EndAnimation(anim, finish);
8407 }
8408
8409 void
8410 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8411 {
8412     int i, x, y;
8413     ChessSquare piece = board[fromY][toY];
8414     board[fromY][toY] = EmptySquare;
8415     DrawPosition(FALSE, board);
8416     if (flipView) {
8417         x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8418         y = lineGap + toY * (squareSize + lineGap);
8419     } else {
8420         x = lineGap + toX * (squareSize + lineGap);
8421         y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8422     }
8423     for(i=1; i<4*kFactor; i++) {
8424         int r = squareSize * 9 * i/(20*kFactor - 5);
8425         XFillArc(xDisplay, xBoardWindow, highlineGC,
8426                 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8427         FrameDelay(appData.animSpeed);
8428     }
8429     board[fromY][toY] = piece;
8430 }
8431
8432 /* Main control logic for deciding what to animate and how */
8433
8434 void
8435 AnimateMove(board, fromX, fromY, toX, toY)
8436      Board board;
8437      int fromX;
8438      int fromY;
8439      int toX;
8440      int toY;
8441 {
8442   ChessSquare piece;
8443   int hop;
8444   XPoint      start, finish, mid;
8445   XPoint      frames[kFactor * 2 + 1];
8446   int         nFrames, startColor, endColor;
8447
8448   /* Are we animating? */
8449   if (!appData.animate || appData.blindfold)
8450     return;
8451
8452   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8453      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8454         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8455
8456   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8457   piece = board[fromY][fromX];
8458   if (piece >= EmptySquare) return;
8459
8460 #if DONT_HOP
8461   hop = FALSE;
8462 #else
8463   hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8464 #endif
8465
8466   if (appData.debugMode) {
8467       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8468                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8469              piece, fromX, fromY, toX, toY);  }
8470
8471   ScreenSquare(fromX, fromY, &start, &startColor);
8472   ScreenSquare(toX, toY, &finish, &endColor);
8473
8474   if (hop) {
8475     /* Knight: make straight movement then diagonal */
8476     if (abs(toY - fromY) < abs(toX - fromX)) {
8477        mid.x = start.x + (finish.x - start.x) / 2;
8478        mid.y = start.y;
8479      } else {
8480        mid.x = start.x;
8481        mid.y = start.y + (finish.y - start.y) / 2;
8482      }
8483   } else {
8484     mid.x = start.x + (finish.x - start.x) / 2;
8485     mid.y = start.y + (finish.y - start.y) / 2;
8486   }
8487
8488   /* Don't use as many frames for very short moves */
8489   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8490     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8491   else
8492     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8493   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8494   if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8495     int i,j;
8496     for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8497       if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8498   }
8499
8500   /* Be sure end square is redrawn */
8501   damage[0][toY][toX] = True;
8502 }
8503
8504 void
8505 DragPieceBegin(x, y)
8506      int x; int y;
8507 {
8508     int  boardX, boardY, color;
8509     XPoint corner;
8510
8511     /* Are we animating? */
8512     if (!appData.animateDragging || appData.blindfold)
8513       return;
8514
8515     /* Figure out which square we start in and the
8516        mouse position relative to top left corner. */
8517     BoardSquare(x, y, &boardX, &boardY);
8518     player.startBoardX = boardX;
8519     player.startBoardY = boardY;
8520     ScreenSquare(boardX, boardY, &corner, &color);
8521     player.startSquare  = corner;
8522     player.startColor   = color;
8523     /* As soon as we start dragging, the piece will jump slightly to
8524        be centered over the mouse pointer. */
8525     player.mouseDelta.x = squareSize/2;
8526     player.mouseDelta.y = squareSize/2;
8527     /* Initialise animation */
8528     player.dragPiece = PieceForSquare(boardX, boardY);
8529     /* Sanity check */
8530     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8531         player.dragActive = True;
8532         BeginAnimation(&player, player.dragPiece, color, &corner);
8533         /* Mark this square as needing to be redrawn. Note that
8534            we don't remove the piece though, since logically (ie
8535            as seen by opponent) the move hasn't been made yet. */
8536            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8537               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8538            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8539                      corner.x, corner.y, squareSize, squareSize,
8540                      0, 0); // [HGM] zh: unstack in stead of grab
8541            if(gatingPiece != EmptySquare) {
8542                /* Kludge alert: When gating we want the introduced
8543                   piece to appear on the from square. To generate an
8544                   image of it, we draw it on the board, copy the image,
8545                   and draw the original piece again. */
8546                ChessSquare piece = boards[currentMove][boardY][boardX];
8547                DrawSquare(boardY, boardX, gatingPiece, 0);
8548                XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8549                      corner.x, corner.y, squareSize, squareSize, 0, 0);
8550                DrawSquare(boardY, boardX, piece, 0);
8551            }
8552         damage[0][boardY][boardX] = True;
8553     } else {
8554         player.dragActive = False;
8555     }
8556 }
8557
8558 void
8559 ChangeDragPiece(ChessSquare piece)
8560 {
8561   Pixmap mask;
8562   player.dragPiece = piece;
8563   /* The piece will be drawn using its own bitmap as a matte    */
8564   SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8565   XSetClipMask(xDisplay, player.pieceGC, mask);
8566 }
8567
8568 static void
8569 DragPieceMove(x, y)
8570      int x; int y;
8571 {
8572     XPoint corner;
8573
8574     /* Are we animating? */
8575     if (!appData.animateDragging || appData.blindfold)
8576       return;
8577
8578     /* Sanity check */
8579     if (! player.dragActive)
8580       return;
8581     /* Move piece, maintaining same relative position
8582        of mouse within square    */
8583     corner.x = x - player.mouseDelta.x;
8584     corner.y = y - player.mouseDelta.y;
8585     AnimationFrame(&player, &corner, player.dragPiece);
8586 #if HIGHDRAG*0
8587     if (appData.highlightDragging) {
8588         int boardX, boardY;
8589         BoardSquare(x, y, &boardX, &boardY);
8590         SetHighlights(fromX, fromY, boardX, boardY);
8591     }
8592 #endif
8593 }
8594
8595 void
8596 DragPieceEnd(x, y)
8597      int x; int y;
8598 {
8599     int boardX, boardY, color;
8600     XPoint corner;
8601
8602     /* Are we animating? */
8603     if (!appData.animateDragging || appData.blindfold)
8604       return;
8605
8606     /* Sanity check */
8607     if (! player.dragActive)
8608       return;
8609     /* Last frame in sequence is square piece is
8610        placed on, which may not match mouse exactly. */
8611     BoardSquare(x, y, &boardX, &boardY);
8612     ScreenSquare(boardX, boardY, &corner, &color);
8613     EndAnimation(&player, &corner);
8614
8615     /* Be sure end square is redrawn */
8616     damage[0][boardY][boardX] = True;
8617
8618     /* This prevents weird things happening with fast successive
8619        clicks which on my Sun at least can cause motion events
8620        without corresponding press/release. */
8621     player.dragActive = False;
8622 }
8623
8624 /* Handle expose event while piece being dragged */
8625
8626 static void
8627 DrawDragPiece ()
8628 {
8629   if (!player.dragActive || appData.blindfold)
8630     return;
8631
8632   /* What we're doing: logically, the move hasn't been made yet,
8633      so the piece is still in it's original square. But visually
8634      it's being dragged around the board. So we erase the square
8635      that the piece is on and draw it at the last known drag point. */
8636   BlankSquare(player.startSquare.x, player.startSquare.y,
8637                 player.startColor, EmptySquare, xBoardWindow, 1);
8638   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8639   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8640 }
8641
8642 #include <sys/ioctl.h>
8643 int get_term_width()
8644 {
8645     int fd, default_width;
8646
8647     fd = STDIN_FILENO;
8648     default_width = 79; // this is FICS default anyway...
8649
8650 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8651     struct ttysize win;
8652     if (!ioctl(fd, TIOCGSIZE, &win))
8653         default_width = win.ts_cols;
8654 #elif defined(TIOCGWINSZ)
8655     struct winsize win;
8656     if (!ioctl(fd, TIOCGWINSZ, &win))
8657         default_width = win.ws_col;
8658 #endif
8659     return default_width;
8660 }
8661
8662 void
8663 update_ics_width()
8664 {
8665   static int old_width = 0;
8666   int new_width = get_term_width();
8667
8668   if (old_width != new_width)
8669     ics_printf("set width %d\n", new_width);
8670   old_width = new_width;
8671 }
8672
8673 void NotifyFrontendLogin()
8674 {
8675     update_ics_width();
8676 }
8677
8678 /* [AS] Arrow highlighting support */
8679
8680 static double A_WIDTH = 5; /* Width of arrow body */
8681
8682 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
8683 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
8684
8685 static double Sqr( double x )
8686 {
8687     return x*x;
8688 }
8689
8690 static int Round( double x )
8691 {
8692     return (int) (x + 0.5);
8693 }
8694
8695 void SquareToPos(int rank, int file, int *x, int *y)
8696 {
8697     if (flipView) {
8698         *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8699         *y = lineGap + rank * (squareSize + lineGap);
8700     } else {
8701         *x = lineGap + file * (squareSize + lineGap);
8702         *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8703     }
8704 }
8705
8706 /* Draw an arrow between two points using current settings */
8707 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8708 {
8709     XPoint arrow[7];
8710     double dx, dy, j, k, x, y;
8711
8712     if( d_x == s_x ) {
8713         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8714
8715         arrow[0].x = s_x + A_WIDTH + 0.5;
8716         arrow[0].y = s_y;
8717
8718         arrow[1].x = s_x + A_WIDTH + 0.5;
8719         arrow[1].y = d_y - h;
8720
8721         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8722         arrow[2].y = d_y - h;
8723
8724         arrow[3].x = d_x;
8725         arrow[3].y = d_y;
8726
8727         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8728         arrow[5].y = d_y - h;
8729
8730         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8731         arrow[4].y = d_y - h;
8732
8733         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8734         arrow[6].y = s_y;
8735     }
8736     else if( d_y == s_y ) {
8737         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8738
8739         arrow[0].x = s_x;
8740         arrow[0].y = s_y + A_WIDTH + 0.5;
8741
8742         arrow[1].x = d_x - w;
8743         arrow[1].y = s_y + A_WIDTH + 0.5;
8744
8745         arrow[2].x = d_x - w;
8746         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8747
8748         arrow[3].x = d_x;
8749         arrow[3].y = d_y;
8750
8751         arrow[5].x = d_x - w;
8752         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8753
8754         arrow[4].x = d_x - w;
8755         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8756
8757         arrow[6].x = s_x;
8758         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8759     }
8760     else {
8761         /* [AS] Needed a lot of paper for this! :-) */
8762         dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8763         dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8764
8765         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8766
8767         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8768
8769         x = s_x;
8770         y = s_y;
8771
8772         arrow[0].x = Round(x - j);
8773         arrow[0].y = Round(y + j*dx);
8774
8775         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
8776         arrow[1].y = Round(arrow[0].y - 2*j*dx);
8777
8778         if( d_x > s_x ) {
8779             x = (double) d_x - k;
8780             y = (double) d_y - k*dy;
8781         }
8782         else {
8783             x = (double) d_x + k;
8784             y = (double) d_y + k*dy;
8785         }
8786
8787         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8788
8789         arrow[6].x = Round(x - j);
8790         arrow[6].y = Round(y + j*dx);
8791
8792         arrow[2].x = Round(arrow[6].x + 2*j);
8793         arrow[2].y = Round(arrow[6].y - 2*j*dx);
8794
8795         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8796         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8797
8798         arrow[4].x = d_x;
8799         arrow[4].y = d_y;
8800
8801         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8802         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8803     }
8804
8805     XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8806 //    Polygon( hdc, arrow, 7 );
8807 }
8808
8809 /* [AS] Draw an arrow between two squares */
8810 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8811 {
8812     int s_x, s_y, d_x, d_y, hor, vert, i;
8813
8814     if( s_col == d_col && s_row == d_row ) {
8815         return;
8816     }
8817
8818     /* Get source and destination points */
8819     SquareToPos( s_row, s_col, &s_x, &s_y);
8820     SquareToPos( d_row, d_col, &d_x, &d_y);
8821
8822     if( d_y > s_y ) {
8823         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8824     }
8825     else if( d_y < s_y ) {
8826         d_y += squareSize / 2 + squareSize / 4;
8827     }
8828     else {
8829         d_y += squareSize / 2;
8830     }
8831
8832     if( d_x > s_x ) {
8833         d_x += squareSize / 2 - squareSize / 4;
8834     }
8835     else if( d_x < s_x ) {
8836         d_x += squareSize / 2 + squareSize / 4;
8837     }
8838     else {
8839         d_x += squareSize / 2;
8840     }
8841
8842     s_x += squareSize / 2;
8843     s_y += squareSize / 2;
8844
8845     /* Adjust width */
8846     A_WIDTH = squareSize / 14.; //[HGM] make float
8847
8848     DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8849
8850     hor = 64*s_col + 32; vert = 64*s_row + 32;
8851     for(i=0; i<= 64; i++) {
8852             damage[0][vert+6>>6][hor+6>>6] = True;
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             hor += d_col - s_col; vert += d_row - s_row;
8857     }
8858 }
8859
8860 Boolean IsDrawArrowEnabled()
8861 {
8862     return appData.highlightMoveWithArrow && squareSize >= 32;
8863 }
8864
8865 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8866 {
8867     if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8868         DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8869 }