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