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