3dd9f659818f86dd9e8a5ece5af1e80e8fb649ef
[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     system("xdg-open mailto:bug-xboard@gnu.org");
6676 }
6677
6678 void
6679 GuideProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6680 {
6681     system("xdg-open http://www.gnu.org/software/xboard/user_guide/UserGuide.html");
6682 }
6683
6684 void
6685 HomePageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6686 {
6687     system("xdg-open http://www.gnu.org/software/xboard/");
6688 }
6689
6690 void
6691 NewsPageProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6692 {
6693     system("xdg-open http://www.gnu.org/software/xboard/whats_new/portal.html");
6694 }
6695
6696 void
6697 AboutProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6698 {
6699     char buf[2 * MSG_SIZ];
6700 #if ZIPPY
6701     char *zippy = _(" (with Zippy code)");
6702 #else
6703     char *zippy = "";
6704 #endif
6705     snprintf(buf, sizeof(buf), 
6706 _("%s%s\n\n"
6707 "Copyright 1991 Digital Equipment Corporation\n"
6708 "Enhancements Copyright 1992-2012 Free Software Foundation\n"
6709 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6710 "%s is free software and carries NO WARRANTY;"
6711 "see the file COPYING for more information.\n\n"
6712 "Visit XBoard on the web at: http://www.gnu.org/software/xboard/\n"
6713 "Check out the newest features at: http://www.gnu.org/software/xboard/whats_new.html\n\n"
6714 "Report bugs via email at: <bug-xboard@gnu.org>\n\n"
6715   ),
6716             programVersion, zippy, PACKAGE);
6717     ErrorPopUp(_("About XBoard"), buf, FALSE);
6718 }
6719
6720 void
6721 DebugProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6722 {
6723     appData.debugMode = !appData.debugMode;
6724 }
6725
6726 void
6727 AboutGameProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6728 {
6729     AboutGameEvent();
6730 }
6731
6732 void
6733 NothingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6734 {
6735     return;
6736 }
6737
6738 void
6739 DisplayMessage (char *message, char *extMessage)
6740 {
6741   /* display a message in the message widget */
6742
6743   char buf[MSG_SIZ];
6744   Arg arg;
6745
6746   if (extMessage)
6747     {
6748       if (*message)
6749         {
6750           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
6751           message = buf;
6752         }
6753       else
6754         {
6755           message = extMessage;
6756         };
6757     };
6758
6759     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6760
6761   /* need to test if messageWidget already exists, since this function
6762      can also be called during the startup, if for example a Xresource
6763      is not set up correctly */
6764   if(messageWidget)
6765     {
6766       XtSetArg(arg, XtNlabel, message);
6767       XtSetValues(messageWidget, &arg, 1);
6768     };
6769
6770   return;
6771 }
6772
6773 void
6774 DisplayTitle (char *text)
6775 {
6776     Arg args[16];
6777     int i;
6778     char title[MSG_SIZ];
6779     char icon[MSG_SIZ];
6780
6781     if (text == NULL) text = "";
6782
6783     if (appData.titleInWindow) {
6784         i = 0;
6785         XtSetArg(args[i], XtNlabel, text);   i++;
6786         XtSetValues(titleWidget, args, i);
6787     }
6788
6789     if (*text != NULLCHAR) {
6790       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6791       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6792     } else if (appData.icsActive) {
6793         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6794         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6795     } else if (appData.cmailGameName[0] != NULLCHAR) {
6796         snprintf(icon, sizeof(icon), "%s", "CMail");
6797         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6798 #ifdef GOTHIC
6799     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6800     } else if (gameInfo.variant == VariantGothic) {
6801       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
6802       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
6803 #endif
6804 #ifdef FALCON
6805     } else if (gameInfo.variant == VariantFalcon) {
6806       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6807       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6808 #endif
6809     } else if (appData.noChessProgram) {
6810       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6811       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6812     } else {
6813       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6814         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6815     }
6816     i = 0;
6817     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6818     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6819     XtSetValues(shellWidget, args, i);
6820     XSync(xDisplay, False);
6821 }
6822
6823
6824 void
6825 DisplayError (String message, int error)
6826 {
6827     char buf[MSG_SIZ];
6828
6829     if (error == 0) {
6830         if (appData.debugMode || appData.matchMode) {
6831             fprintf(stderr, "%s: %s\n", programName, message);
6832         }
6833     } else {
6834         if (appData.debugMode || appData.matchMode) {
6835             fprintf(stderr, "%s: %s: %s\n",
6836                     programName, message, strerror(error));
6837         }
6838         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6839         message = buf;
6840     }
6841     ErrorPopUp(_("Error"), message, FALSE);
6842 }
6843
6844
6845 void
6846 DisplayMoveError (String message)
6847 {
6848     fromX = fromY = -1;
6849     ClearHighlights();
6850     DrawPosition(FALSE, NULL);
6851     if (appData.debugMode || appData.matchMode) {
6852         fprintf(stderr, "%s: %s\n", programName, message);
6853     }
6854     if (appData.popupMoveErrors) {
6855         ErrorPopUp(_("Error"), message, FALSE);
6856     } else {
6857         DisplayMessage(message, "");
6858     }
6859 }
6860
6861
6862 void
6863 DisplayFatalError (String message, int error, int status)
6864 {
6865     char buf[MSG_SIZ];
6866
6867     errorExitStatus = status;
6868     if (error == 0) {
6869         fprintf(stderr, "%s: %s\n", programName, message);
6870     } else {
6871         fprintf(stderr, "%s: %s: %s\n",
6872                 programName, message, strerror(error));
6873         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6874         message = buf;
6875     }
6876     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6877       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6878     } else {
6879       ExitEvent(status);
6880     }
6881 }
6882
6883 void
6884 DisplayInformation (String message)
6885 {
6886     ErrorPopDown();
6887     ErrorPopUp(_("Information"), message, TRUE);
6888 }
6889
6890 void
6891 DisplayNote (String message)
6892 {
6893     ErrorPopDown();
6894     ErrorPopUp(_("Note"), message, FALSE);
6895 }
6896
6897 static int
6898 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
6899 {
6900     return 0;
6901 }
6902
6903 void
6904 DisplayIcsInteractionTitle (String message)
6905 {
6906   if (oldICSInteractionTitle == NULL) {
6907     /* Magic to find the old window title, adapted from vim */
6908     char *wina = getenv("WINDOWID");
6909     if (wina != NULL) {
6910       Window win = (Window) atoi(wina);
6911       Window root, parent, *children;
6912       unsigned int nchildren;
6913       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6914       for (;;) {
6915         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6916         if (!XQueryTree(xDisplay, win, &root, &parent,
6917                         &children, &nchildren)) break;
6918         if (children) XFree((void *)children);
6919         if (parent == root || parent == 0) break;
6920         win = parent;
6921       }
6922       XSetErrorHandler(oldHandler);
6923     }
6924     if (oldICSInteractionTitle == NULL) {
6925       oldICSInteractionTitle = "xterm";
6926     }
6927   }
6928   printf("\033]0;%s\007", message);
6929   fflush(stdout);
6930 }
6931
6932 char pendingReplyPrefix[MSG_SIZ];
6933 ProcRef pendingReplyPR;
6934
6935 void
6936 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6937 {
6938     if (*nprms != 4) {
6939         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6940                 *nprms);
6941         return;
6942     }
6943     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6944 }
6945
6946 void
6947 AskQuestionPopDown ()
6948 {
6949     if (!askQuestionUp) return;
6950     XtPopdown(askQuestionShell);
6951     XtDestroyWidget(askQuestionShell);
6952     askQuestionUp = False;
6953 }
6954
6955 void
6956 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
6957 {
6958     char buf[MSG_SIZ];
6959     int err;
6960     String reply;
6961
6962     reply = XawDialogGetValueString(w = XtParent(w));
6963     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6964     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6965     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6966     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
6967     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6968     AskQuestionPopDown();
6969
6970     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6971 }
6972
6973 void
6974 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
6975 {
6976     String name;
6977     Arg args[16];
6978
6979     XtSetArg(args[0], XtNlabel, &name);
6980     XtGetValues(w, args, 1);
6981
6982     if (strcmp(name, _("cancel")) == 0) {
6983         AskQuestionPopDown();
6984     } else {
6985         AskQuestionReplyAction(w, NULL, NULL, NULL);
6986     }
6987 }
6988
6989 void
6990 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
6991 {
6992     Arg args[16];
6993     Widget popup, layout, dialog, edit;
6994     Window root, child;
6995     int x, y, i;
6996     int win_x, win_y;
6997     unsigned int mask;
6998
6999     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7000     pendingReplyPR = pr;
7001
7002     i = 0;
7003     XtSetArg(args[i], XtNresizable, True); i++;
7004     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7005     askQuestionShell = popup =
7006       XtCreatePopupShell(title, transientShellWidgetClass,
7007                          shellWidget, args, i);
7008
7009     layout =
7010       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7011                             layoutArgs, XtNumber(layoutArgs));
7012
7013     i = 0;
7014     XtSetArg(args[i], XtNlabel, question); i++;
7015     XtSetArg(args[i], XtNvalue, ""); i++;
7016     XtSetArg(args[i], XtNborderWidth, 0); i++;
7017     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7018                                    layout, args, i);
7019
7020     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7021                        (XtPointer) dialog);
7022     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7023                        (XtPointer) dialog);
7024
7025     XtRealizeWidget(popup);
7026     CatchDeleteWindow(popup, "AskQuestionPopDown");
7027
7028     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7029                   &x, &y, &win_x, &win_y, &mask);
7030
7031     XtSetArg(args[0], XtNx, x - 10);
7032     XtSetArg(args[1], XtNy, y - 30);
7033     XtSetValues(popup, args, 2);
7034
7035     XtPopup(popup, XtGrabExclusive);
7036     askQuestionUp = True;
7037
7038     edit = XtNameToWidget(dialog, "*value");
7039     XtSetKeyboardFocus(popup, edit);
7040 }
7041
7042
7043 void
7044 PlaySound (char *name)
7045 {
7046   if (*name == NULLCHAR) {
7047     return;
7048   } else if (strcmp(name, "$") == 0) {
7049     putc(BELLCHAR, stderr);
7050   } else {
7051     char buf[2048];
7052     char *prefix = "", *sep = "";
7053     if(appData.soundProgram[0] == NULLCHAR) return;
7054     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7055     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7056     system(buf);
7057   }
7058 }
7059
7060 void
7061 RingBell ()
7062 {
7063   PlaySound(appData.soundMove);
7064 }
7065
7066 void
7067 PlayIcsWinSound ()
7068 {
7069   PlaySound(appData.soundIcsWin);
7070 }
7071
7072 void
7073 PlayIcsLossSound ()
7074 {
7075   PlaySound(appData.soundIcsLoss);
7076 }
7077
7078 void
7079 PlayIcsDrawSound ()
7080 {
7081   PlaySound(appData.soundIcsDraw);
7082 }
7083
7084 void
7085 PlayIcsUnfinishedSound ()
7086 {
7087   PlaySound(appData.soundIcsUnfinished);
7088 }
7089
7090 void
7091 PlayAlarmSound ()
7092 {
7093   PlaySound(appData.soundIcsAlarm);
7094 }
7095
7096 void
7097 PlayTellSound ()
7098 {
7099   PlaySound(appData.soundTell);
7100 }
7101
7102 void
7103 EchoOn ()
7104 {
7105     system("stty echo");
7106     noEcho = False;
7107 }
7108
7109 void
7110 EchoOff ()
7111 {
7112     system("stty -echo");
7113     noEcho = True;
7114 }
7115
7116 void
7117 RunCommand (char *buf)
7118 {
7119     system(buf);
7120 }
7121
7122 void
7123 Colorize (ColorClass cc, int continuation)
7124 {
7125     char buf[MSG_SIZ];
7126     int count, outCount, error;
7127
7128     if (textColors[(int)cc].bg > 0) {
7129         if (textColors[(int)cc].fg > 0) {
7130           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7131                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7132         } else {
7133           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7134                    textColors[(int)cc].bg);
7135         }
7136     } else {
7137         if (textColors[(int)cc].fg > 0) {
7138           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7139                     textColors[(int)cc].fg);
7140         } else {
7141           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7142         }
7143     }
7144     count = strlen(buf);
7145     outCount = OutputToProcess(NoProc, buf, count, &error);
7146     if (outCount < count) {
7147         DisplayFatalError(_("Error writing to display"), error, 1);
7148     }
7149
7150     if (continuation) return;
7151     switch (cc) {
7152     case ColorShout:
7153       PlaySound(appData.soundShout);
7154       break;
7155     case ColorSShout:
7156       PlaySound(appData.soundSShout);
7157       break;
7158     case ColorChannel1:
7159       PlaySound(appData.soundChannel1);
7160       break;
7161     case ColorChannel:
7162       PlaySound(appData.soundChannel);
7163       break;
7164     case ColorKibitz:
7165       PlaySound(appData.soundKibitz);
7166       break;
7167     case ColorTell:
7168       PlaySound(appData.soundTell);
7169       break;
7170     case ColorChallenge:
7171       PlaySound(appData.soundChallenge);
7172       break;
7173     case ColorRequest:
7174       PlaySound(appData.soundRequest);
7175       break;
7176     case ColorSeek:
7177       PlaySound(appData.soundSeek);
7178       break;
7179     case ColorNormal:
7180     case ColorNone:
7181     default:
7182       break;
7183     }
7184 }
7185
7186 char *
7187 UserName ()
7188 {
7189     return getpwuid(getuid())->pw_name;
7190 }
7191
7192 static char *
7193 ExpandPathName (char *path)
7194 {
7195     static char static_buf[4*MSG_SIZ];
7196     char *d, *s, buf[4*MSG_SIZ];
7197     struct passwd *pwd;
7198
7199     s = path;
7200     d = static_buf;
7201
7202     while (*s && isspace(*s))
7203       ++s;
7204
7205     if (!*s) {
7206         *d = 0;
7207         return static_buf;
7208     }
7209
7210     if (*s == '~') {
7211         if (*(s+1) == '/') {
7212           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7213           strcat(d, s+1);
7214         }
7215         else {
7216           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7217           { char *p; if(p = strchr(buf, '/')) *p = 0; }
7218           pwd = getpwnam(buf);
7219           if (!pwd)
7220             {
7221               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7222                       buf, path);
7223               return NULL;
7224             }
7225           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7226           strcat(d, strchr(s+1, '/'));
7227         }
7228     }
7229     else
7230       safeStrCpy(d, s, 4*MSG_SIZ );
7231
7232     return static_buf;
7233 }
7234
7235 char *
7236 HostName ()
7237 {
7238     static char host_name[MSG_SIZ];
7239
7240 #if HAVE_GETHOSTNAME
7241     gethostname(host_name, MSG_SIZ);
7242     return host_name;
7243 #else  /* not HAVE_GETHOSTNAME */
7244 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7245     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7246     return host_name;
7247 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7248     return "localhost";
7249 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7250 #endif /* not HAVE_GETHOSTNAME */
7251 }
7252
7253 XtIntervalId delayedEventTimerXID = 0;
7254 DelayedEventCallback delayedEventCallback = 0;
7255
7256 void
7257 FireDelayedEvent ()
7258 {
7259     delayedEventTimerXID = 0;
7260     delayedEventCallback();
7261 }
7262
7263 void
7264 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
7265 {
7266     if(delayedEventTimerXID && delayedEventCallback == cb)
7267         // [HGM] alive: replace, rather than add or flush identical event
7268         XtRemoveTimeOut(delayedEventTimerXID);
7269     delayedEventCallback = cb;
7270     delayedEventTimerXID =
7271       XtAppAddTimeOut(appContext, millisec,
7272                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7273 }
7274
7275 DelayedEventCallback
7276 GetDelayedEvent ()
7277 {
7278   if (delayedEventTimerXID) {
7279     return delayedEventCallback;
7280   } else {
7281     return NULL;
7282   }
7283 }
7284
7285 void
7286 CancelDelayedEvent ()
7287 {
7288   if (delayedEventTimerXID) {
7289     XtRemoveTimeOut(delayedEventTimerXID);
7290     delayedEventTimerXID = 0;
7291   }
7292 }
7293
7294 XtIntervalId loadGameTimerXID = 0;
7295
7296 int
7297 LoadGameTimerRunning ()
7298 {
7299     return loadGameTimerXID != 0;
7300 }
7301
7302 int
7303 StopLoadGameTimer ()
7304 {
7305     if (loadGameTimerXID != 0) {
7306         XtRemoveTimeOut(loadGameTimerXID);
7307         loadGameTimerXID = 0;
7308         return TRUE;
7309     } else {
7310         return FALSE;
7311     }
7312 }
7313
7314 void
7315 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
7316 {
7317     loadGameTimerXID = 0;
7318     AutoPlayGameLoop();
7319 }
7320
7321 void
7322 StartLoadGameTimer (long millisec)
7323 {
7324     loadGameTimerXID =
7325       XtAppAddTimeOut(appContext, millisec,
7326                       (XtTimerCallbackProc) LoadGameTimerCallback,
7327                       (XtPointer) 0);
7328 }
7329
7330 XtIntervalId analysisClockXID = 0;
7331
7332 void
7333 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
7334 {
7335     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7336          || appData.icsEngineAnalyze) { // [DM]
7337         AnalysisPeriodicEvent(0);
7338         StartAnalysisClock();
7339     }
7340 }
7341
7342 void
7343 StartAnalysisClock ()
7344 {
7345     analysisClockXID =
7346       XtAppAddTimeOut(appContext, 2000,
7347                       (XtTimerCallbackProc) AnalysisClockCallback,
7348                       (XtPointer) 0);
7349 }
7350
7351 XtIntervalId clockTimerXID = 0;
7352
7353 int
7354 ClockTimerRunning ()
7355 {
7356     return clockTimerXID != 0;
7357 }
7358
7359 int
7360 StopClockTimer ()
7361 {
7362     if (clockTimerXID != 0) {
7363         XtRemoveTimeOut(clockTimerXID);
7364         clockTimerXID = 0;
7365         return TRUE;
7366     } else {
7367         return FALSE;
7368     }
7369 }
7370
7371 void
7372 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
7373 {
7374     clockTimerXID = 0;
7375     DecrementClocks();
7376 }
7377
7378 void
7379 StartClockTimer (long millisec)
7380 {
7381     clockTimerXID =
7382       XtAppAddTimeOut(appContext, millisec,
7383                       (XtTimerCallbackProc) ClockTimerCallback,
7384                       (XtPointer) 0);
7385 }
7386
7387 void
7388 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
7389 {
7390     char buf[MSG_SIZ];
7391     Arg args[16];
7392
7393     /* check for low time warning */
7394     Pixel foregroundOrWarningColor = timerForegroundPixel;
7395
7396     if (timer > 0 &&
7397         appData.lowTimeWarning &&
7398         (timer / 1000) < appData.icsAlarmTime)
7399       foregroundOrWarningColor = lowTimeWarningColor;
7400
7401     if (appData.clockMode) {
7402       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7403       XtSetArg(args[0], XtNlabel, buf);
7404     } else {
7405       snprintf(buf, MSG_SIZ, "%s  ", color);
7406       XtSetArg(args[0], XtNlabel, buf);
7407     }
7408
7409     if (highlight) {
7410
7411         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7412         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7413     } else {
7414         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7415         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7416     }
7417
7418     XtSetValues(w, args, 3);
7419 }
7420
7421 void
7422 DisplayWhiteClock (long timeRemaining, int highlight)
7423 {
7424     Arg args[16];
7425
7426     if(appData.noGUI) return;
7427     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7428     if (highlight && iconPixmap == bIconPixmap) {
7429         iconPixmap = wIconPixmap;
7430         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7431         XtSetValues(shellWidget, args, 1);
7432     }
7433 }
7434
7435 void
7436 DisplayBlackClock (long timeRemaining, int highlight)
7437 {
7438     Arg args[16];
7439
7440     if(appData.noGUI) return;
7441     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7442     if (highlight && iconPixmap == wIconPixmap) {
7443         iconPixmap = bIconPixmap;
7444         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7445         XtSetValues(shellWidget, args, 1);
7446     }
7447 }
7448
7449 #define CPNone 0
7450 #define CPReal 1
7451 #define CPComm 2
7452 #define CPSock 3
7453 #define CPLoop 4
7454 typedef int CPKind;
7455
7456 typedef struct {
7457     CPKind kind;
7458     int pid;
7459     int fdTo, fdFrom;
7460 } ChildProc;
7461
7462
7463 int
7464 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
7465 {
7466     char *argv[64], *p;
7467     int i, pid;
7468     int to_prog[2], from_prog[2];
7469     ChildProc *cp;
7470     char buf[MSG_SIZ];
7471
7472     if (appData.debugMode) {
7473         fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7474     }
7475
7476     /* We do NOT feed the cmdLine to the shell; we just
7477        parse it into blank-separated arguments in the
7478        most simple-minded way possible.
7479        */
7480     i = 0;
7481     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7482     p = buf;
7483     for (;;) {
7484         while(*p == ' ') p++;
7485         argv[i++] = p;
7486         if(*p == '"' || *p == '\'')
7487              p = strchr(++argv[i-1], *p);
7488         else p = strchr(p, ' ');
7489         if (p == NULL) break;
7490         *p++ = NULLCHAR;
7491     }
7492     argv[i] = NULL;
7493
7494     SetUpChildIO(to_prog, from_prog);
7495
7496     if ((pid = fork()) == 0) {
7497         /* Child process */
7498         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7499         close(to_prog[1]);     // first close the unused pipe ends
7500         close(from_prog[0]);
7501         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7502         dup2(from_prog[1], 1);
7503         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7504         close(from_prog[1]);                   // and closing again loses one of the pipes!
7505         if(fileno(stderr) >= 2) // better safe than sorry...
7506                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7507
7508         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7509             perror(dir);
7510             exit(1);
7511         }
7512
7513         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7514
7515         execvp(argv[0], argv);
7516
7517         /* If we get here, exec failed */
7518         perror(argv[0]);
7519         exit(1);
7520     }
7521
7522     /* Parent process */
7523     close(to_prog[0]);
7524     close(from_prog[1]);
7525
7526     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7527     cp->kind = CPReal;
7528     cp->pid = pid;
7529     cp->fdFrom = from_prog[0];
7530     cp->fdTo = to_prog[1];
7531     *pr = (ProcRef) cp;
7532     return 0;
7533 }
7534
7535 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7536 static RETSIGTYPE
7537 AlarmCallBack (int n)
7538 {
7539     return;
7540 }
7541
7542 void
7543 DestroyChildProcess (ProcRef pr, int signalType)
7544 {
7545     ChildProc *cp = (ChildProc *) pr;
7546
7547     if (cp->kind != CPReal) return;
7548     cp->kind = CPNone;
7549     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7550         signal(SIGALRM, AlarmCallBack);
7551         alarm(3);
7552         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7553             kill(cp->pid, SIGKILL); // kill it forcefully
7554             wait((int *) 0);        // and wait again
7555         }
7556     } else {
7557         if (signalType) {
7558             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7559         }
7560         /* Process is exiting either because of the kill or because of
7561            a quit command sent by the backend; either way, wait for it to die.
7562         */
7563         wait((int *) 0);
7564     }
7565     close(cp->fdFrom);
7566     close(cp->fdTo);
7567 }
7568
7569 void
7570 InterruptChildProcess (ProcRef pr)
7571 {
7572     ChildProc *cp = (ChildProc *) pr;
7573
7574     if (cp->kind != CPReal) return;
7575     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7576 }
7577
7578 int
7579 OpenTelnet (char *host, char *port, ProcRef *pr)
7580 {
7581     char cmdLine[MSG_SIZ];
7582
7583     if (port[0] == NULLCHAR) {
7584       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7585     } else {
7586       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7587     }
7588     return StartChildProcess(cmdLine, "", pr);
7589 }
7590
7591 int
7592 OpenTCP (char *host, char *port, ProcRef *pr)
7593 {
7594 #if OMIT_SOCKETS
7595     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7596 #else  /* !OMIT_SOCKETS */
7597     struct addrinfo hints;
7598     struct addrinfo *ais, *ai;
7599     int error;
7600     int s=0;
7601     ChildProc *cp;
7602
7603     memset(&hints, 0, sizeof(hints));
7604     hints.ai_family = AF_UNSPEC;
7605     hints.ai_socktype = SOCK_STREAM;
7606
7607     error = getaddrinfo(host, port, &hints, &ais);
7608     if (error != 0) {
7609       /* a getaddrinfo error is not an errno, so can't return it */
7610       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7611               host, port, gai_strerror(error));
7612       return ENOENT;
7613     }
7614      
7615     for (ai = ais; ai != NULL; ai = ai->ai_next) {
7616       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7617         error = errno;
7618         continue;
7619       }
7620       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7621         error = errno;
7622         continue;
7623       }
7624       error = 0;
7625       break;
7626     }
7627     freeaddrinfo(ais);
7628
7629     if (error != 0) {
7630       return error;
7631     }
7632
7633     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7634     cp->kind = CPSock;
7635     cp->pid = 0;
7636     cp->fdFrom = s;
7637     cp->fdTo = s;
7638     *pr = (ProcRef) cp;
7639 #endif /* !OMIT_SOCKETS */
7640
7641     return 0;
7642 }
7643
7644 int
7645 OpenCommPort (char *name, ProcRef *pr)
7646 {
7647     int fd;
7648     ChildProc *cp;
7649
7650     fd = open(name, 2, 0);
7651     if (fd < 0) return errno;
7652
7653     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7654     cp->kind = CPComm;
7655     cp->pid = 0;
7656     cp->fdFrom = fd;
7657     cp->fdTo = fd;
7658     *pr = (ProcRef) cp;
7659
7660     return 0;
7661 }
7662
7663 int
7664 OpenLoopback (ProcRef *pr)
7665 {
7666     ChildProc *cp;
7667     int to[2], from[2];
7668
7669     SetUpChildIO(to, from);
7670
7671     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7672     cp->kind = CPLoop;
7673     cp->pid = 0;
7674     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7675     cp->fdTo = to[1];
7676     *pr = (ProcRef) cp;
7677
7678     return 0;
7679 }
7680
7681 int
7682 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
7683 {
7684     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7685     return -1;
7686 }
7687
7688 #define INPUT_SOURCE_BUF_SIZE 8192
7689
7690 typedef struct {
7691     CPKind kind;
7692     int fd;
7693     int lineByLine;
7694     char *unused;
7695     InputCallback func;
7696     XtInputId xid;
7697     char buf[INPUT_SOURCE_BUF_SIZE];
7698     VOIDSTAR closure;
7699 } InputSource;
7700
7701 void
7702 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
7703 {
7704     InputSource *is = (InputSource *) closure;
7705     int count;
7706     int error;
7707     char *p, *q;
7708
7709     if (is->lineByLine) {
7710         count = read(is->fd, is->unused,
7711                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7712         if (count <= 0) {
7713             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7714             return;
7715         }
7716         is->unused += count;
7717         p = is->buf;
7718         while (p < is->unused) {
7719             q = memchr(p, '\n', is->unused - p);
7720             if (q == NULL) break;
7721             q++;
7722             (is->func)(is, is->closure, p, q - p, 0);
7723             p = q;
7724         }
7725         q = is->buf;
7726         while (p < is->unused) {
7727             *q++ = *p++;
7728         }
7729         is->unused = q;
7730     } else {
7731         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7732         if (count == -1)
7733           error = errno;
7734         else
7735           error = 0;
7736         (is->func)(is, is->closure, is->buf, count, error);
7737     }
7738 }
7739
7740 InputSourceRef
7741 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
7742 {
7743     InputSource *is;
7744     ChildProc *cp = (ChildProc *) pr;
7745
7746     is = (InputSource *) calloc(1, sizeof(InputSource));
7747     is->lineByLine = lineByLine;
7748     is->func = func;
7749     if (pr == NoProc) {
7750         is->kind = CPReal;
7751         is->fd = fileno(stdin);
7752     } else {
7753         is->kind = cp->kind;
7754         is->fd = cp->fdFrom;
7755     }
7756     if (lineByLine) {
7757         is->unused = is->buf;
7758     }
7759
7760     is->xid = XtAppAddInput(appContext, is->fd,
7761                             (XtPointer) (XtInputReadMask),
7762                             (XtInputCallbackProc) DoInputCallback,
7763                             (XtPointer) is);
7764     is->closure = closure;
7765     return (InputSourceRef) is;
7766 }
7767
7768 void
7769 RemoveInputSource (InputSourceRef isr)
7770 {
7771     InputSource *is = (InputSource *) isr;
7772
7773     if (is->xid == 0) return;
7774     XtRemoveInput(is->xid);
7775     is->xid = 0;
7776 }
7777
7778 int
7779 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
7780 {
7781     static int line = 0;
7782     ChildProc *cp = (ChildProc *) pr;
7783     int outCount;
7784
7785     if (pr == NoProc)
7786     {
7787         if (appData.noJoin || !appData.useInternalWrap)
7788             outCount = fwrite(message, 1, count, stdout);
7789         else
7790         {
7791             int width = get_term_width();
7792             int len = wrap(NULL, message, count, width, &line);
7793             char *msg = malloc(len);
7794             int dbgchk;
7795
7796             if (!msg)
7797                 outCount = fwrite(message, 1, count, stdout);
7798             else
7799             {
7800                 dbgchk = wrap(msg, message, count, width, &line);
7801                 if (dbgchk != len && appData.debugMode)
7802                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7803                 outCount = fwrite(msg, 1, dbgchk, stdout);
7804                 free(msg);
7805             }
7806         }
7807     }
7808     else
7809       outCount = write(cp->fdTo, message, count);
7810
7811     if (outCount == -1)
7812       *outError = errno;
7813     else
7814       *outError = 0;
7815
7816     return outCount;
7817 }
7818
7819 /* Output message to process, with "ms" milliseconds of delay
7820    between each character. This is needed when sending the logon
7821    script to ICC, which for some reason doesn't like the
7822    instantaneous send. */
7823 int
7824 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
7825 {
7826     ChildProc *cp = (ChildProc *) pr;
7827     int outCount = 0;
7828     int r;
7829
7830     while (count--) {
7831         r = write(cp->fdTo, message++, 1);
7832         if (r == -1) {
7833             *outError = errno;
7834             return outCount;
7835         }
7836         ++outCount;
7837         if (msdelay >= 0)
7838           TimeDelay(msdelay);
7839     }
7840
7841     return outCount;
7842 }
7843
7844 /****   Animation code by Hugh Fisher, DCS, ANU.
7845
7846         Known problem: if a window overlapping the board is
7847         moved away while a piece is being animated underneath,
7848         the newly exposed area won't be updated properly.
7849         I can live with this.
7850
7851         Known problem: if you look carefully at the animation
7852         of pieces in mono mode, they are being drawn as solid
7853         shapes without interior detail while moving. Fixing
7854         this would be a major complication for minimal return.
7855 ****/
7856
7857 /*      Masks for XPM pieces. Black and white pieces can have
7858         different shapes, but in the interest of retaining my
7859         sanity pieces must have the same outline on both light
7860         and dark squares, and all pieces must use the same
7861         background square colors/images.                */
7862
7863 static int xpmDone = 0;
7864
7865 static void
7866 CreateAnimMasks (int pieceDepth)
7867 {
7868   ChessSquare   piece;
7869   Pixmap        buf;
7870   GC            bufGC, maskGC;
7871   int           kind, n;
7872   unsigned long plane;
7873   XGCValues     values;
7874
7875   /* Need a bitmap just to get a GC with right depth */
7876   buf = XCreatePixmap(xDisplay, xBoardWindow,
7877                         8, 8, 1);
7878   values.foreground = 1;
7879   values.background = 0;
7880   /* Don't use XtGetGC, not read only */
7881   maskGC = XCreateGC(xDisplay, buf,
7882                     GCForeground | GCBackground, &values);
7883   XFreePixmap(xDisplay, buf);
7884
7885   buf = XCreatePixmap(xDisplay, xBoardWindow,
7886                       squareSize, squareSize, pieceDepth);
7887   values.foreground = XBlackPixel(xDisplay, xScreen);
7888   values.background = XWhitePixel(xDisplay, xScreen);
7889   bufGC = XCreateGC(xDisplay, buf,
7890                     GCForeground | GCBackground, &values);
7891
7892   for (piece = WhitePawn; piece <= BlackKing; piece++) {
7893     /* Begin with empty mask */
7894     if(!xpmDone) // [HGM] pieces: keep using existing
7895     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7896                                  squareSize, squareSize, 1);
7897     XSetFunction(xDisplay, maskGC, GXclear);
7898     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7899                    0, 0, squareSize, squareSize);
7900
7901     /* Take a copy of the piece */
7902     if (White(piece))
7903       kind = 0;
7904     else
7905       kind = 2;
7906     XSetFunction(xDisplay, bufGC, GXcopy);
7907     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7908               buf, bufGC,
7909               0, 0, squareSize, squareSize, 0, 0);
7910
7911     /* XOR the background (light) over the piece */
7912     XSetFunction(xDisplay, bufGC, GXxor);
7913     if (useImageSqs)
7914       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7915                 0, 0, squareSize, squareSize, 0, 0);
7916     else {
7917       XSetForeground(xDisplay, bufGC, lightSquareColor);
7918       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7919     }
7920
7921     /* We now have an inverted piece image with the background
7922        erased. Construct mask by just selecting all the non-zero
7923        pixels - no need to reconstruct the original image.      */
7924     XSetFunction(xDisplay, maskGC, GXor);
7925     plane = 1;
7926     /* Might be quicker to download an XImage and create bitmap
7927        data from it rather than this N copies per piece, but it
7928        only takes a fraction of a second and there is a much
7929        longer delay for loading the pieces.             */
7930     for (n = 0; n < pieceDepth; n ++) {
7931       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7932                  0, 0, squareSize, squareSize,
7933                  0, 0, plane);
7934       plane = plane << 1;
7935     }
7936   }
7937   /* Clean up */
7938   XFreePixmap(xDisplay, buf);
7939   XFreeGC(xDisplay, bufGC);
7940   XFreeGC(xDisplay, maskGC);
7941 }
7942
7943 static void
7944 InitAnimState (AnimState *anim, XWindowAttributes *info)
7945 {
7946   XtGCMask  mask;
7947   XGCValues values;
7948
7949   /* Each buffer is square size, same depth as window */
7950   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7951                         squareSize, squareSize, info->depth);
7952   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7953                         squareSize, squareSize, info->depth);
7954
7955   /* Create a plain GC for blitting */
7956   mask = GCForeground | GCBackground | GCFunction |
7957          GCPlaneMask | GCGraphicsExposures;
7958   values.foreground = XBlackPixel(xDisplay, xScreen);
7959   values.background = XWhitePixel(xDisplay, xScreen);
7960   values.function   = GXcopy;
7961   values.plane_mask = AllPlanes;
7962   values.graphics_exposures = False;
7963   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7964
7965   /* Piece will be copied from an existing context at
7966      the start of each new animation/drag. */
7967   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7968
7969   /* Outline will be a read-only copy of an existing */
7970   anim->outlineGC = None;
7971 }
7972
7973 static void
7974 CreateAnimVars ()
7975 {
7976   XWindowAttributes info;
7977
7978   if (xpmDone && gameInfo.variant == oldVariant) return;
7979   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
7980   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7981
7982   InitAnimState(&game, &info);
7983   InitAnimState(&player, &info);
7984
7985   /* For XPM pieces, we need bitmaps to use as masks. */
7986   if (useImages)
7987     CreateAnimMasks(info.depth), xpmDone = 1;
7988 }
7989
7990 #ifndef HAVE_USLEEP
7991
7992 static Boolean frameWaiting;
7993
7994 static RETSIGTYPE
7995 FrameAlarm (int sig)
7996 {
7997   frameWaiting = False;
7998   /* In case System-V style signals.  Needed?? */
7999   signal(SIGALRM, FrameAlarm);
8000 }
8001
8002 static void
8003 FrameDelay (int time)
8004 {
8005   struct itimerval delay;
8006
8007   XSync(xDisplay, False);
8008
8009   if (time > 0) {
8010     frameWaiting = True;
8011     signal(SIGALRM, FrameAlarm);
8012     delay.it_interval.tv_sec =
8013       delay.it_value.tv_sec = time / 1000;
8014     delay.it_interval.tv_usec =
8015       delay.it_value.tv_usec = (time % 1000) * 1000;
8016     setitimer(ITIMER_REAL, &delay, NULL);
8017     while (frameWaiting) pause();
8018     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8019     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8020     setitimer(ITIMER_REAL, &delay, NULL);
8021   }
8022 }
8023
8024 #else
8025
8026 static void
8027 FrameDelay (int time)
8028 {
8029   XSync(xDisplay, False);
8030   if (time > 0)
8031     usleep(time * 1000);
8032 }
8033
8034 #endif
8035
8036 void
8037 DoSleep (int n)
8038 {
8039     FrameDelay(n);
8040 }
8041
8042 /*      Convert board position to corner of screen rect and color       */
8043
8044 static void
8045 ScreenSquare (int column, int row, XPoint *pt, int *color)
8046 {
8047   if (flipView) {
8048     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8049     pt->y = lineGap + row * (squareSize + lineGap);
8050   } else {
8051     pt->x = lineGap + column * (squareSize + lineGap);
8052     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8053   }
8054   *color = SquareColor(row, column);
8055 }
8056
8057 /*      Convert window coords to square                 */
8058
8059 static void
8060 BoardSquare (int x, int y, int *column, int *row)
8061 {
8062   *column = EventToSquare(x, BOARD_WIDTH);
8063   if (flipView && *column >= 0)
8064     *column = BOARD_WIDTH - 1 - *column;
8065   *row = EventToSquare(y, BOARD_HEIGHT);
8066   if (!flipView && *row >= 0)
8067     *row = BOARD_HEIGHT - 1 - *row;
8068 }
8069
8070 /*   Utilities  */
8071
8072 #undef Max  /* just in case */
8073 #undef Min
8074 #define Max(a, b) ((a) > (b) ? (a) : (b))
8075 #define Min(a, b) ((a) < (b) ? (a) : (b))
8076
8077 static void
8078 SetRect (XRectangle *rect, int x, int y, int width, int height)
8079 {
8080   rect->x = x;
8081   rect->y = y;
8082   rect->width  = width;
8083   rect->height = height;
8084 }
8085
8086 /*      Test if two frames overlap. If they do, return
8087         intersection rect within old and location of
8088         that rect within new. */
8089
8090 static Boolean
8091 Intersect ( XPoint *old, XPoint *new, int size, XRectangle *area, XPoint *pt)
8092 {
8093   if (old->x > new->x + size || new->x > old->x + size ||
8094       old->y > new->y + size || new->y > old->y + size) {
8095     return False;
8096   } else {
8097     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8098             size - abs(old->x - new->x), size - abs(old->y - new->y));
8099     pt->x = Max(old->x - new->x, 0);
8100     pt->y = Max(old->y - new->y, 0);
8101     return True;
8102   }
8103 }
8104
8105 /*      For two overlapping frames, return the rect(s)
8106         in the old that do not intersect with the new.   */
8107
8108 static void
8109 CalcUpdateRects (XPoint *old, XPoint *new, int size, XRectangle update[], int *nUpdates)
8110 {
8111   int        count;
8112
8113   /* If old = new (shouldn't happen) then nothing to draw */
8114   if (old->x == new->x && old->y == new->y) {
8115     *nUpdates = 0;
8116     return;
8117   }
8118   /* Work out what bits overlap. Since we know the rects
8119      are the same size we don't need a full intersect calc. */
8120   count = 0;
8121   /* Top or bottom edge? */
8122   if (new->y > old->y) {
8123     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8124     count ++;
8125   } else if (old->y > new->y) {
8126     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8127                               size, old->y - new->y);
8128     count ++;
8129   }
8130   /* Left or right edge - don't overlap any update calculated above. */
8131   if (new->x > old->x) {
8132     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8133                               new->x - old->x, size - abs(new->y - old->y));
8134     count ++;
8135   } else if (old->x > new->x) {
8136     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8137                               old->x - new->x, size - abs(new->y - old->y));
8138     count ++;
8139   }
8140   /* Done */
8141   *nUpdates = count;
8142 }
8143
8144 /*      Generate a series of frame coords from start->mid->finish.
8145         The movement rate doubles until the half way point is
8146         reached, then halves back down to the final destination,
8147         which gives a nice slow in/out effect. The algorithmn
8148         may seem to generate too many intermediates for short
8149         moves, but remember that the purpose is to attract the
8150         viewers attention to the piece about to be moved and
8151         then to where it ends up. Too few frames would be less
8152         noticeable.                                             */
8153
8154 static void
8155 Tween (XPoint *start, XPoint *mid, XPoint *finish, int factor, XPoint frames[], int *nFrames)
8156 {
8157   int fraction, n, count;
8158
8159   count = 0;
8160
8161   /* Slow in, stepping 1/16th, then 1/8th, ... */
8162   fraction = 1;
8163   for (n = 0; n < factor; n++)
8164     fraction *= 2;
8165   for (n = 0; n < factor; n++) {
8166     frames[count].x = start->x + (mid->x - start->x) / fraction;
8167     frames[count].y = start->y + (mid->y - start->y) / fraction;
8168     count ++;
8169     fraction = fraction / 2;
8170   }
8171
8172   /* Midpoint */
8173   frames[count] = *mid;
8174   count ++;
8175
8176   /* Slow out, stepping 1/2, then 1/4, ... */
8177   fraction = 2;
8178   for (n = 0; n < factor; n++) {
8179     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8180     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8181     count ++;
8182     fraction = fraction * 2;
8183   }
8184   *nFrames = count;
8185 }
8186
8187 /*      Draw a piece on the screen without disturbing what's there      */
8188
8189 static void
8190 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
8191 {
8192   GC source;
8193
8194   /* Bitmap for piece being moved. */
8195   if (appData.monoMode) {
8196       *mask = *pieceToSolid(piece);
8197   } else if (useImages) {
8198 #if HAVE_LIBXPM
8199       *mask = xpmMask[piece];
8200 #else
8201       *mask = ximMaskPm[piece];
8202 #endif
8203   } else {
8204       *mask = *pieceToSolid(piece);
8205   }
8206
8207   /* GC for piece being moved. Square color doesn't matter, but
8208      since it gets modified we make a copy of the original. */
8209   if (White(piece)) {
8210     if (appData.monoMode)
8211       source = bwPieceGC;
8212     else
8213       source = wlPieceGC;
8214   } else {
8215     if (appData.monoMode)
8216       source = wbPieceGC;
8217     else
8218       source = blPieceGC;
8219   }
8220   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8221
8222   /* Outline only used in mono mode and is not modified */
8223   if (White(piece))
8224     *outline = bwPieceGC;
8225   else
8226     *outline = wbPieceGC;
8227 }
8228
8229 static void
8230 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
8231 {
8232   int   kind;
8233
8234   if (!useImages) {
8235     /* Draw solid rectangle which will be clipped to shape of piece */
8236     XFillRectangle(xDisplay, dest, clip,
8237                    0, 0, squareSize, squareSize);
8238     if (appData.monoMode)
8239       /* Also draw outline in contrasting color for black
8240          on black / white on white cases                */
8241       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8242                  0, 0, squareSize, squareSize, 0, 0, 1);
8243   } else {
8244     /* Copy the piece */
8245     if (White(piece))
8246       kind = 0;
8247     else
8248       kind = 2;
8249     if(appData.upsideDown && flipView) kind ^= 2;
8250     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8251               dest, clip,
8252               0, 0, squareSize, squareSize,
8253               0, 0);
8254   }
8255 }
8256
8257 /* Animate the movement of a single piece */
8258
8259 static void
8260 BeginAnimation (AnimState *anim, ChessSquare piece, int startColor, XPoint *start)
8261 {
8262   Pixmap mask;
8263
8264   if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8265   /* The old buffer is initialised with the start square (empty) */
8266   BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8267   anim->prevFrame = *start;
8268
8269   /* The piece will be drawn using its own bitmap as a matte    */
8270   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8271   XSetClipMask(xDisplay, anim->pieceGC, mask);
8272 }
8273
8274 static void
8275 AnimationFrame (AnimState *anim, XPoint *frame, ChessSquare piece)
8276 {
8277   XRectangle updates[4];
8278   XRectangle overlap;
8279   XPoint     pt;
8280   int        count, i;
8281
8282   /* Save what we are about to draw into the new buffer */
8283   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8284             frame->x, frame->y, squareSize, squareSize,
8285             0, 0);
8286
8287   /* Erase bits of the previous frame */
8288   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8289     /* Where the new frame overlapped the previous,
8290        the contents in newBuf are wrong. */
8291     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8292               overlap.x, overlap.y,
8293               overlap.width, overlap.height,
8294               pt.x, pt.y);
8295     /* Repaint the areas in the old that don't overlap new */
8296     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8297     for (i = 0; i < count; i++)
8298       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8299                 updates[i].x - anim->prevFrame.x,
8300                 updates[i].y - anim->prevFrame.y,
8301                 updates[i].width, updates[i].height,
8302                 updates[i].x, updates[i].y);
8303   } else {
8304     /* Easy when no overlap */
8305     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8306                   0, 0, squareSize, squareSize,
8307                   anim->prevFrame.x, anim->prevFrame.y);
8308   }
8309
8310   /* Save this frame for next time round */
8311   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8312                 0, 0, squareSize, squareSize,
8313                 0, 0);
8314   anim->prevFrame = *frame;
8315
8316   /* Draw piece over original screen contents, not current,
8317      and copy entire rect. Wipes out overlapping piece images. */
8318   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8319   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8320                 0, 0, squareSize, squareSize,
8321                 frame->x, frame->y);
8322 }
8323
8324 static void
8325 EndAnimation (AnimState *anim, XPoint *finish)
8326 {
8327   XRectangle updates[4];
8328   XRectangle overlap;
8329   XPoint     pt;
8330   int        count, i;
8331
8332   /* The main code will redraw the final square, so we
8333      only need to erase the bits that don't overlap.    */
8334   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8335     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8336     for (i = 0; i < count; i++)
8337       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8338                 updates[i].x - anim->prevFrame.x,
8339                 updates[i].y - anim->prevFrame.y,
8340                 updates[i].width, updates[i].height,
8341                 updates[i].x, updates[i].y);
8342   } else {
8343     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8344                 0, 0, squareSize, squareSize,
8345                 anim->prevFrame.x, anim->prevFrame.y);
8346   }
8347 }
8348
8349 static void
8350 FrameSequence (AnimState *anim, ChessSquare piece, int startColor, XPoint *start, XPoint *finish, XPoint frames[], int nFrames)
8351 {
8352   int n;
8353
8354   BeginAnimation(anim, piece, startColor, start);
8355   for (n = 0; n < nFrames; n++) {
8356     AnimationFrame(anim, &(frames[n]), piece);
8357     FrameDelay(appData.animSpeed);
8358   }
8359   EndAnimation(anim, finish);
8360 }
8361
8362 void
8363 AnimateAtomicCapture (Board board, int fromX, int fromY, int toX, int toY)
8364 {
8365     int i, x, y;
8366     ChessSquare piece = board[fromY][toY];
8367     board[fromY][toY] = EmptySquare;
8368     DrawPosition(FALSE, board);
8369     if (flipView) {
8370         x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8371         y = lineGap + toY * (squareSize + lineGap);
8372     } else {
8373         x = lineGap + toX * (squareSize + lineGap);
8374         y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8375     }
8376     for(i=1; i<4*kFactor; i++) {
8377         int r = squareSize * 9 * i/(20*kFactor - 5);
8378         XFillArc(xDisplay, xBoardWindow, highlineGC,
8379                 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8380         FrameDelay(appData.animSpeed);
8381     }
8382     board[fromY][toY] = piece;
8383 }
8384
8385 /* Main control logic for deciding what to animate and how */
8386
8387 void
8388 AnimateMove (Board board, int fromX, int fromY, int toX, int toY)
8389 {
8390   ChessSquare piece;
8391   int hop;
8392   XPoint      start, finish, mid;
8393   XPoint      frames[kFactor * 2 + 1];
8394   int         nFrames, startColor, endColor;
8395
8396   /* Are we animating? */
8397   if (!appData.animate || appData.blindfold)
8398     return;
8399
8400   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8401      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8402         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8403
8404   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8405   piece = board[fromY][fromX];
8406   if (piece >= EmptySquare) return;
8407
8408 #if DONT_HOP
8409   hop = FALSE;
8410 #else
8411   hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8412 #endif
8413
8414   ScreenSquare(fromX, fromY, &start, &startColor);
8415   ScreenSquare(toX, toY, &finish, &endColor);
8416
8417   if (hop) {
8418     /* Knight: make straight movement then diagonal */
8419     if (abs(toY - fromY) < abs(toX - fromX)) {
8420        mid.x = start.x + (finish.x - start.x) / 2;
8421        mid.y = start.y;
8422      } else {
8423        mid.x = start.x;
8424        mid.y = start.y + (finish.y - start.y) / 2;
8425      }
8426   } else {
8427     mid.x = start.x + (finish.x - start.x) / 2;
8428     mid.y = start.y + (finish.y - start.y) / 2;
8429   }
8430
8431   /* Don't use as many frames for very short moves */
8432   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8433     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8434   else
8435     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8436   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8437   if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8438     int i,j;
8439     for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8440       if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8441   }
8442
8443   /* Be sure end square is redrawn */
8444   damage[0][toY][toX] = True;
8445 }
8446
8447 void
8448 DragPieceBegin (int x, int y, Boolean instantly)
8449 {
8450     int  boardX, boardY, color;
8451     XPoint corner;
8452
8453     /* Are we animating? */
8454     if (!appData.animateDragging || appData.blindfold)
8455       return;
8456
8457     /* Figure out which square we start in and the
8458        mouse position relative to top left corner. */
8459     BoardSquare(x, y, &boardX, &boardY);
8460     player.startBoardX = boardX;
8461     player.startBoardY = boardY;
8462     ScreenSquare(boardX, boardY, &corner, &color);
8463     player.startSquare  = corner;
8464     player.startColor   = color;
8465     /* As soon as we start dragging, the piece will jump slightly to
8466        be centered over the mouse pointer. */
8467     player.mouseDelta.x = squareSize/2;
8468     player.mouseDelta.y = squareSize/2;
8469     /* Initialise animation */
8470     player.dragPiece = PieceForSquare(boardX, boardY);
8471     /* Sanity check */
8472     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8473         player.dragActive = True;
8474         BeginAnimation(&player, player.dragPiece, color, &corner);
8475         /* Mark this square as needing to be redrawn. Note that
8476            we don't remove the piece though, since logically (ie
8477            as seen by opponent) the move hasn't been made yet. */
8478            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8479               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8480            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8481                      corner.x, corner.y, squareSize, squareSize,
8482                      0, 0); // [HGM] zh: unstack in stead of grab
8483            if(gatingPiece != EmptySquare) {
8484                /* Kludge alert: When gating we want the introduced
8485                   piece to appear on the from square. To generate an
8486                   image of it, we draw it on the board, copy the image,
8487                   and draw the original piece again. */
8488                ChessSquare piece = boards[currentMove][boardY][boardX];
8489                DrawSquare(boardY, boardX, gatingPiece, 0);
8490                XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8491                      corner.x, corner.y, squareSize, squareSize, 0, 0);
8492                DrawSquare(boardY, boardX, piece, 0);
8493            }
8494         damage[0][boardY][boardX] = True;
8495     } else {
8496         player.dragActive = False;
8497     }
8498 }
8499
8500 void
8501 ChangeDragPiece (ChessSquare piece)
8502 {
8503   Pixmap mask;
8504   player.dragPiece = piece;
8505   /* The piece will be drawn using its own bitmap as a matte    */
8506   SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8507   XSetClipMask(xDisplay, player.pieceGC, mask);
8508 }
8509
8510 static void
8511 DragPieceMove (int x, int y)
8512 {
8513     XPoint corner;
8514
8515     /* Are we animating? */
8516     if (!appData.animateDragging || appData.blindfold)
8517       return;
8518
8519     /* Sanity check */
8520     if (! player.dragActive)
8521       return;
8522     /* Move piece, maintaining same relative position
8523        of mouse within square    */
8524     corner.x = x - player.mouseDelta.x;
8525     corner.y = y - player.mouseDelta.y;
8526     AnimationFrame(&player, &corner, player.dragPiece);
8527 #if HIGHDRAG*0
8528     if (appData.highlightDragging) {
8529         int boardX, boardY;
8530         BoardSquare(x, y, &boardX, &boardY);
8531         SetHighlights(fromX, fromY, boardX, boardY);
8532     }
8533 #endif
8534 }
8535
8536 void
8537 DragPieceEnd (int x, int y)
8538 {
8539     int boardX, boardY, color;
8540     XPoint corner;
8541
8542     /* Are we animating? */
8543     if (!appData.animateDragging || appData.blindfold)
8544       return;
8545
8546     /* Sanity check */
8547     if (! player.dragActive)
8548       return;
8549     /* Last frame in sequence is square piece is
8550        placed on, which may not match mouse exactly. */
8551     BoardSquare(x, y, &boardX, &boardY);
8552     ScreenSquare(boardX, boardY, &corner, &color);
8553     EndAnimation(&player, &corner);
8554
8555     /* Be sure end square is redrawn */
8556     damage[0][boardY][boardX] = True;
8557
8558     /* This prevents weird things happening with fast successive
8559        clicks which on my Sun at least can cause motion events
8560        without corresponding press/release. */
8561     player.dragActive = False;
8562 }
8563
8564 /* Handle expose event while piece being dragged */
8565
8566 static void
8567 DrawDragPiece ()
8568 {
8569   if (!player.dragActive || appData.blindfold)
8570     return;
8571
8572   /* What we're doing: logically, the move hasn't been made yet,
8573      so the piece is still in it's original square. But visually
8574      it's being dragged around the board. So we erase the square
8575      that the piece is on and draw it at the last known drag point. */
8576   BlankSquare(player.startSquare.x, player.startSquare.y,
8577                 player.startColor, EmptySquare, xBoardWindow, 1);
8578   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8579   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8580 }
8581
8582 #include <sys/ioctl.h>
8583 int
8584 get_term_width ()
8585 {
8586     int fd, default_width;
8587
8588     fd = STDIN_FILENO;
8589     default_width = 79; // this is FICS default anyway...
8590
8591 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8592     struct ttysize win;
8593     if (!ioctl(fd, TIOCGSIZE, &win))
8594         default_width = win.ts_cols;
8595 #elif defined(TIOCGWINSZ)
8596     struct winsize win;
8597     if (!ioctl(fd, TIOCGWINSZ, &win))
8598         default_width = win.ws_col;
8599 #endif
8600     return default_width;
8601 }
8602
8603 void
8604 update_ics_width ()
8605 {
8606   static int old_width = 0;
8607   int new_width = get_term_width();
8608
8609   if (old_width != new_width)
8610     ics_printf("set width %d\n", new_width);
8611   old_width = new_width;
8612 }
8613
8614 void
8615 NotifyFrontendLogin ()
8616 {
8617     update_ics_width();
8618 }
8619
8620 /* [AS] Arrow highlighting support */
8621
8622 static double A_WIDTH = 5; /* Width of arrow body */
8623
8624 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
8625 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
8626
8627 static double
8628 Sqr (double x)
8629 {
8630     return x*x;
8631 }
8632
8633 static int
8634 Round (double x)
8635 {
8636     return (int) (x + 0.5);
8637 }
8638
8639 void
8640 SquareToPos (int rank, int file, int *x, int *y)
8641 {
8642     if (flipView) {
8643         *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8644         *y = lineGap + rank * (squareSize + lineGap);
8645     } else {
8646         *x = lineGap + file * (squareSize + lineGap);
8647         *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8648     }
8649 }
8650
8651 /* Draw an arrow between two points using current settings */
8652 void
8653 DrawArrowBetweenPoints (int s_x, int s_y, int d_x, int d_y)
8654 {
8655     XPoint arrow[8];
8656     double dx, dy, j, k, x, y;
8657
8658     if( d_x == s_x ) {
8659         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8660
8661         arrow[0].x = s_x + A_WIDTH + 0.5;
8662         arrow[0].y = s_y;
8663
8664         arrow[1].x = s_x + A_WIDTH + 0.5;
8665         arrow[1].y = d_y - h;
8666
8667         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8668         arrow[2].y = d_y - h;
8669
8670         arrow[3].x = d_x;
8671         arrow[3].y = d_y;
8672
8673         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8674         arrow[5].y = d_y - h;
8675
8676         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8677         arrow[4].y = d_y - h;
8678
8679         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8680         arrow[6].y = s_y;
8681     }
8682     else if( d_y == s_y ) {
8683         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8684
8685         arrow[0].x = s_x;
8686         arrow[0].y = s_y + A_WIDTH + 0.5;
8687
8688         arrow[1].x = d_x - w;
8689         arrow[1].y = s_y + A_WIDTH + 0.5;
8690
8691         arrow[2].x = d_x - w;
8692         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8693
8694         arrow[3].x = d_x;
8695         arrow[3].y = d_y;
8696
8697         arrow[5].x = d_x - w;
8698         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8699
8700         arrow[4].x = d_x - w;
8701         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8702
8703         arrow[6].x = s_x;
8704         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8705     }
8706     else {
8707         /* [AS] Needed a lot of paper for this! :-) */
8708         dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8709         dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8710
8711         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8712
8713         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8714
8715         x = s_x;
8716         y = s_y;
8717
8718         arrow[0].x = Round(x - j);
8719         arrow[0].y = Round(y + j*dx);
8720
8721         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
8722         arrow[1].y = Round(arrow[0].y - 2*j*dx);
8723
8724         if( d_x > s_x ) {
8725             x = (double) d_x - k;
8726             y = (double) d_y - k*dy;
8727         }
8728         else {
8729             x = (double) d_x + k;
8730             y = (double) d_y + k*dy;
8731         }
8732
8733         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8734
8735         arrow[6].x = Round(x - j);
8736         arrow[6].y = Round(y + j*dx);
8737
8738         arrow[2].x = Round(arrow[6].x + 2*j);
8739         arrow[2].y = Round(arrow[6].y - 2*j*dx);
8740
8741         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8742         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8743
8744         arrow[4].x = d_x;
8745         arrow[4].y = d_y;
8746
8747         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8748         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8749     }
8750
8751     XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8752     if(appData.monoMode) arrow[7] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, arrow, 8, CoordModeOrigin);
8753 //    Polygon( hdc, arrow, 7 );
8754 }
8755
8756 void
8757 ArrowDamage (int s_col, int s_row, int d_col, int d_row)
8758 {
8759     int hor, vert, i;
8760     hor = 64*s_col + 32; vert = 64*s_row + 32;
8761     for(i=0; i<= 64; i++) {
8762             damage[0][vert+6>>6][hor+6>>6] = True;
8763             damage[0][vert-6>>6][hor+6>>6] = True;
8764             damage[0][vert+6>>6][hor-6>>6] = True;
8765             damage[0][vert-6>>6][hor-6>>6] = True;
8766             hor += d_col - s_col; vert += d_row - s_row;
8767     }
8768 }
8769
8770 /* [AS] Draw an arrow between two squares */
8771 void
8772 DrawArrowBetweenSquares (int s_col, int s_row, int d_col, int d_row)
8773 {
8774     int s_x, s_y, d_x, d_y;
8775
8776     if( s_col == d_col && s_row == d_row ) {
8777         return;
8778     }
8779
8780     /* Get source and destination points */
8781     SquareToPos( s_row, s_col, &s_x, &s_y);
8782     SquareToPos( d_row, d_col, &d_x, &d_y);
8783
8784     if( d_y > s_y ) {
8785         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8786     }
8787     else if( d_y < s_y ) {
8788         d_y += squareSize / 2 + squareSize / 4;
8789     }
8790     else {
8791         d_y += squareSize / 2;
8792     }
8793
8794     if( d_x > s_x ) {
8795         d_x += squareSize / 2 - squareSize / 4;
8796     }
8797     else if( d_x < s_x ) {
8798         d_x += squareSize / 2 + squareSize / 4;
8799     }
8800     else {
8801         d_x += squareSize / 2;
8802     }
8803
8804     s_x += squareSize / 2;
8805     s_y += squareSize / 2;
8806
8807     /* Adjust width */
8808     A_WIDTH = squareSize / 14.; //[HGM] make float
8809
8810     DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8811     ArrowDamage(s_col, s_row, d_col, d_row);
8812 }
8813
8814 Boolean
8815 IsDrawArrowEnabled ()
8816 {
8817     return appData.highlightMoveWithArrow && squareSize >= 32;
8818 }
8819
8820 void
8821 DrawArrowHighlight (int fromX, int fromY, int toX,int toY)
8822 {
8823     if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8824         DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8825 }
8826
8827 void
8828 UpdateLogos (int displ)
8829 {
8830     return; // no logos in XBoard yet
8831 }
8832