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