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