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