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