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