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