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