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