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