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