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