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