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