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