Make system open command configurable
[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 void
4712 DragProc ()
4713 {
4714         GetActualPlacement(shellWidget, &wpNew);
4715         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
4716            wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
4717             return; // false alarm
4718         if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
4719         if(MoveHistoryIsUp()) CoDrag(shells[7], &wpMoveHistory);
4720         if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
4721         if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
4722         wpMain = wpNew;
4723         XDrawPosition(boardWidget, True, NULL);
4724 }
4725
4726
4727 void
4728 DelayedDrag ()
4729 {
4730     static XtIntervalId delayedDragID = 0;
4731     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
4732     delayedDragID =
4733       XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
4734 }
4735
4736 /* Why is this needed on some versions of X? */
4737 void
4738 EventProc (Widget widget, caddr_t unused, XEvent *event)
4739 {
4740     if (!XtIsRealized(widget))
4741       return;
4742     switch (event->type) {
4743       case ConfigureNotify: // main window is being dragged: drag attached windows with it
4744         if(appData.useStickyWindows)
4745             DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
4746         break;
4747       case Expose:
4748         if (event->xexpose.count > 0) return;  /* no clipping is done */
4749         XDrawPosition(widget, True, NULL);
4750         if(twoBoards) { // [HGM] dual: draw other board in other orientation
4751             flipView = !flipView; partnerUp = !partnerUp;
4752             XDrawPosition(widget, True, NULL);
4753             flipView = !flipView; partnerUp = !partnerUp;
4754         }
4755         break;
4756       case MotionNotify:
4757         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4758       default:
4759         return;
4760     }
4761 }
4762 /* end why */
4763
4764 void
4765 DrawPosition (int fullRedraw, Board board)
4766 {
4767     XDrawPosition(boardWidget, fullRedraw, board);
4768 }
4769
4770 /* Returns 1 if there are "too many" differences between b1 and b2
4771    (i.e. more than 1 move was made) */
4772 static int
4773 too_many_diffs (Board b1, Board b2)
4774 {
4775     int i, j;
4776     int c = 0;
4777
4778     for (i=0; i<BOARD_HEIGHT; ++i) {
4779         for (j=0; j<BOARD_WIDTH; ++j) {
4780             if (b1[i][j] != b2[i][j]) {
4781                 if (++c > 4)    /* Castling causes 4 diffs */
4782                   return 1;
4783             }
4784         }
4785     }
4786     return 0;
4787 }
4788
4789 /* Matrix describing castling maneuvers */
4790 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4791 static int castling_matrix[4][5] = {
4792     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4793     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4794     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4795     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4796 };
4797
4798 /* Checks whether castling occurred. If it did, *rrow and *rcol
4799    are set to the destination (row,col) of the rook that moved.
4800
4801    Returns 1 if castling occurred, 0 if not.
4802
4803    Note: Only handles a max of 1 castling move, so be sure
4804    to call too_many_diffs() first.
4805    */
4806 static int
4807 check_castle_draw (Board newb, Board oldb, int *rrow, int *rcol)
4808 {
4809     int i, *r, j;
4810     int match;
4811
4812     /* For each type of castling... */
4813     for (i=0; i<4; ++i) {
4814         r = castling_matrix[i];
4815
4816         /* Check the 4 squares involved in the castling move */
4817         match = 0;
4818         for (j=1; j<=4; ++j) {
4819             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4820                 match = 1;
4821                 break;
4822             }
4823         }
4824
4825         if (!match) {
4826             /* All 4 changed, so it must be a castling move */
4827             *rrow = r[0];
4828             *rcol = r[3];
4829             return 1;
4830         }
4831     }
4832     return 0;
4833 }
4834
4835 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4836 void
4837 DrawSeekAxis (int x, int y, int xTo, int yTo)
4838 {
4839       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4840 }
4841
4842 void
4843 DrawSeekBackground (int left, int top, int right, int bottom)
4844 {
4845     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4846 }
4847
4848 void
4849 DrawSeekText (char *buf, int x, int y)
4850 {
4851     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4852 }
4853
4854 void
4855 DrawSeekDot (int x, int y, int colorNr)
4856 {
4857     int square = colorNr & 0x80;
4858     GC color;
4859     colorNr &= 0x7F;
4860     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4861     if(square)
4862         XFillRectangle(xDisplay, xBoardWindow, color,
4863                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4864     else
4865         XFillArc(xDisplay, xBoardWindow, color,
4866                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4867 }
4868
4869 static int damage[2][BOARD_RANKS][BOARD_FILES];
4870
4871 /*
4872  * event handler for redrawing the board
4873  */
4874 void
4875 XDrawPosition (Widget w, int repaint, Board board)
4876 {
4877     int i, j, do_flash;
4878     static int lastFlipView = 0;
4879     static int lastBoardValid[2] = {0, 0};
4880     static Board lastBoard[2];
4881     Arg args[16];
4882     int rrow, rcol;
4883     int nr = twoBoards*partnerUp;
4884
4885     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4886
4887     if (board == NULL) {
4888         if (!lastBoardValid[nr]) return;
4889         board = lastBoard[nr];
4890     }
4891     if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4892         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4893         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4894                     args, 1);
4895     }
4896
4897     /*
4898      * It would be simpler to clear the window with XClearWindow()
4899      * but this causes a very distracting flicker.
4900      */
4901
4902     if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4903
4904         if ( lineGap && IsDrawArrowEnabled())
4905             XDrawSegments(xDisplay, xBoardWindow, lineGC,
4906                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4907
4908         /* If too much changes (begin observing new game, etc.), don't
4909            do flashing */
4910         do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4911
4912         /* Special check for castling so we don't flash both the king
4913            and the rook (just flash the king). */
4914         if (do_flash) {
4915             if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4916                 /* Draw rook with NO flashing. King will be drawn flashing later */
4917                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4918                 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4919             }
4920         }
4921
4922         /* First pass -- Draw (newly) empty squares and repair damage.
4923            This prevents you from having a piece show up twice while it
4924            is flashing on its new square */
4925         for (i = 0; i < BOARD_HEIGHT; i++)
4926           for (j = 0; j < BOARD_WIDTH; j++)
4927             if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4928                 || damage[nr][i][j]) {
4929                 DrawSquare(i, j, board[i][j], 0);
4930                 damage[nr][i][j] = False;
4931             }
4932
4933         /* Second pass -- Draw piece(s) in new position and flash them */
4934         for (i = 0; i < BOARD_HEIGHT; i++)
4935           for (j = 0; j < BOARD_WIDTH; j++)
4936             if (board[i][j] != lastBoard[nr][i][j]) {
4937                 DrawSquare(i, j, board[i][j], do_flash);
4938             }
4939     } else {
4940         if (lineGap > 0)
4941           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4942                         twoBoards & partnerUp ? secondSegments : // [HGM] dual
4943                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4944
4945         for (i = 0; i < BOARD_HEIGHT; i++)
4946           for (j = 0; j < BOARD_WIDTH; j++) {
4947               DrawSquare(i, j, board[i][j], 0);
4948               damage[nr][i][j] = False;
4949           }
4950     }
4951
4952     CopyBoard(lastBoard[nr], board);
4953     lastBoardValid[nr] = 1;
4954   if(nr == 0) { // [HGM] dual: no highlights on second board yet
4955     lastFlipView = flipView;
4956
4957     /* Draw highlights */
4958     if (pm1X >= 0 && pm1Y >= 0) {
4959       drawHighlight(pm1X, pm1Y, prelineGC);
4960     }
4961     if (pm2X >= 0 && pm2Y >= 0) {
4962       drawHighlight(pm2X, pm2Y, prelineGC);
4963     }
4964     if (hi1X >= 0 && hi1Y >= 0) {
4965       drawHighlight(hi1X, hi1Y, highlineGC);
4966     }
4967     if (hi2X >= 0 && hi2Y >= 0) {
4968       drawHighlight(hi2X, hi2Y, highlineGC);
4969     }
4970     DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4971   }
4972     /* If piece being dragged around board, must redraw that too */
4973     DrawDragPiece();
4974
4975     XSync(xDisplay, False);
4976 }
4977
4978
4979 /*
4980  * event handler for redrawing the board
4981  */
4982 void
4983 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4984 {
4985     XDrawPosition(w, True, NULL);
4986 }
4987
4988
4989 /*
4990  * event handler for parsing user moves
4991  */
4992 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4993 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4994 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4995 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
4996 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4997 //       and at the end FinishMove() to perform the move after optional promotion popups.
4998 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4999 void
5000 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5001 {
5002     if (w != boardWidget || errorExitStatus != -1) return;
5003     if(nprms) shiftKey = !strcmp(prms[0], "1");
5004
5005     if (promotionUp) {
5006         if (event->type == ButtonPress) {
5007             XtPopdown(promotionShell);
5008             XtDestroyWidget(promotionShell);
5009             promotionUp = False;
5010             ClearHighlights();
5011             fromX = fromY = -1;
5012         } else {
5013             return;
5014         }
5015     }
5016
5017     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
5018     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
5019     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
5020 }
5021
5022 void
5023 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
5024 {
5025     if(!PromoScroll(event->xmotion.x, event->xmotion.y))
5026     DragPieceMove(event->xmotion.x, event->xmotion.y);
5027 }
5028
5029 void
5030 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
5031 {   // [HGM] pv: walk PV
5032     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
5033 }
5034
5035 static int savedIndex;  /* gross that this is global */
5036
5037 void
5038 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
5039 {
5040         String val;
5041         XawTextPosition index, dummy;
5042         Arg arg;
5043
5044         XawTextGetSelectionPos(w, &index, &dummy);
5045         XtSetArg(arg, XtNstring, &val);
5046         XtGetValues(w, &arg, 1);
5047         ReplaceComment(savedIndex, val);
5048         if(savedIndex != currentMove) ToNrEvent(savedIndex);
5049         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
5050 }
5051
5052 void
5053 EditCommentPopUp (int index, char *title, char *text)
5054 {
5055     savedIndex = index;
5056     if (text == NULL) text = "";
5057     NewCommentPopup(title, text, index);
5058 }
5059
5060 void
5061 ICSInputBoxPopUp ()
5062 {
5063     InputBoxPopup();
5064 }
5065
5066 extern Option boxOptions[];
5067
5068 void
5069 ICSInputSendText ()
5070 {
5071     Widget edit;
5072     int j;
5073     Arg args[16];
5074     String val;
5075
5076     edit = boxOptions[0].handle;
5077     j = 0;
5078     XtSetArg(args[j], XtNstring, &val); j++;
5079     XtGetValues(edit, args, j);
5080     SaveInHistory(val);
5081     SendMultiLineToICS(val);
5082     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5083     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5084 }
5085
5086 void
5087 ICSInputBoxPopDown ()
5088 {
5089     PopDown(4);
5090 }
5091
5092 void
5093 CommentPopUp (char *title, char *text)
5094 {
5095     savedIndex = currentMove; // [HGM] vari
5096     NewCommentPopup(title, text, currentMove);
5097 }
5098
5099 void
5100 CommentPopDown ()
5101 {
5102     PopDown(1);
5103 }
5104
5105 static char *openName;
5106 FILE *openFP;
5107
5108 void
5109 DelayedLoad ()
5110 {
5111   (void) (*fileProc)(openFP, 0, openName);
5112 }
5113
5114 void
5115 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
5116 {
5117     fileProc = proc;            /* I can't see a way not */
5118     fileOpenMode = openMode;    /*   to use globals here */
5119     {   // [HGM] use file-selector dialog stolen from Ghostview
5120         int index; // this is not supported yet
5121         if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
5122                            (def[0] ? def : NULL), filter, openMode, NULL, &openName))
5123           // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
5124           ScheduleDelayedEvent(&DelayedLoad, 50);
5125     }
5126 }
5127
5128 void
5129 FileNamePopDown ()
5130 {
5131     if (!filenameUp) return;
5132     XtPopdown(fileNameShell);
5133     XtDestroyWidget(fileNameShell);
5134     filenameUp = False;
5135     ModeHighlight();
5136 }
5137
5138 void
5139 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
5140 {
5141     String name;
5142     Arg args[16];
5143
5144     XtSetArg(args[0], XtNlabel, &name);
5145     XtGetValues(w, args, 1);
5146
5147     if (strcmp(name, _("cancel")) == 0) {
5148         FileNamePopDown();
5149         return;
5150     }
5151
5152     FileNameAction(w, NULL, NULL, NULL);
5153 }
5154
5155 void
5156 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5157 {
5158     char buf[MSG_SIZ];
5159     String name;
5160     FILE *f;
5161     char *p, *fullname;
5162     int index;
5163
5164     name = XawDialogGetValueString(w = XtParent(w));
5165
5166     if ((name != NULL) && (*name != NULLCHAR)) {
5167         safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5168         XtPopdown(w = XtParent(XtParent(w)));
5169         XtDestroyWidget(w);
5170         filenameUp = False;
5171
5172         p = strrchr(buf, ' ');
5173         if (p == NULL) {
5174             index = 0;
5175         } else {
5176             *p++ = NULLCHAR;
5177             index = atoi(p);
5178         }
5179         fullname = ExpandPathName(buf);
5180         if (!fullname) {
5181             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5182         }
5183         else {
5184             f = fopen(fullname, fileOpenMode);
5185             if (f == NULL) {
5186                 DisplayError(_("Failed to open file"), errno);
5187             } else {
5188                 (void) (*fileProc)(f, index, buf);
5189             }
5190         }
5191         ModeHighlight();
5192         return;
5193     }
5194
5195     XtPopdown(w = XtParent(XtParent(w)));
5196     XtDestroyWidget(w);
5197     filenameUp = False;
5198     ModeHighlight();
5199 }
5200
5201 void
5202 PromotionPopUp ()
5203 {
5204     Arg args[16];
5205     Widget dialog, layout;
5206     Position x, y;
5207     Dimension bw_width, pw_width;
5208     int j;
5209     char *PromoChars = "wglcqrbnkac+=\0";
5210
5211     j = 0;
5212     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5213     XtGetValues(boardWidget, args, j);
5214
5215     j = 0;
5216     XtSetArg(args[j], XtNresizable, True); j++;
5217     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5218     promotionShell =
5219       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5220                          shellWidget, args, j);
5221     layout =
5222       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5223                             layoutArgs, XtNumber(layoutArgs));
5224
5225     j = 0;
5226     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5227     XtSetArg(args[j], XtNborderWidth, 0); j++;
5228     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5229                                    layout, args, j);
5230
5231   if(gameInfo.variant != VariantShogi) {
5232    if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5233       XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
5234       XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
5235       XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
5236       XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
5237     } else {
5238     XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
5239     XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
5240     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
5241     XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
5242     }
5243     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5244         gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
5245         gameInfo.variant == VariantGiveaway) {
5246       XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
5247     }
5248     if(gameInfo.variant == VariantCapablanca ||
5249        gameInfo.variant == VariantGothic ||
5250        gameInfo.variant == VariantCapaRandom) {
5251       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
5252       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
5253     }
5254   } else // [HGM] shogi
5255   {
5256       XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
5257       XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
5258   }
5259     XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
5260
5261     XtRealizeWidget(promotionShell);
5262     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5263
5264     j = 0;
5265     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5266     XtGetValues(promotionShell, args, j);
5267
5268     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5269                       lineGap + squareSize/3 +
5270                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5271                        0 : 6*(squareSize + lineGap)), &x, &y);
5272
5273     j = 0;
5274     XtSetArg(args[j], XtNx, x); j++;
5275     XtSetArg(args[j], XtNy, y); j++;
5276     XtSetValues(promotionShell, args, j);
5277
5278     XtPopup(promotionShell, XtGrabNone);
5279
5280     promotionUp = True;
5281 }
5282
5283 void
5284 PromotionPopDown ()
5285 {
5286     if (!promotionUp) return;
5287     XtPopdown(promotionShell);
5288     XtDestroyWidget(promotionShell);
5289     promotionUp = False;
5290 }
5291
5292 void
5293 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
5294 {
5295     int promoChar = * (const char *) client_data;
5296
5297     PromotionPopDown();
5298
5299     if (fromX == -1) return;
5300
5301     if (! promoChar) {
5302         fromX = fromY = -1;
5303         ClearHighlights();
5304         return;
5305     }
5306     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5307
5308     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5309     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5310     fromX = fromY = -1;
5311 }
5312
5313
5314 void
5315 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
5316 {
5317     dialogError = errorUp = False;
5318     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5319     XtDestroyWidget(w);
5320     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5321 }
5322
5323
5324 void
5325 ErrorPopDown ()
5326 {
5327     if (!errorUp) return;
5328     dialogError = errorUp = False;
5329     XtPopdown(errorShell);
5330     XtDestroyWidget(errorShell);
5331     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5332 }
5333
5334 void
5335 ErrorPopUp (char *title, char *label, int modal)
5336 {
5337     Arg args[16];
5338     Widget dialog, layout;
5339     Position x, y;
5340     int xx, yy;
5341     Window junk;
5342     Dimension bw_width, pw_width;
5343     Dimension pw_height;
5344     int i;
5345
5346     i = 0;
5347     XtSetArg(args[i], XtNresizable, True);  i++;
5348     XtSetArg(args[i], XtNtitle, title); i++;
5349     errorShell =
5350       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5351                          shellUp[0] ? (dialogError = modal = TRUE, shells[0]) : shellWidget, args, i);
5352     layout =
5353       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5354                             layoutArgs, XtNumber(layoutArgs));
5355
5356     i = 0;
5357     XtSetArg(args[i], XtNlabel, label); i++;
5358     XtSetArg(args[i], XtNborderWidth, 0); i++;
5359     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5360                                    layout, args, i);
5361
5362     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5363
5364     XtRealizeWidget(errorShell);
5365     CatchDeleteWindow(errorShell, "ErrorPopDown");
5366
5367     i = 0;
5368     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5369     XtGetValues(boardWidget, args, i);
5370     i = 0;
5371     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5372     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5373     XtGetValues(errorShell, args, i);
5374
5375 #ifdef NOTDEF
5376     /* This code seems to tickle an X bug if it is executed too soon
5377        after xboard starts up.  The coordinates get transformed as if
5378        the main window was positioned at (0, 0).
5379        */
5380     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5381                       0 - pw_height + squareSize / 3, &x, &y);
5382 #else
5383     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5384                           RootWindowOfScreen(XtScreen(boardWidget)),
5385                           (bw_width - pw_width) / 2,
5386                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5387     x = xx;
5388     y = yy;
5389 #endif
5390     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5391
5392     i = 0;
5393     XtSetArg(args[i], XtNx, x);  i++;
5394     XtSetArg(args[i], XtNy, y);  i++;
5395     XtSetValues(errorShell, args, i);
5396
5397     errorUp = True;
5398     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5399 }
5400
5401 /* Disable all user input other than deleting the window */
5402 static int frozen = 0;
5403
5404 void
5405 FreezeUI ()
5406 {
5407   if (frozen) return;
5408   /* Grab by a widget that doesn't accept input */
5409   XtAddGrab(messageWidget, TRUE, FALSE);
5410   frozen = 1;
5411 }
5412
5413 /* Undo a FreezeUI */
5414 void
5415 ThawUI ()
5416 {
5417   if (!frozen) return;
5418   XtRemoveGrab(messageWidget);
5419   frozen = 0;
5420 }
5421
5422 char *
5423 ModeToWidgetName (GameMode mode)
5424 {
5425     switch (mode) {
5426       case BeginningOfGame:
5427         if (appData.icsActive)
5428           return "menuMode.ICS Client";
5429         else if (appData.noChessProgram ||
5430                  *appData.cmailGameName != NULLCHAR)
5431           return "menuMode.Edit Game";
5432         else
5433           return "menuMode.Machine Black";
5434       case MachinePlaysBlack:
5435         return "menuMode.Machine Black";
5436       case MachinePlaysWhite:
5437         return "menuMode.Machine White";
5438       case AnalyzeMode:
5439         return "menuMode.Analysis Mode";
5440       case AnalyzeFile:
5441         return "menuMode.Analyze File";
5442       case TwoMachinesPlay:
5443         return "menuMode.Two Machines";
5444       case EditGame:
5445         return "menuMode.Edit Game";
5446       case PlayFromGameFile:
5447         return "menuFile.Load Game";
5448       case EditPosition:
5449         return "menuMode.Edit Position";
5450       case Training:
5451         return "menuMode.Training";
5452       case IcsPlayingWhite:
5453       case IcsPlayingBlack:
5454       case IcsObserving:
5455       case IcsIdle:
5456       case IcsExamining:
5457         return "menuMode.ICS Client";
5458       default:
5459       case EndOfGame:
5460         return NULL;
5461     }
5462 }
5463
5464 void
5465 ModeHighlight ()
5466 {
5467     Arg args[16];
5468     static int oldPausing = FALSE;
5469     static GameMode oldmode = (GameMode) -1;
5470     char *wname;
5471
5472     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5473
5474     if (pausing != oldPausing) {
5475         oldPausing = pausing;
5476         if (pausing) {
5477             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5478         } else {
5479             XtSetArg(args[0], XtNleftBitmap, None);
5480         }
5481         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5482                     args, 1);
5483
5484         if (appData.showButtonBar) {
5485           /* Always toggle, don't set.  Previous code messes up when
5486              invoked while the button is pressed, as releasing it
5487              toggles the state again. */
5488           {
5489             Pixel oldbg, oldfg;
5490             XtSetArg(args[0], XtNbackground, &oldbg);
5491             XtSetArg(args[1], XtNforeground, &oldfg);
5492             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5493                         args, 2);
5494             XtSetArg(args[0], XtNbackground, oldfg);
5495             XtSetArg(args[1], XtNforeground, oldbg);
5496           }
5497           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5498         }
5499     }
5500
5501     wname = ModeToWidgetName(oldmode);
5502     if (wname != NULL) {
5503         XtSetArg(args[0], XtNleftBitmap, None);
5504         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5505     }
5506     wname = ModeToWidgetName(gameMode);
5507     if (wname != NULL) {
5508         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5509         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5510     }
5511     oldmode = gameMode;
5512     XtSetArg(args[0], XtNleftBitmap, matchMode && matchGame < appData.matchGames ? xMarkPixmap : None);
5513     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Machine Match"), args, 1);
5514
5515     /* Maybe all the enables should be handled here, not just this one */
5516     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5517                    gameMode == Training || gameMode == PlayFromGameFile);
5518 }
5519
5520
5521 /*
5522  * Button/menu procedures
5523  */
5524 void
5525 ResetProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5526 {
5527     ResetGameEvent();
5528 }
5529
5530 int
5531 LoadGamePopUp (FILE *f, int gameNumber, char *title)
5532 {
5533     cmailMsgLoaded = FALSE;
5534     if (gameNumber == 0) {
5535         int error = GameListBuild(f);
5536         if (error) {
5537             DisplayError(_("Cannot build game list"), error);
5538         } else if (!ListEmpty(&gameList) &&
5539                    ((ListGame *) gameList.tailPred)->number > 1) {
5540             GameListPopUp(f, title);
5541             return TRUE;
5542         }
5543         GameListDestroy();
5544         gameNumber = 1;
5545     }
5546     return LoadGame(f, gameNumber, title, FALSE);
5547 }
5548
5549 void
5550 LoadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5551 {
5552     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5553         Reset(FALSE, TRUE);
5554     }
5555     FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5556 }
5557
5558 void
5559 LoadNextGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5560 {
5561     ReloadGame(1);
5562 }
5563
5564 void
5565 LoadPrevGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5566 {
5567     ReloadGame(-1);
5568 }
5569
5570 void
5571 ReloadGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5572 {
5573     ReloadGame(0);
5574 }
5575
5576 void
5577 LoadNextPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5578 {
5579     ReloadPosition(1);
5580 }
5581
5582 void
5583 LoadPrevPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5584 {
5585     ReloadPosition(-1);
5586 }
5587
5588 void
5589 ReloadPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5590 {
5591     ReloadPosition(0);
5592 }
5593
5594 void
5595 LoadPositionProc(Widget w, XEvent *event, String *prms, Cardinal *nprms) 
5596 {
5597     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5598         Reset(FALSE, TRUE);
5599     }
5600     FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5601 }
5602
5603 void
5604 SaveGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5605 {
5606     FileNamePopUp(_("Save game file name?"),
5607                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5608                   appData.oldSaveStyle ? ".game" : ".pgn",
5609                   SaveGame, "a");
5610 }
5611
5612 void
5613 SavePositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5614 {
5615     FileNamePopUp(_("Save position file name?"),
5616                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5617                   appData.oldSaveStyle ? ".pos" : ".fen",
5618                   SavePosition, "a");
5619 }
5620
5621 void
5622 ReloadCmailMsgProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5623 {
5624     ReloadCmailMsgEvent(FALSE);
5625 }
5626
5627 void
5628 MailMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5629 {
5630     MailMoveEvent();
5631 }
5632
5633 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5634 char *selected_fen_position=NULL;
5635
5636 Boolean
5637 SendPositionSelection (Widget w, Atom *selection, Atom *target,
5638                        Atom *type_return, XtPointer *value_return,
5639                        unsigned long *length_return, int *format_return)
5640 {
5641   char *selection_tmp;
5642
5643   if (!selected_fen_position) return False; /* should never happen */
5644   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5645     /* note: since no XtSelectionDoneProc was registered, Xt will
5646      * automatically call XtFree on the value returned.  So have to
5647      * make a copy of it allocated with XtMalloc */
5648     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5649     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5650
5651     *value_return=selection_tmp;
5652     *length_return=strlen(selection_tmp);
5653     *type_return=*target;
5654     *format_return = 8; /* bits per byte */
5655     return True;
5656   } else if (*target == XA_TARGETS(xDisplay)) {
5657     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5658     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5659     targets_tmp[1] = XA_STRING;
5660     *value_return = targets_tmp;
5661     *type_return = XA_ATOM;
5662     *length_return = 2;
5663 #if 0
5664     // This code leads to a read of value_return out of bounds on 64-bit systems.
5665     // Other code which I have seen always sets *format_return to 32 independent of
5666     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5667     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5668     *format_return = 8 * sizeof(Atom);
5669     if (*format_return > 32) {
5670       *length_return *= *format_return / 32;
5671       *format_return = 32;
5672     }
5673 #else
5674     *format_return = 32;
5675 #endif
5676     return True;
5677   } else {
5678     return False;
5679   }
5680 }
5681
5682 /* note: when called from menu all parameters are NULL, so no clue what the
5683  * Widget which was clicked on was, or what the click event was
5684  */
5685 void
5686 CopyPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5687 {
5688     /*
5689      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5690      * have a notion of a position that is selected but not copied.
5691      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5692      */
5693     if(gameMode == EditPosition) EditPositionDone(TRUE);
5694     if (selected_fen_position) free(selected_fen_position);
5695     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5696     if (!selected_fen_position) return;
5697     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5698                    CurrentTime,
5699                    SendPositionSelection,
5700                    NULL/* lose_ownership_proc */ ,
5701                    NULL/* transfer_done_proc */);
5702     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5703                    CurrentTime,
5704                    SendPositionSelection,
5705                    NULL/* lose_ownership_proc */ ,
5706                    NULL/* transfer_done_proc */);
5707 }
5708
5709 void
5710 CopyFENToClipboard ()
5711 { // wrapper to make call from back-end possible
5712   CopyPositionProc(NULL, NULL, NULL, NULL);
5713 }
5714
5715 /* function called when the data to Paste is ready */
5716 static void
5717 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
5718                  Atom *type, XtPointer value, unsigned long *len, int *format)
5719 {
5720   char *fenstr=value;
5721   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5722   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5723   EditPositionPasteFEN(fenstr);
5724   XtFree(value);
5725 }
5726
5727 /* called when Paste Position button is pressed,
5728  * all parameters will be NULL */
5729 void PastePositionProc(w, event, prms, nprms)
5730   Widget w;
5731   XEvent *event;
5732   String *prms;
5733   Cardinal *nprms;
5734 {
5735     XtGetSelectionValue(menuBarWidget,
5736       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5737       /* (XtSelectionCallbackProc) */ PastePositionCB,
5738       NULL, /* client_data passed to PastePositionCB */
5739
5740       /* better to use the time field from the event that triggered the
5741        * call to this function, but that isn't trivial to get
5742        */
5743       CurrentTime
5744     );
5745     return;
5746 }
5747
5748 static Boolean
5749 SendGameSelection (Widget w, Atom *selection, Atom *target,
5750                    Atom *type_return, XtPointer *value_return,
5751                    unsigned long *length_return, int *format_return)
5752 {
5753   char *selection_tmp;
5754
5755   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5756     FILE* f = fopen(gameCopyFilename, "r");
5757     long len;
5758     size_t count;
5759     if (f == NULL) return False;
5760     fseek(f, 0, 2);
5761     len = ftell(f);
5762     rewind(f);
5763     selection_tmp = XtMalloc(len + 1);
5764     count = fread(selection_tmp, 1, len, f);
5765     fclose(f);
5766     if (len != count) {
5767       XtFree(selection_tmp);
5768       return False;
5769     }
5770     selection_tmp[len] = NULLCHAR;
5771     *value_return = selection_tmp;
5772     *length_return = len;
5773     *type_return = *target;
5774     *format_return = 8; /* bits per byte */
5775     return True;
5776   } else if (*target == XA_TARGETS(xDisplay)) {
5777     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5778     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5779     targets_tmp[1] = XA_STRING;
5780     *value_return = targets_tmp;
5781     *type_return = XA_ATOM;
5782     *length_return = 2;
5783 #if 0
5784     // This code leads to a read of value_return out of bounds on 64-bit systems.
5785     // Other code which I have seen always sets *format_return to 32 independent of
5786     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
5787     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
5788     *format_return = 8 * sizeof(Atom);
5789     if (*format_return > 32) {
5790       *length_return *= *format_return / 32;
5791       *format_return = 32;
5792     }
5793 #else
5794     *format_return = 32;
5795 #endif
5796     return True;
5797   } else {
5798     return False;
5799   }
5800 }
5801
5802 void
5803 CopySomething ()
5804 {
5805   /*
5806    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5807    * have a notion of a game that is selected but not copied.
5808    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5809    */
5810   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5811                  CurrentTime,
5812                  SendGameSelection,
5813                  NULL/* lose_ownership_proc */ ,
5814                  NULL/* transfer_done_proc */);
5815   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5816                  CurrentTime,
5817                  SendGameSelection,
5818                  NULL/* lose_ownership_proc */ ,
5819                  NULL/* transfer_done_proc */);
5820 }
5821
5822 /* note: when called from menu all parameters are NULL, so no clue what the
5823  * Widget which was clicked on was, or what the click event was
5824  */
5825 void
5826 CopyGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5827 {
5828   int ret;
5829
5830   ret = SaveGameToFile(gameCopyFilename, FALSE);
5831   if (!ret) return;
5832
5833   CopySomething();
5834 }
5835
5836 void
5837 CopyGameListProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5838 {
5839   if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5840   CopySomething();
5841 }
5842
5843 /* function called when the data to Paste is ready */
5844 static void
5845 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
5846              Atom *type, XtPointer value, unsigned long *len, int *format)
5847 {
5848   FILE* f;
5849   if (value == NULL || *len == 0) {
5850     return; /* nothing had been selected to copy */
5851   }
5852   f = fopen(gamePasteFilename, "w");
5853   if (f == NULL) {
5854     DisplayError(_("Can't open temp file"), errno);
5855     return;
5856   }
5857   fwrite(value, 1, *len, f);
5858   fclose(f);
5859   XtFree(value);
5860   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5861 }
5862
5863 /* called when Paste Game button is pressed,
5864  * all parameters will be NULL */
5865 void
5866 PasteGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5867 {
5868     XtGetSelectionValue(menuBarWidget,
5869       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5870       /* (XtSelectionCallbackProc) */ PasteGameCB,
5871       NULL, /* client_data passed to PasteGameCB */
5872
5873       /* better to use the time field from the event that triggered the
5874        * call to this function, but that isn't trivial to get
5875        */
5876       CurrentTime
5877     );
5878     return;
5879 }
5880
5881
5882 void
5883 AutoSaveGame ()
5884 {
5885     SaveGameProc(NULL, NULL, NULL, NULL);
5886 }
5887
5888
5889 void
5890 QuitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5891 {
5892     ExitEvent(0);
5893 }
5894
5895 void
5896 PauseProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5897 {
5898     PauseEvent();
5899 }
5900
5901 void
5902 MachineBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5903 {
5904     MachineBlackEvent();
5905 }
5906
5907 void
5908 MachineWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5909 {
5910     MachineWhiteEvent();
5911 }
5912
5913 void
5914 AnalyzeModeProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5915 {
5916     char buf[MSG_SIZ];
5917
5918     if (!first.analysisSupport) {
5919       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5920       DisplayError(buf, 0);
5921       return;
5922     }
5923     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5924     if (appData.icsActive) {
5925         if (gameMode != IcsObserving) {
5926           snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5927             DisplayError(buf, 0);
5928             /* secure check */
5929             if (appData.icsEngineAnalyze) {
5930                 if (appData.debugMode)
5931                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5932                 ExitAnalyzeMode();
5933                 ModeHighlight();
5934             }
5935             return;
5936         }
5937         /* if enable, use want disable icsEngineAnalyze */
5938         if (appData.icsEngineAnalyze) {
5939                 ExitAnalyzeMode();
5940                 ModeHighlight();
5941                 return;
5942         }
5943         appData.icsEngineAnalyze = TRUE;
5944         if (appData.debugMode)
5945             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5946     }
5947 #ifndef OPTIONSDIALOG
5948     if (!appData.showThinking)
5949       ShowThinkingProc(w,event,prms,nprms);
5950 #endif
5951
5952     AnalyzeModeEvent();
5953 }
5954
5955 void
5956 AnalyzeFileProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5957 {
5958     if (!first.analysisSupport) {
5959       char buf[MSG_SIZ];
5960       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5961       DisplayError(buf, 0);
5962       return;
5963     }
5964 //    Reset(FALSE, TRUE);
5965 #ifndef OPTIONSDIALOG
5966     if (!appData.showThinking)
5967       ShowThinkingProc(w,event,prms,nprms);
5968 #endif
5969     AnalyzeFileEvent();
5970 //    FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5971     AnalysisPeriodicEvent(1);
5972 }
5973
5974 void
5975 TwoMachinesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5976 {
5977     TwoMachinesEvent();
5978 }
5979
5980 void
5981 MatchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5982 {
5983     MatchEvent(2);
5984 }
5985
5986 void
5987 IcsClientProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5988 {
5989     IcsClientEvent();
5990 }
5991
5992 void
5993 EditGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
5994 {
5995     EditGameEvent();
5996 }
5997
5998 void
5999 EditPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6000 {
6001     EditPositionEvent();
6002 }
6003
6004 void
6005 TrainingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6006 {
6007     TrainingEvent();
6008 }
6009
6010 void
6011 EditCommentProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6012 {
6013     Arg args[5];
6014     int j;
6015     if (PopDown(1)) { // popdown succesful
6016         j = 0;
6017         XtSetArg(args[j], XtNleftBitmap, None); j++;
6018         XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6019         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6020     } else // was not up
6021         EditCommentEvent();
6022 }
6023
6024 void
6025 IcsInputBoxProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6026 {
6027     if (!PopDown(4)) ICSInputBoxPopUp();
6028 }
6029
6030 void
6031 AcceptProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6032 {
6033     AcceptEvent();
6034 }
6035
6036 void
6037 DeclineProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6038 {
6039     DeclineEvent();
6040 }
6041
6042 void
6043 RematchProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6044 {
6045     RematchEvent();
6046 }
6047
6048 void
6049 CallFlagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6050 {
6051     CallFlagEvent();
6052 }
6053
6054 void
6055 DrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6056 {
6057     DrawEvent();
6058 }
6059
6060 void
6061 AbortProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6062 {
6063     AbortEvent();
6064 }
6065
6066 void
6067 AdjournProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6068 {
6069     AdjournEvent();
6070 }
6071
6072 void
6073 ResignProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6074 {
6075     ResignEvent();
6076 }
6077
6078 void
6079 AdjuWhiteProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6080 {
6081     UserAdjudicationEvent(+1);
6082 }
6083
6084 void
6085 AdjuBlackProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6086 {
6087     UserAdjudicationEvent(-1);
6088 }
6089
6090 void
6091 AdjuDrawProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6092 {
6093     UserAdjudicationEvent(0);
6094 }
6095
6096 void
6097 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6098 {
6099     if (shellUp[4] == True)
6100       ICSInputSendText();
6101 }
6102
6103 void
6104 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6105 {   // [HGM] input: let up-arrow recall previous line from history
6106     Widget edit;
6107     int j;
6108     Arg args[16];
6109     String val;
6110     XawTextBlock t;
6111
6112     if (!shellUp[4]) return;
6113     edit = boxOptions[0].handle;
6114     j = 0;
6115     XtSetArg(args[j], XtNstring, &val); j++;
6116     XtGetValues(edit, args, j);
6117     val = PrevInHistory(val);
6118     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6119     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6120     if(val) {
6121         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6122         XawTextReplace(edit, 0, 0, &t);
6123         XawTextSetInsertionPoint(edit, 9999);
6124     }
6125 }
6126
6127 void
6128 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6129 {   // [HGM] input: let down-arrow recall next line from history
6130     Widget edit;
6131     String val;
6132     XawTextBlock t;
6133
6134     if (!shellUp[4]) return;
6135     edit = boxOptions[0].handle;
6136     val = NextInHistory();
6137     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6138     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6139     if(val) {
6140         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6141         XawTextReplace(edit, 0, 0, &t);
6142         XawTextSetInsertionPoint(edit, 9999);
6143     }
6144 }
6145
6146 void
6147 StopObservingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6148 {
6149     StopObservingEvent();
6150 }
6151
6152 void
6153 StopExaminingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6154 {
6155     StopExaminingEvent();
6156 }
6157
6158 void
6159 UploadProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6160 {
6161     UploadGameEvent();
6162 }
6163
6164
6165 void
6166 ForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6167 {
6168     ForwardEvent();
6169 }
6170
6171
6172 void
6173 BackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6174 {
6175     BackwardEvent();
6176 }
6177
6178 void
6179 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6180 {
6181         if (!TempBackwardActive) {
6182                 TempBackwardActive = True;
6183                 BackwardEvent();
6184         }
6185 }
6186
6187 void
6188 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6189 {
6190         /* Check to see if triggered by a key release event for a repeating key.
6191          * If so the next queued event will be a key press of the same key at the same time */
6192         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6193                 XEvent next;
6194                 XPeekEvent(xDisplay, &next);
6195                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6196                         next.xkey.keycode == event->xkey.keycode)
6197                                 return;
6198         }
6199     ForwardEvent();
6200         TempBackwardActive = False;
6201 }
6202
6203 void
6204 ToStartProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6205 {
6206     ToStartEvent();
6207 }
6208
6209 void
6210 ToEndProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6211 {
6212     ToEndEvent();
6213 }
6214
6215 void
6216 RevertProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6217 {
6218     RevertEvent(False);
6219 }
6220
6221 void
6222 AnnotateProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6223 {
6224     RevertEvent(True);
6225 }
6226
6227 void
6228 TruncateGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6229 {
6230     TruncateGameEvent();
6231 }
6232
6233 void
6234 RetractMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6235 {
6236     RetractMoveEvent();
6237 }
6238
6239 void
6240 MoveNowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6241 {
6242     MoveNowEvent();
6243 }
6244
6245 void
6246 FlipViewProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6247 {
6248     flipView = !flipView;
6249     DrawPosition(True, NULL);
6250 }
6251
6252 void
6253 PonderNextMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6254 {
6255     Arg args[16];
6256
6257     PonderNextMoveEvent(!appData.ponderNextMove);
6258 #ifndef OPTIONSDIALOG
6259     if (appData.ponderNextMove) {
6260         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6261     } else {
6262         XtSetArg(args[0], XtNleftBitmap, None);
6263     }
6264     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6265                 args, 1);
6266 #endif
6267 }
6268
6269 #ifndef OPTIONSDIALOG
6270 void
6271 AlwaysQueenProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6272 {
6273     Arg args[16];
6274
6275     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6276
6277     if (appData.alwaysPromoteToQueen) {
6278         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6279     } else {
6280         XtSetArg(args[0], XtNleftBitmap, None);
6281     }
6282     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6283                 args, 1);
6284 }
6285
6286 void
6287 AnimateDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6288 {
6289     Arg args[16];
6290
6291     appData.animateDragging = !appData.animateDragging;
6292
6293     if (appData.animateDragging) {
6294         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6295         CreateAnimVars();
6296     } else {
6297         XtSetArg(args[0], XtNleftBitmap, None);
6298     }
6299     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6300                 args, 1);
6301 }
6302
6303 void
6304 AnimateMovingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6305 {
6306     Arg args[16];
6307
6308     appData.animate = !appData.animate;
6309
6310     if (appData.animate) {
6311         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6312         CreateAnimVars();
6313     } else {
6314         XtSetArg(args[0], XtNleftBitmap, None);
6315     }
6316     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6317                 args, 1);
6318 }
6319
6320 void
6321 AutoflagProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6322 {
6323     Arg args[16];
6324
6325     appData.autoCallFlag = !appData.autoCallFlag;
6326
6327     if (appData.autoCallFlag) {
6328         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6329     } else {
6330         XtSetArg(args[0], XtNleftBitmap, None);
6331     }
6332     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6333                 args, 1);
6334 }
6335
6336 void
6337 AutoflipProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6338 {
6339     Arg args[16];
6340
6341     appData.autoFlipView = !appData.autoFlipView;
6342
6343     if (appData.autoFlipView) {
6344         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6345     } else {
6346         XtSetArg(args[0], XtNleftBitmap, None);
6347     }
6348     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6349                 args, 1);
6350 }
6351
6352 void
6353 BlindfoldProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6354 {
6355     Arg args[16];
6356
6357     appData.blindfold = !appData.blindfold;
6358
6359     if (appData.blindfold) {
6360         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6361     } else {
6362         XtSetArg(args[0], XtNleftBitmap, None);
6363     }
6364     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6365                 args, 1);
6366
6367     DrawPosition(True, NULL);
6368 }
6369
6370 void
6371 TestLegalityProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6372 {
6373     Arg args[16];
6374
6375     appData.testLegality = !appData.testLegality;
6376
6377     if (appData.testLegality) {
6378         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6379     } else {
6380         XtSetArg(args[0], XtNleftBitmap, None);
6381     }
6382     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6383                 args, 1);
6384 }
6385
6386
6387 void
6388 FlashMovesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6389 {
6390     Arg args[16];
6391
6392     if (appData.flashCount == 0) {
6393         appData.flashCount = 3;
6394     } else {
6395         appData.flashCount = -appData.flashCount;
6396     }
6397
6398     if (appData.flashCount > 0) {
6399         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6400     } else {
6401         XtSetArg(args[0], XtNleftBitmap, None);
6402     }
6403     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6404                 args, 1);
6405 }
6406
6407 #if HIGHDRAG
6408 void
6409 HighlightDraggingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6410 {
6411     Arg args[16];
6412
6413     appData.highlightDragging = !appData.highlightDragging;
6414
6415     if (appData.highlightDragging) {
6416         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6417     } else {
6418         XtSetArg(args[0], XtNleftBitmap, None);
6419     }
6420     XtSetValues(XtNameToWidget(menuBarWidget,
6421                                "menuOptions.Highlight Dragging"), args, 1);
6422 }
6423 #endif
6424
6425 void
6426 HighlightLastMoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6427 {
6428     Arg args[16];
6429
6430     appData.highlightLastMove = !appData.highlightLastMove;
6431
6432     if (appData.highlightLastMove) {
6433         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6434     } else {
6435         XtSetArg(args[0], XtNleftBitmap, None);
6436     }
6437     XtSetValues(XtNameToWidget(menuBarWidget,
6438                                "menuOptions.Highlight Last Move"), args, 1);
6439 }
6440
6441 void
6442 HighlightArrowProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6443 {
6444     Arg args[16];
6445
6446     appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6447
6448     if (appData.highlightMoveWithArrow) {
6449         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6450     } else {
6451         XtSetArg(args[0], XtNleftBitmap, None);
6452     }
6453     XtSetValues(XtNameToWidget(menuBarWidget,
6454                                "menuOptions.Arrow"), args, 1);
6455 }
6456
6457 #if 0
6458 void
6459 IcsAlarmProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6460 {
6461     Arg args[16];
6462
6463     appData.icsAlarm = !appData.icsAlarm;
6464
6465     if (appData.icsAlarm) {
6466         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6467     } else {
6468         XtSetArg(args[0], XtNleftBitmap, None);
6469     }
6470     XtSetValues(XtNameToWidget(menuBarWidget,
6471                                "menuOptions.ICS Alarm"), args, 1);
6472 }
6473 #endif
6474
6475 void
6476 MoveSoundProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6477 {
6478     Arg args[16];
6479
6480     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6481
6482     if (appData.ringBellAfterMoves) {
6483         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6484     } else {
6485         XtSetArg(args[0], XtNleftBitmap, None);
6486     }
6487     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6488                 args, 1);
6489 }
6490
6491 void
6492 OneClickProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6493 {
6494     Arg args[16];
6495
6496     appData.oneClick = !appData.oneClick;
6497
6498     if (appData.oneClick) {
6499         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6500     } else {
6501         XtSetArg(args[0], XtNleftBitmap, None);
6502     }
6503     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6504                 args, 1);
6505 }
6506
6507 void
6508 PeriodicUpdatesProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6509 {
6510     Arg args[16];
6511
6512     PeriodicUpdatesEvent(!appData.periodicUpdates);
6513
6514     if (appData.periodicUpdates) {
6515         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6516     } else {
6517         XtSetArg(args[0], XtNleftBitmap, None);
6518     }
6519     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6520                 args, 1);
6521 }
6522
6523 void
6524 PopupExitMessageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6525 {
6526     Arg args[16];
6527
6528     appData.popupExitMessage = !appData.popupExitMessage;
6529
6530     if (appData.popupExitMessage) {
6531         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6532     } else {
6533         XtSetArg(args[0], XtNleftBitmap, None);
6534     }
6535     XtSetValues(XtNameToWidget(menuBarWidget,
6536                                "menuOptions.Popup Exit Message"), args, 1);
6537 }
6538
6539 void
6540 PopupMoveErrorsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6541 {
6542     Arg args[16];
6543
6544     appData.popupMoveErrors = !appData.popupMoveErrors;
6545
6546     if (appData.popupMoveErrors) {
6547         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6548     } else {
6549         XtSetArg(args[0], XtNleftBitmap, None);
6550     }
6551     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6552                 args, 1);
6553 }
6554
6555 #if 0
6556 void
6557 PremoveProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6558 {
6559     Arg args[16];
6560
6561     appData.premove = !appData.premove;
6562
6563     if (appData.premove) {
6564         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6565     } else {
6566         XtSetArg(args[0], XtNleftBitmap, None);
6567     }
6568     XtSetValues(XtNameToWidget(menuBarWidget,
6569                                "menuOptions.Premove"), args, 1);
6570 }
6571 #endif
6572
6573 void
6574 ShowCoordsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6575 {
6576     Arg args[16];
6577
6578     appData.showCoords = !appData.showCoords;
6579
6580     if (appData.showCoords) {
6581         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6582     } else {
6583         XtSetArg(args[0], XtNleftBitmap, None);
6584     }
6585     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6586                 args, 1);
6587
6588     DrawPosition(True, NULL);
6589 }
6590
6591 void
6592 ShowThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6593 {
6594     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6595     ShowThinkingEvent();
6596 }
6597
6598 void
6599 HideThinkingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6600 {
6601     Arg args[16];
6602
6603     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6604     ShowThinkingEvent();
6605
6606     if (appData.hideThinkingFromHuman) {
6607         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6608     } else {
6609         XtSetArg(args[0], XtNleftBitmap, None);
6610     }
6611     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6612                 args, 1);
6613 }
6614 #endif
6615
6616 void
6617 SaveOnExitProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6618 {
6619     Arg args[16];
6620
6621     saveSettingsOnExit = !saveSettingsOnExit;
6622
6623     if (saveSettingsOnExit) {
6624         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6625     } else {
6626         XtSetArg(args[0], XtNleftBitmap, None);
6627     }
6628     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6629                 args, 1);
6630 }
6631
6632 void
6633 SaveSettingsProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6634 {
6635      SaveSettings(settingsFileName);
6636 }
6637
6638 void
6639 InfoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6640 {
6641     char buf[MSG_SIZ];
6642     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6643             INFODIR, INFOFILE);
6644     system(buf);
6645 }
6646
6647 void
6648 ManProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6649 {
6650     char buf[MSG_SIZ];
6651     String name;
6652     if (nprms && *nprms > 0)
6653       name = prms[0];
6654     else
6655       name = "xboard";
6656     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6657     system(buf);
6658 }
6659
6660 void
6661 HintProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6662 {
6663     HintEvent();
6664 }
6665
6666 void
6667 BookProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6668 {
6669     BookEvent();
6670 }
6671
6672 void
6673 BugReportProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6674 {
6675     char buf[MSG_SIZ];
6676     snprintf(buf, MSG_SIZ, "%s mailto:bug-xboard@gnu.org", appData.sysOpen);
6677     system(buf);
6678 }
6679
6680 void
6681 GuideProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6682 {
6683     char buf[MSG_SIZ];
6684     snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/user_guide/UserGuide.html", appData.sysOpen);
6685     system(buf);
6686 }
6687
6688 void
6689 HomePageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6690 {
6691     char buf[MSG_SIZ];
6692     snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/", appData.sysOpen);
6693     system(buf);
6694 }
6695
6696 void
6697 NewsPageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6698 {
6699     char buf[MSG_SIZ];
6700     snprintf(buf, MSG_SIZ, "%s http://www.gnu.org/software/xboard/whats_new/portal.html", appData.sysOpen);
6701     system(buf);
6702 }
6703
6704 void
6705 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6706 {
6707     char buf[2 * MSG_SIZ];
6708 #if ZIPPY
6709     char *zippy = _(" (with Zippy code)");
6710 #else
6711     char *zippy = "";
6712 #endif
6713     snprintf(buf, sizeof(buf), 
6714 _("%s%s\n\n"
6715 "Copyright 1991 Digital Equipment Corporation\n"
6716 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6717 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6718 "%s is free software and carries NO WARRANTY;"
6719 "see the file COPYING for more information.\n\n"
6720 "Visit XBoard on the web at: http://www.gnu.org/software/xboard/\n"
6721 "Check out the newest features at: http://www.gnu.org/software/xboard/whats_new.html\n\n"
6722 "Report bugs via email at: <bug-xboard@gnu.org>\n\n"
6723   ),
6724             programVersion, zippy, PACKAGE);
6725     ErrorPopUp(_("About XBoard"), buf, FALSE);
6726 }
6727
6728 void
6729 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6730 {
6731     appData.debugMode = !appData.debugMode;
6732 }
6733
6734 void
6735 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6736 {
6737     AboutGameEvent();
6738 }
6739
6740 void
6741 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6742 {
6743     return;
6744 }
6745
6746 void
6747 DisplayMessage (char *message, char *extMessage)
6748 {
6749   /* display a message in the message widget */
6750
6751   char buf[MSG_SIZ];
6752   Arg arg;
6753
6754   if (extMessage)
6755     {
6756       if (*message)
6757         {
6758           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
6759           message = buf;
6760         }
6761       else
6762         {
6763           message = extMessage;
6764         };
6765     };
6766
6767     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6768
6769   /* need to test if messageWidget already exists, since this function
6770      can also be called during the startup, if for example a Xresource
6771      is not set up correctly */
6772   if(messageWidget)
6773     {
6774       XtSetArg(arg, XtNlabel, message);
6775       XtSetValues(messageWidget, &arg, 1);
6776     };
6777
6778   return;
6779 }
6780
6781 void
6782 DisplayTitle (char *text)
6783 {
6784     Arg args[16];
6785     int i;
6786     char title[MSG_SIZ];
6787     char icon[MSG_SIZ];
6788
6789     if (text == NULL) text = "";
6790
6791     if (appData.titleInWindow) {
6792         i = 0;
6793         XtSetArg(args[i], XtNlabel, text);   i++;
6794         XtSetValues(titleWidget, args, i);
6795     }
6796
6797     if (*text != NULLCHAR) {
6798       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6799       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6800     } else if (appData.icsActive) {
6801         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6802         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6803     } else if (appData.cmailGameName[0] != NULLCHAR) {
6804         snprintf(icon, sizeof(icon), "%s", "CMail");
6805         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6806 #ifdef GOTHIC
6807     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6808     } else if (gameInfo.variant == VariantGothic) {
6809       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
6810       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
6811 #endif
6812 #ifdef FALCON
6813     } else if (gameInfo.variant == VariantFalcon) {
6814       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6815       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6816 #endif
6817     } else if (appData.noChessProgram) {
6818       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6819       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6820     } else {
6821       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6822         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6823     }
6824     i = 0;
6825     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6826     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6827     XtSetValues(shellWidget, args, i);
6828     XSync(xDisplay, False);
6829 }
6830
6831
6832 void
6833 DisplayError (String message, int error)
6834 {
6835     char buf[MSG_SIZ];
6836
6837     if (error == 0) {
6838         if (appData.debugMode || appData.matchMode) {
6839             fprintf(stderr, "%s: %s\n", programName, message);
6840         }
6841     } else {
6842         if (appData.debugMode || appData.matchMode) {
6843             fprintf(stderr, "%s: %s: %s\n",
6844                     programName, message, strerror(error));
6845         }
6846         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6847         message = buf;
6848     }
6849     ErrorPopUp(_("Error"), message, FALSE);
6850 }
6851
6852
6853 void
6854 DisplayMoveError (String message)
6855 {
6856     fromX = fromY = -1;
6857     ClearHighlights();
6858     DrawPosition(FALSE, NULL);
6859     if (appData.debugMode || appData.matchMode) {
6860         fprintf(stderr, "%s: %s\n", programName, message);
6861     }
6862     if (appData.popupMoveErrors) {
6863         ErrorPopUp(_("Error"), message, FALSE);
6864     } else {
6865         DisplayMessage(message, "");
6866     }
6867 }
6868
6869
6870 void
6871 DisplayFatalError (String message, int error, int status)
6872 {
6873     char buf[MSG_SIZ];
6874
6875     errorExitStatus = status;
6876     if (error == 0) {
6877         fprintf(stderr, "%s: %s\n", programName, message);
6878     } else {
6879         fprintf(stderr, "%s: %s: %s\n",
6880                 programName, message, strerror(error));
6881         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6882         message = buf;
6883     }
6884     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6885       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6886     } else {
6887       ExitEvent(status);
6888     }
6889 }
6890
6891 void
6892 DisplayInformation (String message)
6893 {
6894     ErrorPopDown();
6895     ErrorPopUp(_("Information"), message, TRUE);
6896 }
6897
6898 void
6899 DisplayNote (String message)
6900 {
6901     ErrorPopDown();
6902     ErrorPopUp(_("Note"), message, FALSE);
6903 }
6904
6905 static int
6906 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6907 {
6908     return 0;
6909 }
6910
6911 void
6912 DisplayIcsInteractionTitle (String message)
6913 {
6914   if (oldICSInteractionTitle == NULL) {
6915     /* Magic to find the old window title, adapted from vim */
6916     char *wina = getenv("WINDOWID");
6917     if (wina != NULL) {
6918       Window win = (Window) atoi(wina);
6919       Window root, parent, *children;
6920       unsigned int nchildren;
6921       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6922       for (;;) {
6923         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6924         if (!XQueryTree(xDisplay, win, &root, &parent,
6925                         &children, &nchildren)) break;
6926         if (children) XFree((void *)children);
6927         if (parent == root || parent == 0) break;
6928         win = parent;
6929       }
6930       XSetErrorHandler(oldHandler);
6931     }
6932     if (oldICSInteractionTitle == NULL) {
6933       oldICSInteractionTitle = "xterm";
6934     }
6935   }
6936   printf("\033]0;%s\007", message);
6937   fflush(stdout);
6938 }
6939
6940 char pendingReplyPrefix[MSG_SIZ];
6941 ProcRef pendingReplyPR;
6942
6943 void
6944 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6945 {
6946     if (*nprms != 4) {
6947         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6948                 *nprms);
6949         return;
6950     }
6951     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6952 }
6953
6954 void
6955 AskQuestionPopDown ()
6956 {
6957     if (!askQuestionUp) return;
6958     XtPopdown(askQuestionShell);
6959     XtDestroyWidget(askQuestionShell);
6960     askQuestionUp = False;
6961 }
6962
6963 void
6964 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6965 {
6966     char buf[MSG_SIZ];
6967     int err;
6968     String reply;
6969
6970     reply = XawDialogGetValueString(w = XtParent(w));
6971     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6972     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6973     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6974     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
6975     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6976     AskQuestionPopDown();
6977
6978     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6979 }
6980
6981 void
6982 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6983 {
6984     String name;
6985     Arg args[16];
6986
6987     XtSetArg(args[0], XtNlabel, &name);
6988     XtGetValues(w, args, 1);
6989
6990     if (strcmp(name, _("cancel")) == 0) {
6991         AskQuestionPopDown();
6992     } else {
6993         AskQuestionReplyAction(w, NULL, NULL, NULL);
6994     }
6995 }
6996
6997 void
6998 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6999 {
7000     Arg args[16];
7001     Widget popup, layout, dialog, edit;
7002     Window root, child;
7003     int x, y, i;
7004     int win_x, win_y;
7005     unsigned int mask;
7006
7007     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7008     pendingReplyPR = pr;
7009
7010     i = 0;
7011     XtSetArg(args[i], XtNresizable, True); i++;
7012     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7013     askQuestionShell = popup =
7014       XtCreatePopupShell(title, transientShellWidgetClass,
7015                          shellWidget, args, i);
7016
7017     layout =
7018       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7019                             layoutArgs, XtNumber(layoutArgs));
7020
7021     i = 0;
7022     XtSetArg(args[i], XtNlabel, question); i++;
7023     XtSetArg(args[i], XtNvalue, ""); i++;
7024     XtSetArg(args[i], XtNborderWidth, 0); i++;
7025     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7026                                    layout, args, i);
7027
7028     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7029                        (XtPointer) dialog);
7030     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7031                        (XtPointer) dialog);
7032
7033     XtRealizeWidget(popup);
7034     CatchDeleteWindow(popup, "AskQuestionPopDown");
7035
7036     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7037                   &x, &y, &win_x, &win_y, &mask);
7038
7039     XtSetArg(args[0], XtNx, x - 10);
7040     XtSetArg(args[1], XtNy, y - 30);
7041     XtSetValues(popup, args, 2);
7042
7043     XtPopup(popup, XtGrabExclusive);
7044     askQuestionUp = True;
7045
7046     edit = XtNameToWidget(dialog, "*value");
7047     XtSetKeyboardFocus(popup, edit);
7048 }
7049
7050
7051 void
7052 PlaySound (char *name)
7053 {
7054   if (*name == NULLCHAR) {
7055     return;
7056   } else if (strcmp(name, "$") == 0) {
7057     putc(BELLCHAR, stderr);
7058   } else {
7059     char buf[2048];
7060     char *prefix = "", *sep = "";
7061     if(appData.soundProgram[0] == NULLCHAR) return;
7062     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7063     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7064     system(buf);
7065   }
7066 }
7067
7068 void
7069 RingBell ()
7070 {
7071   PlaySound(appData.soundMove);
7072 }
7073
7074 void
7075 PlayIcsWinSound ()
7076 {
7077   PlaySound(appData.soundIcsWin);
7078 }
7079
7080 void
7081 PlayIcsLossSound ()
7082 {
7083   PlaySound(appData.soundIcsLoss);
7084 }
7085
7086 void
7087 PlayIcsDrawSound ()
7088 {
7089   PlaySound(appData.soundIcsDraw);
7090 }
7091
7092 void
7093 PlayIcsUnfinishedSound ()
7094 {
7095   PlaySound(appData.soundIcsUnfinished);
7096 }
7097
7098 void
7099 PlayAlarmSound ()
7100 {
7101   PlaySound(appData.soundIcsAlarm);
7102 }
7103
7104 void
7105 PlayTellSound ()
7106 {
7107   PlaySound(appData.soundTell);
7108 }
7109
7110 void
7111 EchoOn ()
7112 {
7113     system("stty echo");
7114     noEcho = False;
7115 }
7116
7117 void
7118 EchoOff ()
7119 {
7120     system("stty -echo");
7121     noEcho = True;
7122 }
7123
7124 void
7125 RunCommand (char *buf)
7126 {
7127     system(buf);
7128 }
7129
7130 void
7131 Colorize (ColorClass cc, int continuation)
7132 {
7133     char buf[MSG_SIZ];
7134     int count, outCount, error;
7135
7136     if (textColors[(int)cc].bg > 0) {
7137         if (textColors[(int)cc].fg > 0) {
7138           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7139                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7140         } else {
7141           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7142                    textColors[(int)cc].bg);
7143         }
7144     } else {
7145         if (textColors[(int)cc].fg > 0) {
7146           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7147                     textColors[(int)cc].fg);
7148         } else {
7149           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7150         }
7151     }
7152     count = strlen(buf);
7153     outCount = OutputToProcess(NoProc, buf, count, &error);
7154     if (outCount < count) {
7155         DisplayFatalError(_("Error writing to display"), error, 1);
7156     }
7157
7158     if (continuation) return;
7159     switch (cc) {
7160     case ColorShout:
7161       PlaySound(appData.soundShout);
7162       break;
7163     case ColorSShout:
7164       PlaySound(appData.soundSShout);
7165       break;
7166     case ColorChannel1:
7167       PlaySound(appData.soundChannel1);
7168       break;
7169     case ColorChannel:
7170       PlaySound(appData.soundChannel);
7171       break;
7172     case ColorKibitz:
7173       PlaySound(appData.soundKibitz);
7174       break;
7175     case ColorTell:
7176       PlaySound(appData.soundTell);
7177       break;
7178     case ColorChallenge:
7179       PlaySound(appData.soundChallenge);
7180       break;
7181     case ColorRequest:
7182       PlaySound(appData.soundRequest);
7183       break;
7184     case ColorSeek:
7185       PlaySound(appData.soundSeek);
7186       break;
7187     case ColorNormal:
7188     case ColorNone:
7189     default:
7190       break;
7191     }
7192 }
7193
7194 char *
7195 UserName ()
7196 {
7197     return getpwuid(getuid())->pw_name;
7198 }
7199
7200 static char *
7201 ExpandPathName (char *path)
7202 {
7203     static char static_buf[4*MSG_SIZ];
7204     char *d, *s, buf[4*MSG_SIZ];
7205     struct passwd *pwd;
7206
7207     s = path;
7208     d = static_buf;
7209
7210     while (*s && isspace(*s))
7211       ++s;
7212
7213     if (!*s) {
7214         *d = 0;
7215         return static_buf;
7216     }
7217
7218     if (*s == '~') {
7219         if (*(s+1) == '/') {
7220           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7221           strcat(d, s+1);
7222         }
7223         else {
7224           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7225           { char *p; if(p = strchr(buf, '/')) *p = 0; }
7226           pwd = getpwnam(buf);
7227           if (!pwd)
7228             {
7229               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7230                       buf, path);
7231               return NULL;
7232             }
7233           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7234           strcat(d, strchr(s+1, '/'));
7235         }
7236     }
7237     else
7238       safeStrCpy(d, s, 4*MSG_SIZ );
7239
7240     return static_buf;
7241 }
7242
7243 char *
7244 HostName ()
7245 {
7246     static char host_name[MSG_SIZ];
7247
7248 #if HAVE_GETHOSTNAME
7249     gethostname(host_name, MSG_SIZ);
7250     return host_name;
7251 #else  /* not HAVE_GETHOSTNAME */
7252 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7253     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7254     return host_name;
7255 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7256     return "localhost";
7257 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7258 #endif /* not HAVE_GETHOSTNAME */
7259 }
7260
7261 XtIntervalId delayedEventTimerXID = 0;
7262 DelayedEventCallback delayedEventCallback = 0;
7263
7264 void
7265 FireDelayedEvent ()
7266 {
7267     delayedEventTimerXID = 0;
7268     delayedEventCallback();
7269 }
7270
7271 void
7272 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7273 {
7274     if(delayedEventTimerXID && delayedEventCallback == cb)
7275         // [HGM] alive: replace, rather than add or flush identical event
7276         XtRemoveTimeOut(delayedEventTimerXID);
7277     delayedEventCallback = cb;
7278     delayedEventTimerXID =
7279       XtAppAddTimeOut(appContext, millisec,
7280                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7281 }
7282
7283 DelayedEventCallback
7284 GetDelayedEvent ()
7285 {
7286   if (delayedEventTimerXID) {
7287     return delayedEventCallback;
7288   } else {
7289     return NULL;
7290   }
7291 }
7292
7293 void
7294 CancelDelayedEvent ()
7295 {
7296   if (delayedEventTimerXID) {
7297     XtRemoveTimeOut(delayedEventTimerXID);
7298     delayedEventTimerXID = 0;
7299   }
7300 }
7301
7302 XtIntervalId loadGameTimerXID = 0;
7303
7304 int
7305 LoadGameTimerRunning ()
7306 {
7307     return loadGameTimerXID != 0;
7308 }
7309
7310 int
7311 StopLoadGameTimer ()
7312 {
7313     if (loadGameTimerXID != 0) {
7314         XtRemoveTimeOut(loadGameTimerXID);
7315         loadGameTimerXID = 0;
7316         return TRUE;
7317     } else {
7318         return FALSE;
7319     }
7320 }
7321
7322 void
7323 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7324 {
7325     loadGameTimerXID = 0;
7326     AutoPlayGameLoop();
7327 }
7328
7329 void
7330 StartLoadGameTimer (long millisec)
7331 {
7332     loadGameTimerXID =
7333       XtAppAddTimeOut(appContext, millisec,
7334                       (XtTimerCallbackProc) LoadGameTimerCallback,
7335                       (XtPointer) 0);
7336 }
7337
7338 XtIntervalId analysisClockXID = 0;
7339
7340 void
7341 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7342 {
7343     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7344          || appData.icsEngineAnalyze) { // [DM]
7345         AnalysisPeriodicEvent(0);
7346         StartAnalysisClock();
7347     }
7348 }
7349
7350 void
7351 StartAnalysisClock ()
7352 {
7353     analysisClockXID =
7354       XtAppAddTimeOut(appContext, 2000,
7355                       (XtTimerCallbackProc) AnalysisClockCallback,
7356                       (XtPointer) 0);
7357 }
7358
7359 XtIntervalId clockTimerXID = 0;
7360
7361 int
7362 ClockTimerRunning ()
7363 {
7364     return clockTimerXID != 0;
7365 }
7366
7367 int
7368 StopClockTimer ()
7369 {
7370     if (clockTimerXID != 0) {
7371         XtRemoveTimeOut(clockTimerXID);
7372         clockTimerXID = 0;
7373         return TRUE;
7374     } else {
7375         return FALSE;
7376     }
7377 }
7378
7379 void
7380 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7381 {
7382     clockTimerXID = 0;
7383     DecrementClocks();
7384 }
7385
7386 void
7387 StartClockTimer (long millisec)
7388 {
7389     clockTimerXID =
7390       XtAppAddTimeOut(appContext, millisec,
7391                       (XtTimerCallbackProc) ClockTimerCallback,
7392                       (XtPointer) 0);
7393 }
7394
7395 void
7396 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7397 {
7398     char buf[MSG_SIZ];
7399     Arg args[16];
7400
7401     /* check for low time warning */
7402     Pixel foregroundOrWarningColor = timerForegroundPixel;
7403
7404     if (timer > 0 &&
7405         appData.lowTimeWarning &&
7406         (timer / 1000) < appData.icsAlarmTime)
7407       foregroundOrWarningColor = lowTimeWarningColor;
7408
7409     if (appData.clockMode) {
7410       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7411       XtSetArg(args[0], XtNlabel, buf);
7412     } else {
7413       snprintf(buf, MSG_SIZ, "%s  ", color);
7414       XtSetArg(args[0], XtNlabel, buf);
7415     }
7416
7417     if (highlight) {
7418
7419         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7420         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7421     } else {
7422         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7423         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7424     }
7425
7426     XtSetValues(w, args, 3);
7427 }
7428
7429 void
7430 DisplayWhiteClock (long timeRemaining, int highlight)
7431 {
7432     Arg args[16];
7433
7434     if(appData.noGUI) return;
7435     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7436     if (highlight && iconPixmap == bIconPixmap) {
7437         iconPixmap = wIconPixmap;
7438         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7439         XtSetValues(shellWidget, args, 1);
7440     }
7441 }
7442
7443 void
7444 DisplayBlackClock (long timeRemaining, int highlight)
7445 {
7446     Arg args[16];
7447
7448     if(appData.noGUI) return;
7449     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7450     if (highlight && iconPixmap == wIconPixmap) {
7451         iconPixmap = bIconPixmap;
7452         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7453         XtSetValues(shellWidget, args, 1);
7454     }
7455 }
7456
7457 #define CPNone 0
7458 #define CPReal 1
7459 #define CPComm 2
7460 #define CPSock 3
7461 #define CPLoop 4
7462 typedef int CPKind;
7463
7464 typedef struct {
7465     CPKind kind;
7466     int pid;
7467     int fdTo, fdFrom;
7468 } ChildProc;
7469
7470
7471 int
7472 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7473 {
7474     char *argv[64], *p;
7475     int i, pid;
7476     int to_prog[2], from_prog[2];
7477     ChildProc *cp;
7478     char buf[MSG_SIZ];
7479
7480     if (appData.debugMode) {
7481         fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7482     }
7483
7484     /* We do NOT feed the cmdLine to the shell; we just
7485        parse it into blank-separated arguments in the
7486        most simple-minded way possible.
7487        */
7488     i = 0;
7489     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7490     p = buf;
7491     for (;;) {
7492         while(*p == ' ') p++;
7493         argv[i++] = p;
7494         if(*p == '"' || *p == '\'')
7495              p = strchr(++argv[i-1], *p);
7496         else p = strchr(p, ' ');
7497         if (p == NULL) break;
7498         *p++ = NULLCHAR;
7499     }
7500     argv[i] = NULL;
7501
7502     SetUpChildIO(to_prog, from_prog);
7503
7504     if ((pid = fork()) == 0) {
7505         /* Child process */
7506         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7507         close(to_prog[1]);     // first close the unused pipe ends
7508         close(from_prog[0]);
7509         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7510         dup2(from_prog[1], 1);
7511         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7512         close(from_prog[1]);                   // and closing again loses one of the pipes!
7513         if(fileno(stderr) >= 2) // better safe than sorry...
7514                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7515
7516         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7517             perror(dir);
7518             exit(1);
7519         }
7520
7521         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7522
7523         execvp(argv[0], argv);
7524
7525         /* If we get here, exec failed */
7526         perror(argv[0]);
7527         exit(1);
7528     }
7529
7530     /* Parent process */
7531     close(to_prog[0]);
7532     close(from_prog[1]);
7533
7534     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7535     cp->kind = CPReal;
7536     cp->pid = pid;
7537     cp->fdFrom = from_prog[0];
7538     cp->fdTo = to_prog[1];
7539     *pr = (ProcRef) cp;
7540     return 0;
7541 }
7542
7543 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7544 static RETSIGTYPE
7545 AlarmCallBack (int n)
7546 {
7547     return;
7548 }
7549
7550 void
7551 DestroyChildProcess (ProcRef pr, int signalType)
7552 {
7553     ChildProc *cp = (ChildProc *) pr;
7554
7555     if (cp->kind != CPReal) return;
7556     cp->kind = CPNone;
7557     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7558         signal(SIGALRM, AlarmCallBack);
7559         alarm(3);
7560         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7561             kill(cp->pid, SIGKILL); // kill it forcefully
7562             wait((int *) 0);        // and wait again
7563         }
7564     } else {
7565         if (signalType) {
7566             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7567         }
7568         /* Process is exiting either because of the kill or because of
7569            a quit command sent by the backend; either way, wait for it to die.
7570         */
7571         wait((int *) 0);
7572     }
7573     close(cp->fdFrom);
7574     close(cp->fdTo);
7575 }
7576
7577 void
7578 InterruptChildProcess (ProcRef pr)
7579 {
7580     ChildProc *cp = (ChildProc *) pr;
7581
7582     if (cp->kind != CPReal) return;
7583     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7584 }
7585
7586 int
7587 OpenTelnet (char *host, char *port, ProcRef *pr)
7588 {
7589     char cmdLine[MSG_SIZ];
7590
7591     if (port[0] == NULLCHAR) {
7592       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7593     } else {
7594       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7595     }
7596     return StartChildProcess(cmdLine, "", pr);
7597 }
7598
7599 int
7600 OpenTCP (char *host, char *port, ProcRef *pr)
7601 {
7602 #if OMIT_SOCKETS
7603     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7604 #else  /* !OMIT_SOCKETS */
7605     struct addrinfo hints;
7606     struct addrinfo *ais, *ai;
7607     int error;
7608     int s=0;
7609     ChildProc *cp;
7610
7611     memset(&hints, 0, sizeof(hints));
7612     hints.ai_family = AF_UNSPEC;
7613     hints.ai_socktype = SOCK_STREAM;
7614
7615     error = getaddrinfo(host, port, &hints, &ais);
7616     if (error != 0) {
7617       /* a getaddrinfo error is not an errno, so can't return it */
7618       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7619               host, port, gai_strerror(error));
7620       return ENOENT;
7621     }
7622      
7623     for (ai = ais; ai != NULL; ai = ai->ai_next) {
7624       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7625         error = errno;
7626         continue;
7627       }
7628       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7629         error = errno;
7630         continue;
7631       }
7632       error = 0;
7633       break;
7634     }
7635     freeaddrinfo(ais);
7636
7637     if (error != 0) {
7638       return error;
7639     }
7640
7641     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7642     cp->kind = CPSock;
7643     cp->pid = 0;
7644     cp->fdFrom = s;
7645     cp->fdTo = s;
7646     *pr = (ProcRef) cp;
7647 #endif /* !OMIT_SOCKETS */
7648
7649     return 0;
7650 }
7651
7652 int
7653 OpenCommPort (char *name, ProcRef *pr)
7654 {
7655     int fd;
7656     ChildProc *cp;
7657
7658     fd = open(name, 2, 0);
7659     if (fd < 0) return errno;
7660
7661     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7662     cp->kind = CPComm;
7663     cp->pid = 0;
7664     cp->fdFrom = fd;
7665     cp->fdTo = fd;
7666     *pr = (ProcRef) cp;
7667
7668     return 0;
7669 }
7670
7671 int
7672 OpenLoopback (ProcRef *pr)
7673 {
7674     ChildProc *cp;
7675     int to[2], from[2];
7676
7677     SetUpChildIO(to, from);
7678
7679     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7680     cp->kind = CPLoop;
7681     cp->pid = 0;
7682     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7683     cp->fdTo = to[1];
7684     *pr = (ProcRef) cp;
7685
7686     return 0;
7687 }
7688
7689 int
7690 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7691 {
7692     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7693     return -1;
7694 }
7695
7696 #define INPUT_SOURCE_BUF_SIZE 8192
7697
7698 typedef struct {
7699     CPKind kind;
7700     int fd;
7701     int lineByLine;
7702     char *unused;
7703     InputCallback func;
7704     XtInputId xid;
7705     char buf[INPUT_SOURCE_BUF_SIZE];
7706     VOIDSTAR closure;
7707 } InputSource;
7708
7709 void
7710 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7711 {
7712     InputSource *is = (InputSource *) closure;
7713     int count;
7714     int error;
7715     char *p, *q;
7716
7717     if (is->lineByLine) {
7718         count = read(is->fd, is->unused,
7719                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7720         if (count <= 0) {
7721             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7722             return;
7723         }
7724         is->unused += count;
7725         p = is->buf;
7726         while (p < is->unused) {
7727             q = memchr(p, '\n', is->unused - p);
7728             if (q == NULL) break;
7729             q++;
7730             (is->func)(is, is->closure, p, q - p, 0);
7731             p = q;
7732         }
7733         q = is->buf;
7734         while (p < is->unused) {
7735             *q++ = *p++;
7736         }
7737         is->unused = q;
7738     } else {
7739         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7740         if (count == -1)
7741           error = errno;
7742         else
7743           error = 0;
7744         (is->func)(is, is->closure, is->buf, count, error);
7745     }
7746 }
7747
7748 InputSourceRef
7749 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7750 {
7751     InputSource *is;
7752     ChildProc *cp = (ChildProc *) pr;
7753
7754     is = (InputSource *) calloc(1, sizeof(InputSource));
7755     is->lineByLine = lineByLine;
7756     is->func = func;
7757     if (pr == NoProc) {
7758         is->kind = CPReal;
7759         is->fd = fileno(stdin);
7760     } else {
7761         is->kind = cp->kind;
7762         is->fd = cp->fdFrom;
7763     }
7764     if (lineByLine) {
7765         is->unused = is->buf;
7766     }
7767
7768     is->xid = XtAppAddInput(appContext, is->fd,
7769                             (XtPointer) (XtInputReadMask),
7770                             (XtInputCallbackProc) DoInputCallback,
7771                             (XtPointer) is);
7772     is->closure = closure;
7773     return (InputSourceRef) is;
7774 }
7775
7776 void
7777 RemoveInputSource (InputSourceRef isr)
7778 {
7779     InputSource *is = (InputSource *) isr;
7780
7781     if (is->xid == 0) return;
7782     XtRemoveInput(is->xid);
7783     is->xid = 0;
7784 }
7785
7786 int
7787 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7788 {
7789     static int line = 0;
7790     ChildProc *cp = (ChildProc *) pr;
7791     int outCount;
7792
7793     if (pr == NoProc)
7794     {
7795         if (appData.noJoin || !appData.useInternalWrap)
7796             outCount = fwrite(message, 1, count, stdout);
7797         else
7798         {
7799             int width = get_term_width();
7800             int len = wrap(NULL, message, count, width, &line);
7801             char *msg = malloc(len);
7802             int dbgchk;
7803
7804             if (!msg)
7805                 outCount = fwrite(message, 1, count, stdout);
7806             else
7807             {
7808                 dbgchk = wrap(msg, message, count, width, &line);
7809                 if (dbgchk != len && appData.debugMode)
7810                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7811                 outCount = fwrite(msg, 1, dbgchk, stdout);
7812                 free(msg);
7813             }
7814         }
7815     }
7816     else
7817       outCount = write(cp->fdTo, message, count);
7818
7819     if (outCount == -1)
7820       *outError = errno;
7821     else
7822       *outError = 0;
7823
7824     return outCount;
7825 }
7826
7827 /* Output message to process, with "ms" milliseconds of delay
7828    between each character. This is needed when sending the logon
7829    script to ICC, which for some reason doesn't like the
7830    instantaneous send. */
7831 int
7832 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7833 {
7834     ChildProc *cp = (ChildProc *) pr;
7835     int outCount = 0;
7836     int r;
7837
7838     while (count--) {
7839         r = write(cp->fdTo, message++, 1);
7840         if (r == -1) {
7841             *outError = errno;
7842             return outCount;
7843         }
7844         ++outCount;
7845         if (msdelay >= 0)
7846           TimeDelay(msdelay);
7847     }
7848
7849     return outCount;
7850 }
7851
7852 /****   Animation code by Hugh Fisher, DCS, ANU.
7853
7854         Known problem: if a window overlapping the board is
7855         moved away while a piece is being animated underneath,
7856         the newly exposed area won't be updated properly.
7857         I can live with this.
7858
7859         Known problem: if you look carefully at the animation
7860         of pieces in mono mode, they are being drawn as solid
7861         shapes without interior detail while moving. Fixing
7862         this would be a major complication for minimal return.
7863 ****/
7864
7865 /*      Masks for XPM pieces. Black and white pieces can have
7866         different shapes, but in the interest of retaining my
7867         sanity pieces must have the same outline on both light
7868         and dark squares, and all pieces must use the same
7869         background square colors/images.                */
7870
7871 static int xpmDone = 0;
7872
7873 static void
7874 CreateAnimMasks (int pieceDepth)
7875 {
7876   ChessSquare   piece;
7877   Pixmap        buf;
7878   GC            bufGC, maskGC;
7879   int           kind, n;
7880   unsigned long plane;
7881   XGCValues     values;
7882
7883   /* Need a bitmap just to get a GC with right depth */
7884   buf = XCreatePixmap(xDisplay, xBoardWindow,
7885                         8, 8, 1);
7886   values.foreground = 1;
7887   values.background = 0;
7888   /* Don't use XtGetGC, not read only */
7889   maskGC = XCreateGC(xDisplay, buf,
7890                     GCForeground | GCBackground, &values);
7891   XFreePixmap(xDisplay, buf);
7892
7893   buf = XCreatePixmap(xDisplay, xBoardWindow,
7894                       squareSize, squareSize, pieceDepth);
7895   values.foreground = XBlackPixel(xDisplay, xScreen);
7896   values.background = XWhitePixel(xDisplay, xScreen);
7897   bufGC = XCreateGC(xDisplay, buf,
7898                     GCForeground | GCBackground, &values);
7899
7900   for (piece = WhitePawn; piece <= BlackKing; piece++) {
7901     /* Begin with empty mask */
7902     if(!xpmDone) // [HGM] pieces: keep using existing
7903     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7904                                  squareSize, squareSize, 1);
7905     XSetFunction(xDisplay, maskGC, GXclear);
7906     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7907                    0, 0, squareSize, squareSize);
7908
7909     /* Take a copy of the piece */
7910     if (White(piece))
7911       kind = 0;
7912     else
7913       kind = 2;
7914     XSetFunction(xDisplay, bufGC, GXcopy);
7915     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7916               buf, bufGC,
7917               0, 0, squareSize, squareSize, 0, 0);
7918
7919     /* XOR the background (light) over the piece */
7920     XSetFunction(xDisplay, bufGC, GXxor);
7921     if (useImageSqs)
7922       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7923                 0, 0, squareSize, squareSize, 0, 0);
7924     else {
7925       XSetForeground(xDisplay, bufGC, lightSquareColor);
7926       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7927     }
7928
7929     /* We now have an inverted piece image with the background
7930        erased. Construct mask by just selecting all the non-zero
7931        pixels - no need to reconstruct the original image.      */
7932     XSetFunction(xDisplay, maskGC, GXor);
7933     plane = 1;
7934     /* Might be quicker to download an XImage and create bitmap
7935        data from it rather than this N copies per piece, but it
7936        only takes a fraction of a second and there is a much
7937        longer delay for loading the pieces.             */
7938     for (n = 0; n < pieceDepth; n ++) {
7939       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7940                  0, 0, squareSize, squareSize,
7941                  0, 0, plane);
7942       plane = plane << 1;
7943     }
7944   }
7945   /* Clean up */
7946   XFreePixmap(xDisplay, buf);
7947   XFreeGC(xDisplay, bufGC);
7948   XFreeGC(xDisplay, maskGC);
7949 }
7950
7951 static void
7952 InitAnimState (AnimState *anim, XWindowAttributes *info)
7953 {
7954   XtGCMask  mask;
7955   XGCValues values;
7956
7957   /* Each buffer is square size, same depth as window */
7958   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7959                         squareSize, squareSize, info->depth);
7960   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7961                         squareSize, squareSize, info->depth);
7962
7963   /* Create a plain GC for blitting */
7964   mask = GCForeground | GCBackground | GCFunction |
7965          GCPlaneMask | GCGraphicsExposures;
7966   values.foreground = XBlackPixel(xDisplay, xScreen);
7967   values.background = XWhitePixel(xDisplay, xScreen);
7968   values.function   = GXcopy;
7969   values.plane_mask = AllPlanes;
7970   values.graphics_exposures = False;
7971   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7972
7973   /* Piece will be copied from an existing context at
7974      the start of each new animation/drag. */
7975   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7976
7977   /* Outline will be a read-only copy of an existing */
7978   anim->outlineGC = None;
7979 }
7980
7981 static void
7982 CreateAnimVars ()
7983 {
7984   XWindowAttributes info;
7985
7986   if (xpmDone && gameInfo.variant == oldVariant) return;
7987   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7988   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7989
7990   InitAnimState(&game, &info);
7991   InitAnimState(&player, &info);
7992
7993   /* For XPM pieces, we need bitmaps to use as masks. */
7994   if (useImages)
7995     CreateAnimMasks(info.depth), xpmDone = 1;
7996 }
7997
7998 #ifndef HAVE_USLEEP
7999
8000 static Boolean frameWaiting;
8001
8002 static RETSIGTYPE
8003 FrameAlarm (int sig)
8004 {
8005   frameWaiting = False;
8006   /* In case System-V style signals.  Needed?? */
8007   signal(SIGALRM, FrameAlarm);
8008 }
8009
8010 static void
8011 FrameDelay (int time)
8012 {
8013   struct itimerval delay;
8014
8015   XSync(xDisplay, False);
8016
8017   if (time > 0) {
8018     frameWaiting = True;
8019     signal(SIGALRM, FrameAlarm);
8020     delay.it_interval.tv_sec =
8021       delay.it_value.tv_sec = time / 1000;
8022     delay.it_interval.tv_usec =
8023       delay.it_value.tv_usec = (time % 1000) * 1000;
8024     setitimer(ITIMER_REAL, &delay, NULL);
8025     while (frameWaiting) pause();
8026     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8027     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8028     setitimer(ITIMER_REAL, &delay, NULL);
8029   }
8030 }
8031
8032 #else
8033
8034 static void
8035 FrameDelay (int time)
8036 {
8037   XSync(xDisplay, False);
8038   if (time > 0)
8039     usleep(time * 1000);
8040 }
8041
8042 #endif
8043
8044 void
8045 DoSleep (int n)
8046 {
8047     FrameDelay(n);
8048 }
8049
8050 /*      Convert board position to corner of screen rect and color       */
8051
8052 static void
8053 ScreenSquare (int column, int row, XPoint *pt, int *color)
8054 {
8055   if (flipView) {
8056     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8057     pt->y = lineGap + row * (squareSize + lineGap);
8058   } else {
8059     pt->x = lineGap + column * (squareSize + lineGap);
8060     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8061   }
8062   *color = SquareColor(row, column);
8063 }
8064
8065 /*      Convert window coords to square                 */
8066
8067 static void
8068 BoardSquare (int x, int y, int *column, int *row)
8069 {
8070   *column = EventToSquare(x, BOARD_WIDTH);
8071   if (flipView && *column >= 0)
8072     *column = BOARD_WIDTH - 1 - *column;
8073   *row = EventToSquare(y, BOARD_HEIGHT);
8074   if (!flipView && *row >= 0)
8075     *row = BOARD_HEIGHT - 1 - *row;
8076 }
8077
8078 /*   Utilities  */
8079
8080 #undef Max  /* just in case */
8081 #undef Min
8082 #define Max(a, b) ((a) > (b) ? (a) : (b))
8083 #define Min(a, b) ((a) < (b) ? (a) : (b))
8084
8085 static void
8086 SetRect (XRectangle *rect, int x, int y, int width, int height)
8087 {
8088   rect->x = x;
8089   rect->y = y;
8090   rect->width  = width;
8091   rect->height = height;
8092 }
8093
8094 /*      Test if two frames overlap. If they do, return
8095         intersection rect within old and location of
8096         that rect within new. */
8097
8098 static Boolean
8099 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
8100 {
8101   if (old->x > new->x + size || new->x > old->x + size ||
8102       old->y > new->y + size || new->y > old->y + size) {
8103     return False;
8104   } else {
8105     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8106             size - abs(old->x - new->x), size - abs(old->y - new->y));
8107     pt->x = Max(old->x - new->x, 0);
8108     pt->y = Max(old->y - new->y, 0);
8109     return True;
8110   }
8111 }
8112
8113 /*      For two overlapping frames, return the rect(s)
8114         in the old that do not intersect with the new.   */
8115
8116 static void
8117 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
8118 {
8119   int        count;
8120
8121   /* If old = new (shouldn't happen) then nothing to draw */
8122   if (old->x == new->x && old->y == new->y) {
8123     *nUpdates = 0;
8124     return;
8125   }
8126   /* Work out what bits overlap. Since we know the rects
8127      are the same size we don't need a full intersect calc. */
8128   count = 0;
8129   /* Top or bottom edge? */
8130   if (new->y > old->y) {
8131     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8132     count ++;
8133   } else if (old->y > new->y) {
8134     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8135                               size, old->y - new->y);
8136     count ++;
8137   }
8138   /* Left or right edge - don't overlap any update calculated above. */
8139   if (new->x > old->x) {
8140     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8141                               new->x - old->x, size - abs(new->y - old->y));
8142     count ++;
8143   } else if (old->x > new->x) {
8144     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8145                               old->x - new->x, size - abs(new->y - old->y));
8146     count ++;
8147   }
8148   /* Done */
8149   *nUpdates = count;
8150 }
8151
8152 /*      Generate a series of frame coords from start->mid->finish.
8153         The movement rate doubles until the half way point is
8154         reached, then halves back down to the final destination,
8155         which gives a nice slow in/out effect. The algorithmn
8156         may seem to generate too many intermediates for short
8157         moves, but remember that the purpose is to attract the
8158         viewers attention to the piece about to be moved and
8159         then to where it ends up. Too few frames would be less
8160         noticeable.                                             */
8161
8162 static void
8163 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8164 {
8165   int fraction, n, count;
8166
8167   count = 0;
8168
8169   /* Slow in, stepping 1/16th, then 1/8th, ... */
8170   fraction = 1;
8171   for (n = 0; n < factor; n++)
8172     fraction *= 2;
8173   for (n = 0; n < factor; n++) {
8174     frames[count].x = start->x + (mid->x - start->x) / fraction;
8175     frames[count].y = start->y + (mid->y - start->y) / fraction;
8176     count ++;
8177     fraction = fraction / 2;
8178   }
8179
8180   /* Midpoint */
8181   frames[count] = *mid;
8182   count ++;
8183
8184   /* Slow out, stepping 1/2, then 1/4, ... */
8185   fraction = 2;
8186   for (n = 0; n < factor; n++) {
8187     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8188     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8189     count ++;
8190     fraction = fraction * 2;
8191   }
8192   *nFrames = count;
8193 }
8194
8195 /*      Draw a piece on the screen without disturbing what's there      */
8196
8197 static void
8198 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8199 {
8200   GC source;
8201
8202   /* Bitmap for piece being moved. */
8203   if (appData.monoMode) {
8204       *mask = *pieceToSolid(piece);
8205   } else if (useImages) {
8206 #if HAVE_LIBXPM
8207       *mask = xpmMask[piece];
8208 #else
8209       *mask = ximMaskPm[piece];
8210 #endif
8211   } else {
8212       *mask = *pieceToSolid(piece);
8213   }
8214
8215   /* GC for piece being moved. Square color doesn't matter, but
8216      since it gets modified we make a copy of the original. */
8217   if (White(piece)) {
8218     if (appData.monoMode)
8219       source = bwPieceGC;
8220     else
8221       source = wlPieceGC;
8222   } else {
8223     if (appData.monoMode)
8224       source = wbPieceGC;
8225     else
8226       source = blPieceGC;
8227   }
8228   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8229
8230   /* Outline only used in mono mode and is not modified */
8231   if (White(piece))
8232     *outline = bwPieceGC;
8233   else
8234     *outline = wbPieceGC;
8235 }
8236
8237 static void
8238 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
8239 {
8240   int   kind;
8241
8242   if (!useImages) {
8243     /* Draw solid rectangle which will be clipped to shape of piece */
8244     XFillRectangle(xDisplay, dest, clip,
8245                    0, 0, squareSize, squareSize);
8246     if (appData.monoMode)
8247       /* Also draw outline in contrasting color for black
8248          on black / white on white cases                */
8249       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8250                  0, 0, squareSize, squareSize, 0, 0, 1);
8251   } else {
8252     /* Copy the piece */
8253     if (White(piece))
8254       kind = 0;
8255     else
8256       kind = 2;
8257     if(appData.upsideDown && flipView) kind ^= 2;
8258     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8259               dest, clip,
8260               0, 0, squareSize, squareSize,
8261               0, 0);
8262   }
8263 }
8264
8265 /* Animate the movement of a single piece */
8266
8267 static void
8268 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8269 {
8270   Pixmap mask;
8271
8272   if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8273   /* The old buffer is initialised with the start square (empty) */
8274   BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8275   anim->prevFrame = *start;
8276
8277   /* The piece will be drawn using its own bitmap as a matte    */
8278   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8279   XSetClipMask(xDisplay, anim->pieceGC, mask);
8280 }
8281
8282 static void
8283 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8284 {
8285   XRectangle updates[4];
8286   XRectangle overlap;
8287   XPoint     pt;
8288   int        count, i;
8289
8290   /* Save what we are about to draw into the new buffer */
8291   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8292             frame->x, frame->y, squareSize, squareSize,
8293             0, 0);
8294
8295   /* Erase bits of the previous frame */
8296   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8297     /* Where the new frame overlapped the previous,
8298        the contents in newBuf are wrong. */
8299     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8300               overlap.x, overlap.y,
8301               overlap.width, overlap.height,
8302               pt.x, pt.y);
8303     /* Repaint the areas in the old that don't overlap new */
8304     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8305     for (i = 0; i < count; i++)
8306       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8307                 updates[i].x - anim->prevFrame.x,
8308                 updates[i].y - anim->prevFrame.y,
8309                 updates[i].width, updates[i].height,
8310                 updates[i].x, updates[i].y);
8311   } else {
8312     /* Easy when no overlap */
8313     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8314                   0, 0, squareSize, squareSize,
8315                   anim->prevFrame.x, anim->prevFrame.y);
8316   }
8317
8318   /* Save this frame for next time round */
8319   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8320                 0, 0, squareSize, squareSize,
8321                 0, 0);
8322   anim->prevFrame = *frame;
8323
8324   /* Draw piece over original screen contents, not current,
8325      and copy entire rect. Wipes out overlapping piece images. */
8326   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8327   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8328                 0, 0, squareSize, squareSize,
8329                 frame->x, frame->y);
8330 }
8331
8332 static void
8333 EndAnimation (AnimState *anim, XPoint *finish)
8334 {
8335   XRectangle updates[4];
8336   XRectangle overlap;
8337   XPoint     pt;
8338   int        count, i;
8339
8340   /* The main code will redraw the final square, so we
8341      only need to erase the bits that don't overlap.    */
8342   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8343     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8344     for (i = 0; i < count; i++)
8345       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8346                 updates[i].x - anim->prevFrame.x,
8347                 updates[i].y - anim->prevFrame.y,
8348                 updates[i].width, updates[i].height,
8349                 updates[i].x, updates[i].y);
8350   } else {
8351     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8352                 0, 0, squareSize, squareSize,
8353                 anim->prevFrame.x, anim->prevFrame.y);
8354   }
8355 }
8356
8357 static void
8358 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8359 {
8360   int n;
8361
8362   BeginAnimation(anim, piece, startColor, start);
8363   for (n = 0; n < nFrames; n++) {
8364     AnimationFrame(anim, &(frames[n]), piece);
8365     FrameDelay(appData.animSpeed);
8366   }
8367   EndAnimation(anim, finish);
8368 }
8369
8370 void
8371 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8372 {
8373     int i, x, y;
8374     ChessSquare piece = board[fromY][toY];
8375     board[fromY][toY] = EmptySquare;
8376     DrawPosition(FALSE, board);
8377     if (flipView) {
8378         x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8379         y = lineGap + toY * (squareSize + lineGap);
8380     } else {
8381         x = lineGap + toX * (squareSize + lineGap);
8382         y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8383     }
8384     for(i=1; i<4*kFactor; i++) {
8385         int r = squareSize * 9 * i/(20*kFactor - 5);
8386         XFillArc(xDisplay, xBoardWindow, highlineGC,
8387                 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8388         FrameDelay(appData.animSpeed);
8389     }
8390     board[fromY][toY] = piece;
8391 }
8392
8393 /* Main control logic for deciding what to animate and how */
8394
8395 void
8396 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8397 {
8398   ChessSquare piece;
8399   int hop;
8400   XPoint      start, finish, mid;
8401   XPoint      frames[kFactor * 2 + 1];
8402   int         nFrames, startColor, endColor;
8403
8404   /* Are we animating? */
8405   if (!appData.animate || appData.blindfold)
8406     return;
8407
8408   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8409      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8410         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8411
8412   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8413   piece = board[fromY][fromX];
8414   if (piece >= EmptySquare) return;
8415
8416 #if DONT_HOP
8417   hop = FALSE;
8418 #else
8419   hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8420 #endif
8421
8422   ScreenSquare(fromX, fromY, &start, &startColor);
8423   ScreenSquare(toX, toY, &finish, &endColor);
8424
8425   if (hop) {
8426     /* Knight: make straight movement then diagonal */
8427     if (abs(toY - fromY) < abs(toX - fromX)) {
8428        mid.x = start.x + (finish.x - start.x) / 2;
8429        mid.y = start.y;
8430      } else {
8431        mid.x = start.x;
8432        mid.y = start.y + (finish.y - start.y) / 2;
8433      }
8434   } else {
8435     mid.x = start.x + (finish.x - start.x) / 2;
8436     mid.y = start.y + (finish.y - start.y) / 2;
8437   }
8438
8439   /* Don't use as many frames for very short moves */
8440   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8441     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8442   else
8443     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8444   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8445   if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8446     int i,j;
8447     for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8448       if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8449   }
8450
8451   /* Be sure end square is redrawn */
8452   damage[0][toY][toX] = True;
8453 }
8454
8455 void
8456 DragPieceBegin (int x, int y, Boolean instantly)
8457 {
8458     int  boardX, boardY, color;
8459     XPoint corner;
8460
8461     /* Are we animating? */
8462     if (!appData.animateDragging || appData.blindfold)
8463       return;
8464
8465     /* Figure out which square we start in and the
8466        mouse position relative to top left corner. */
8467     BoardSquare(x, y, &boardX, &boardY);
8468     player.startBoardX = boardX;
8469     player.startBoardY = boardY;
8470     ScreenSquare(boardX, boardY, &corner, &color);
8471     player.startSquare  = corner;
8472     player.startColor   = color;
8473     /* As soon as we start dragging, the piece will jump slightly to
8474        be centered over the mouse pointer. */
8475     player.mouseDelta.x = squareSize/2;
8476     player.mouseDelta.y = squareSize/2;
8477     /* Initialise animation */
8478     player.dragPiece = PieceForSquare(boardX, boardY);
8479     /* Sanity check */
8480     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8481         player.dragActive = True;
8482         BeginAnimation(&player, player.dragPiece, color, &corner);
8483         /* Mark this square as needing to be redrawn. Note that
8484            we don't remove the piece though, since logically (ie
8485            as seen by opponent) the move hasn't been made yet. */
8486            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8487               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8488            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8489                      corner.x, corner.y, squareSize, squareSize,
8490                      0, 0); // [HGM] zh: unstack in stead of grab
8491            if(gatingPiece != EmptySquare) {
8492                /* Kludge alert: When gating we want the introduced
8493                   piece to appear on the from square. To generate an
8494                   image of it, we draw it on the board, copy the image,
8495                   and draw the original piece again. */
8496                ChessSquare piece = boards[currentMove][boardY][boardX];
8497                DrawSquare(boardY, boardX, gatingPiece, 0);
8498                XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8499                      corner.x, corner.y, squareSize, squareSize, 0, 0);
8500                DrawSquare(boardY, boardX, piece, 0);
8501            }
8502         damage[0][boardY][boardX] = True;
8503     } else {
8504         player.dragActive = False;
8505     }
8506 }
8507
8508 void
8509 ChangeDragPiece (ChessSquare piece)
8510 {
8511   Pixmap mask;
8512   player.dragPiece = piece;
8513   /* The piece will be drawn using its own bitmap as a matte    */
8514   SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8515   XSetClipMask(xDisplay, player.pieceGC, mask);
8516 }
8517
8518 static void
8519 DragPieceMove (int x, int y)
8520 {
8521     XPoint corner;
8522
8523     /* Are we animating? */
8524     if (!appData.animateDragging || appData.blindfold)
8525       return;
8526
8527     /* Sanity check */
8528     if (! player.dragActive)
8529       return;
8530     /* Move piece, maintaining same relative position
8531        of mouse within square    */
8532     corner.x = x - player.mouseDelta.x;
8533     corner.y = y - player.mouseDelta.y;
8534     AnimationFrame(&player, &corner, player.dragPiece);
8535 #if HIGHDRAG*0
8536     if (appData.highlightDragging) {
8537         int boardX, boardY;
8538         BoardSquare(x, y, &boardX, &boardY);
8539         SetHighlights(fromX, fromY, boardX, boardY);
8540     }
8541 #endif
8542 }
8543
8544 void
8545 DragPieceEnd (int x, int y)
8546 {
8547     int boardX, boardY, color;
8548     XPoint corner;
8549
8550     /* Are we animating? */
8551     if (!appData.animateDragging || appData.blindfold)
8552       return;
8553
8554     /* Sanity check */
8555     if (! player.dragActive)
8556       return;
8557     /* Last frame in sequence is square piece is
8558        placed on, which may not match mouse exactly. */
8559     BoardSquare(x, y, &boardX, &boardY);
8560     ScreenSquare(boardX, boardY, &corner, &color);
8561     EndAnimation(&player, &corner);
8562
8563     /* Be sure end square is redrawn */
8564     damage[0][boardY][boardX] = True;
8565
8566     /* This prevents weird things happening with fast successive
8567        clicks which on my Sun at least can cause motion events
8568        without corresponding press/release. */
8569     player.dragActive = False;
8570 }
8571
8572 /* Handle expose event while piece being dragged */
8573
8574 static void
8575 DrawDragPiece ()
8576 {
8577   if (!player.dragActive || appData.blindfold)
8578     return;
8579
8580   /* What we're doing: logically, the move hasn't been made yet,
8581      so the piece is still in it's original square. But visually
8582      it's being dragged around the board. So we erase the square
8583      that the piece is on and draw it at the last known drag point. */
8584   BlankSquare(player.startSquare.x, player.startSquare.y,
8585                 player.startColor, EmptySquare, xBoardWindow, 1);
8586   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8587   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8588 }
8589
8590 #include <sys/ioctl.h>
8591 int
8592 get_term_width ()
8593 {
8594     int fd, default_width;
8595
8596     fd = STDIN_FILENO;
8597     default_width = 79; // this is FICS default anyway...
8598
8599 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8600     struct ttysize win;
8601     if (!ioctl(fd, TIOCGSIZE, &win))
8602         default_width = win.ts_cols;
8603 #elif defined(TIOCGWINSZ)
8604     struct winsize win;
8605     if (!ioctl(fd, TIOCGWINSZ, &win))
8606         default_width = win.ws_col;
8607 #endif
8608     return default_width;
8609 }
8610
8611 void
8612 update_ics_width ()
8613 {
8614   static int old_width = 0;
8615   int new_width = get_term_width();
8616
8617   if (old_width != new_width)
8618     ics_printf("set width %d\n", new_width);
8619   old_width = new_width;
8620 }
8621
8622 void
8623 NotifyFrontendLogin ()
8624 {
8625     update_ics_width();
8626 }
8627
8628 /* [AS] Arrow highlighting support */
8629
8630 static double A_WIDTH = 5; /* Width of arrow body */
8631
8632 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
8633 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
8634
8635 static double
8636 Sqr (double x)
8637 {
8638     return x*x;
8639 }
8640
8641 static int
8642 Round (double x)
8643 {
8644     return (int) (x + 0.5);
8645 }
8646
8647 void
8648 SquareToPos (int rank, int file, int *x, int *y)
8649 {
8650     if (flipView) {
8651         *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8652         *y = lineGap + rank * (squareSize + lineGap);
8653     } else {
8654         *x = lineGap + file * (squareSize + lineGap);
8655         *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8656     }
8657 }
8658
8659 /* Draw an arrow between two points using current settings */
8660 void
8661 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8662 {
8663     XPoint arrow[8];
8664     double dx, dy, j, k, x, y;
8665
8666     if( d_x == s_x ) {
8667         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8668
8669         arrow[0].x = s_x + A_WIDTH + 0.5;
8670         arrow[0].y = s_y;
8671
8672         arrow[1].x = s_x + A_WIDTH + 0.5;
8673         arrow[1].y = d_y - h;
8674
8675         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8676         arrow[2].y = d_y - h;
8677
8678         arrow[3].x = d_x;
8679         arrow[3].y = d_y;
8680
8681         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8682         arrow[5].y = d_y - h;
8683
8684         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8685         arrow[4].y = d_y - h;
8686
8687         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8688         arrow[6].y = s_y;
8689     }
8690     else if( d_y == s_y ) {
8691         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8692
8693         arrow[0].x = s_x;
8694         arrow[0].y = s_y + A_WIDTH + 0.5;
8695
8696         arrow[1].x = d_x - w;
8697         arrow[1].y = s_y + A_WIDTH + 0.5;
8698
8699         arrow[2].x = d_x - w;
8700         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8701
8702         arrow[3].x = d_x;
8703         arrow[3].y = d_y;
8704
8705         arrow[5].x = d_x - w;
8706         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8707
8708         arrow[4].x = d_x - w;
8709         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8710
8711         arrow[6].x = s_x;
8712         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8713     }
8714     else {
8715         /* [AS] Needed a lot of paper for this! :-) */
8716         dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8717         dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8718
8719         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8720
8721         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8722
8723         x = s_x;
8724         y = s_y;
8725
8726         arrow[0].x = Round(x - j);
8727         arrow[0].y = Round(y + j*dx);
8728
8729         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
8730         arrow[1].y = Round(arrow[0].y - 2*j*dx);
8731
8732         if( d_x > s_x ) {
8733             x = (double) d_x - k;
8734             y = (double) d_y - k*dy;
8735         }
8736         else {
8737             x = (double) d_x + k;
8738             y = (double) d_y + k*dy;
8739         }
8740
8741         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8742
8743         arrow[6].x = Round(x - j);
8744         arrow[6].y = Round(y + j*dx);
8745
8746         arrow[2].x = Round(arrow[6].x + 2*j);
8747         arrow[2].y = Round(arrow[6].y - 2*j*dx);
8748
8749         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8750         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8751
8752         arrow[4].x = d_x;
8753         arrow[4].y = d_y;
8754
8755         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8756         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8757     }
8758
8759     XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8760     if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8761 //    Polygon( hdc, arrow, 7 );
8762 }
8763
8764 void
8765 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8766 {
8767     int hor, vert, i;
8768     hor = 64*s_col + 32; vert = 64*s_row + 32;
8769     for(i=0; i<= 64; i++) {
8770             damage[0][vert+6>>6][hor+6>>6] = True;
8771             damage[0][vert-6>>6][hor+6>>6] = True;
8772             damage[0][vert+6>>6][hor-6>>6] = True;
8773             damage[0][vert-6>>6][hor-6>>6] = True;
8774             hor += d_col - s_col; vert += d_row - s_row;
8775     }
8776 }
8777
8778 /* [AS] Draw an arrow between two squares */
8779 void
8780 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8781 {
8782     int s_x, s_y, d_x, d_y;
8783
8784     if( s_col == d_col && s_row == d_row ) {
8785         return;
8786     }
8787
8788     /* Get source and destination points */
8789     SquareToPos( s_row, s_col, &s_x, &s_y);
8790     SquareToPos( d_row, d_col, &d_x, &d_y);
8791
8792     if( d_y > s_y ) {
8793         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8794     }
8795     else if( d_y < s_y ) {
8796         d_y += squareSize / 2 + squareSize / 4;
8797     }
8798     else {
8799         d_y += squareSize / 2;
8800     }
8801
8802     if( d_x > s_x ) {
8803         d_x += squareSize / 2 - squareSize / 4;
8804     }
8805     else if( d_x < s_x ) {
8806         d_x += squareSize / 2 + squareSize / 4;
8807     }
8808     else {
8809         d_x += squareSize / 2;
8810     }
8811
8812     s_x += squareSize / 2;
8813     s_y += squareSize / 2;
8814
8815     /* Adjust width */
8816     A_WIDTH = squareSize / 14.; //[HGM] make float
8817
8818     DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8819     ArrowDamage(s_col, s_row, d_col, d_row);
8820 }
8821
8822 Boolean
8823 IsDrawArrowEnabled ()
8824 {
8825     return appData.highlightMoveWithArrow && squareSize >= 32;
8826 }
8827
8828 void
8829 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8830 {
8831     if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8832         DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8833 }
8834
8835 void
8836 UpdateLogos (int displ)
8837 {
8838     return; // no logos in XBoard yet
8839 }
8840