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