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