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