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