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