a676d24f6f1a9859e62a1811d6218959e565b730
[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 SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
477 void GameListOptionsPopDown P(());
478 void GenericPopDown P(());
479 void update_ics_width P(());
480 int get_term_width P(());
481 int CopyMemoProc P(());
482 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
483 Boolean IsDrawArrowEnabled P(());
484
485 /*
486 * XBoard depends on Xt R4 or higher
487 */
488 int xtVersion = XtSpecificationRelease;
489
490 int xScreen;
491 Display *xDisplay;
492 Window xBoardWindow;
493 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
494   jailSquareColor, highlightSquareColor, premoveHighlightColor;
495 Pixel lowTimeWarningColor;
496 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
497   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
498   wjPieceGC, bjPieceGC, prelineGC, countGC;
499 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
500 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
501   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
502   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
503   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
504   ICSInputShell, fileNameShell, askQuestionShell;
505 Widget historyShell, evalGraphShell, gameListShell;
506 int hOffset; // [HGM] dual
507 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
508 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
509 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
510 #if ENABLE_NLS
511 XFontSet fontSet, clockFontSet;
512 #else
513 Font clockFontID;
514 XFontStruct *clockFontStruct;
515 #endif
516 Font coordFontID, countFontID;
517 XFontStruct *coordFontStruct, *countFontStruct;
518 XtAppContext appContext;
519 char *layoutName;
520 char *oldICSInteractionTitle;
521
522 FileProc fileProc;
523 char *fileOpenMode;
524 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
525
526 Position commentX = -1, commentY = -1;
527 Dimension commentW, commentH;
528 typedef unsigned int BoardSize;
529 BoardSize boardSize;
530 Boolean chessProgram;
531
532 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
533 int squareSize, smallLayout = 0, tinyLayout = 0,
534   marginW, marginH, // [HGM] for run-time resizing
535   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
536   ICSInputBoxUp = False, askQuestionUp = False,
537   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
538   errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
539 Pixel timerForegroundPixel, timerBackgroundPixel;
540 Pixel buttonForegroundPixel, buttonBackgroundPixel;
541 char *chessDir, *programName, *programVersion,
542   *gameCopyFilename, *gamePasteFilename;
543 Boolean alwaysOnTop = False;
544 Boolean saveSettingsOnExit;
545 char *settingsFileName;
546 char *icsTextMenuString;
547 char *icsNames;
548 char *firstChessProgramNames;
549 char *secondChessProgramNames;
550
551 WindowPlacement wpMain;
552 WindowPlacement wpConsole;
553 WindowPlacement wpComment;
554 WindowPlacement wpMoveHistory;
555 WindowPlacement wpEvalGraph;
556 WindowPlacement wpEngineOutput;
557 WindowPlacement wpGameList;
558 WindowPlacement wpTags;
559
560 extern Widget shells[];
561 extern Boolean shellUp[];
562
563 #define SOLID 0
564 #define OUTLINE 1
565 Pixmap pieceBitmap[2][(int)BlackPawn];
566 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
567 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
568 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
569 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
570 Pixmap xpmBoardBitmap[2];
571 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
572 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
573 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
574 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
575 XImage *ximLightSquare, *ximDarkSquare;
576 XImage *xim_Cross;
577
578 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
579 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
580
581 #define White(piece) ((int)(piece) < (int)BlackPawn)
582
583 /* Variables for doing smooth animation. This whole thing
584    would be much easier if the board was double-buffered,
585    but that would require a fairly major rewrite.       */
586
587 typedef struct {
588         Pixmap  saveBuf;
589         Pixmap  newBuf;
590         GC      blitGC, pieceGC, outlineGC;
591         XPoint  startSquare, prevFrame, mouseDelta;
592         int     startColor;
593         int     dragPiece;
594         Boolean dragActive;
595         int     startBoardX, startBoardY;
596     } AnimState;
597
598 /* There can be two pieces being animated at once: a player
599    can begin dragging a piece before the remote opponent has moved. */
600
601 static AnimState game, player;
602
603 /* Bitmaps for use as masks when drawing XPM pieces.
604    Need one for each black and white piece.             */
605 static Pixmap xpmMask[BlackKing + 1];
606
607 /* This magic number is the number of intermediate frames used
608    in each half of the animation. For short moves it's reduced
609    by 1. The total number of frames will be factor * 2 + 1.  */
610 #define kFactor    4
611
612 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
613
614 MenuItem fileMenu[] = {
615     {N_("New Game        Ctrl+N"),        "New Game", ResetProc},
616     {N_("New Shuffle Game ..."),          "New Shuffle Game", ShuffleMenuProc},
617     {N_("New Variant ...   Alt+Shift+V"), "New Variant", NewVariantProc},      // [HGM] variant: not functional yet
618     {"----", NULL, NothingProc},
619     {N_("Load Game       Ctrl+O"),        "Load Game", LoadGameProc},
620     {N_("Load Position    Ctrl+Shift+O"), "Load Position", LoadPositionProc},
621 //    {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
622 //    {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
623 //    {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
624     {N_("Next Position     Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
625     {N_("Prev Position     Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
626     {"----", NULL, NothingProc},
627 //    {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
628     {N_("Save Game       Ctrl+S"),        "Save Game", SaveGameProc},
629     {N_("Save Position    Ctrl+Shift+S"), "Save Position", SavePositionProc},
630     {"----", NULL, NothingProc},
631     {N_("Mail Move"),            "Mail Move", MailMoveProc},
632     {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
633     {"----", NULL, NothingProc},
634     {N_("Quit                 Ctr+Q"), "Exit", QuitProc},
635     {NULL, NULL, NULL}
636 };
637
638 MenuItem editMenu[] = {
639     {N_("Copy Game    Ctrl+C"),        "Copy Game", CopyGameProc},
640     {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
641     {N_("Copy Game List"),        "Copy Game List", CopyGameListProc},
642     {"----", NULL, NothingProc},
643     {N_("Paste Game    Ctrl+V"),        "Paste Game", PasteGameProc},
644     {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
645     {"----", NULL, NothingProc},
646     {N_("Edit Game      Ctrl+E"),        "Edit Game", EditGameProc},
647     {N_("Edit Position   Ctrl+Shift+E"), "Edit Position", EditPositionProc},
648     {N_("Edit Tags"),                    "Edit Tags", EditTagsProc},
649     {N_("Edit Comment"),                 "Edit Comment", EditCommentProc},
650     {N_("Edit Book"),                    "Edit Book", EditBookProc},
651     {"----", NULL, NothingProc},
652     {N_("Revert              Home"), "Revert", RevertProc},
653     {N_("Annotate"),                 "Annotate", AnnotateProc},
654     {N_("Truncate Game  End"),       "Truncate Game", TruncateGameProc},
655     {"----", NULL, NothingProc},
656     {N_("Backward         Alt+Left"),   "Backward", BackwardProc},
657     {N_("Forward           Alt+Right"), "Forward", ForwardProc},
658     {N_("Back to Start     Alt+Home"),  "Back to Start", ToStartProc},
659     {N_("Forward to End Alt+End"),      "Forward to End", ToEndProc},
660     {NULL, NULL, NULL}
661 };
662
663 MenuItem viewMenu[] = {
664     {N_("Flip View             F2"),         "Flip View", FlipViewProc},
665     {"----", NULL, NothingProc},
666     {N_("Engine Output      Alt+Shift+O"),   "Show Engine Output", EngineOutputProc},
667     {N_("Move History       Alt+Shift+H"),   "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
668     {N_("Evaluation Graph  Alt+Shift+E"),    "Show Evaluation Graph", EvalGraphProc},
669     {N_("Game List            Alt+Shift+G"), "Show Game List", ShowGameListProc},
670     {N_("ICS text menu"), "ICStex", IcsTextProc},
671     {"----", NULL, NothingProc},
672     {N_("Tags"),             "Show Tags", EditTagsProc},
673     {N_("Comments"),         "Show Comments", EditCommentProc},
674     {N_("ICS Input Box"),    "ICS Input Box", IcsInputBoxProc},
675     {"----", NULL, NothingProc},
676     {N_("Board..."),          "Board Options", BoardOptionsProc},
677     {N_("Game List Tags..."), "Game List", GameListOptionsPopUp},
678     {NULL, NULL, NULL}
679 };
680
681 MenuItem modeMenu[] = {
682     {N_("Machine White  Ctrl+W"), "Machine White", MachineWhiteProc},
683     {N_("Machine Black  Ctrl+B"), "Machine Black", MachineBlackProc},
684     {N_("Two Machines   Ctrl+T"), "Two Machines", TwoMachinesProc},
685     {N_("Analysis Mode  Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
686     {N_("Analyze File      Ctrl+F"), "Analyze File", AnalyzeFileProc },
687     {N_("Edit Game         Ctrl+E"), "Edit Game", EditGameProc},
688     {N_("Edit Position      Ctrl+Shift+E"), "Edit Position", EditPositionProc},
689     {N_("Training"),      "Training", TrainingProc},
690     {N_("ICS Client"),    "ICS Client", IcsClientProc},
691     {"----", NULL, NothingProc},
692     {N_("Machine Match"),         "Machine Match", MatchProc},
693     {N_("Pause               Pause"),         "Pause", PauseProc},
694     {NULL, NULL, NULL}
695 };
696
697 MenuItem actionMenu[] = {
698     {N_("Accept             F3"), "Accept", AcceptProc},
699     {N_("Decline            F4"), "Decline", DeclineProc},
700     {N_("Rematch           F12"), "Rematch", RematchProc},
701     {"----", NULL, NothingProc},
702     {N_("Call Flag          F5"), "Call Flag", CallFlagProc},
703     {N_("Draw                F6"), "Draw", DrawProc},
704     {N_("Adjourn            F7"),  "Adjourn", AdjournProc},
705     {N_("Abort                F8"),"Abort", AbortProc},
706     {N_("Resign              F9"), "Resign", ResignProc},
707     {"----", NULL, NothingProc},
708     {N_("Stop Observing  F10"), "Stop Observing", StopObservingProc},
709     {N_("Stop Examining  F11"), "Stop Examining", StopExaminingProc},
710     {N_("Upload to Examine"),   "Upload to Examine", UploadProc},
711     {"----", NULL, NothingProc},
712     {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
713     {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
714     {N_("Adjudicate Draw"),     "Adjudicate Draw", AdjuDrawProc},
715     {NULL, NULL, NULL}
716 };
717
718 MenuItem engineMenu[] = {
719     {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
720     {"----", NULL, NothingProc},
721     {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
722     {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
723     {"----", NULL, NothingProc},
724     {N_("Hint"), "Hint", HintProc},
725     {N_("Book"), "Book", BookProc},
726     {"----", NULL, NothingProc},
727     {N_("Move Now     Ctrl+M"),     "Move Now", MoveNowProc},
728     {N_("Retract Move  Ctrl+X"), "Retract Move", RetractMoveProc},
729     {NULL, NULL, NULL}
730 };
731
732 MenuItem optionsMenu[] = {
733 #define OPTIONSDIALOG
734 #ifdef OPTIONSDIALOG
735     {N_("General ..."), "General", OptionsProc},
736 #endif
737     {N_("Time Control ...       Alt+Shift+T"), "Time Control", TimeControlProc},
738     {N_("Common Engine ...  Alt+Shift+U"),     "Common Engine", UciMenuProc},
739     {N_("Adjudications ...      Alt+Shift+J"), "Adjudications", EngineMenuProc},
740     {N_("ICS ..."),    "ICS", IcsOptionsProc},
741     {N_("Match ..."), "Match", MatchOptionsProc},
742     {N_("Load Game ..."),    "Load Game", LoadOptionsProc},
743     {N_("Save Game ..."),    "Save Game", SaveOptionsProc},
744 //    {N_(" ..."),    "", OptionsProc},
745     {N_("Game List ..."),    "Game List", GameListOptionsPopUp},
746     {N_("Sounds ..."),    "Sounds", SoundOptionsProc},
747     {"----", NULL, NothingProc},
748 #ifndef OPTIONSDIALOG
749     {N_("Always Queen        Ctrl+Shift+Q"),   "Always Queen", AlwaysQueenProc},
750     {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
751     {N_("Animate Moving      Ctrl+Shift+A"),   "Animate Moving", AnimateMovingProc},
752     {N_("Auto Flag               Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
753     {N_("Auto Flip View"),   "Auto Flip View", AutoflipProc},
754     {N_("Blindfold"),        "Blindfold", BlindfoldProc},
755     {N_("Flash Moves"),      "Flash Moves", FlashMovesProc},
756 #if HIGHDRAG
757     {N_("Highlight Dragging"),    "Highlight Dragging", HighlightDraggingProc},
758 #endif
759     {N_("Highlight Last Move"),   "Highlight Last Move", HighlightLastMoveProc},
760     {N_("Highlight With Arrow"),  "Arrow", HighlightArrowProc},
761     {N_("Move Sound"),            "Move Sound", MoveSoundProc},
762 //    {N_("ICS Alarm"),             "ICS Alarm", IcsAlarmProc},
763     {N_("One-Click Moving"),      "OneClick", OneClickProc},
764     {N_("Periodic Updates"),      "Periodic Updates", PeriodicUpdatesProc},
765     {N_("Ponder Next Move  Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
766     {N_("Popup Exit Message"),    "Popup Exit Message", PopupExitMessageProc},
767     {N_("Popup Move Errors"),     "Popup Move Errors", PopupMoveErrorsProc},
768 //    {N_("Premove"),               "Premove", PremoveProc},
769     {N_("Show Coords"),           "Show Coords", ShowCoordsProc},
770     {N_("Hide Thinking        Ctrl+Shift+H"),   "Hide Thinking", HideThinkingProc},
771     {N_("Test Legality          Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
772     {"----", NULL, NothingProc},
773 #endif
774     {N_("Save Settings Now"),     "Save Settings Now", SaveSettingsProc},
775     {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
776     {NULL, NULL, NULL}
777 };
778
779 MenuItem helpMenu[] = {
780     {N_("Info XBoard"),     "Info XBoard", InfoProc},
781     {N_("Man XBoard   F1"), "Man XBoard", ManProc},
782     {"----", NULL, NothingProc},
783     {N_("About XBoard"), "About XBoard", AboutProc},
784     {NULL, NULL, NULL}
785 };
786
787 Menu menuBar[] = {
788     {N_("File"),    "File", fileMenu},
789     {N_("Edit"),    "Edit", editMenu},
790     {N_("View"),    "View", viewMenu},
791     {N_("Mode"),    "Mode", modeMenu},
792     {N_("Action"),  "Action", actionMenu},
793     {N_("Engine"),  "Engine", engineMenu},
794     {N_("Options"), "Options", optionsMenu},
795     {N_("Help"),    "Help", helpMenu},
796     {NULL, NULL, NULL}
797 };
798
799 #define PAUSE_BUTTON "P"
800 MenuItem buttonBar[] = {
801     {"<<", "<<", ToStartProc},
802     {"<", "<", BackwardProc},
803     {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
804     {">", ">", ForwardProc},
805     {">>", ">>", ToEndProc},
806     {NULL, NULL, NULL}
807 };
808
809 #define PIECE_MENU_SIZE 18
810 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
811     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
812       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
813       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
814       N_("Empty square"), N_("Clear board") },
815     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
816       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
817       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
818       N_("Empty square"), N_("Clear board") }
819 };
820 /* must be in same order as pieceMenuStrings! */
821 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
822     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
823         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
824         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
825         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
826     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
827         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
828         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
829         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
830 };
831
832 #define DROP_MENU_SIZE 6
833 String dropMenuStrings[DROP_MENU_SIZE] = {
834     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
835   };
836 /* must be in same order as dropMenuStrings! */
837 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
838     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
839     WhiteRook, WhiteQueen
840 };
841
842 typedef struct {
843     char piece;
844     char* widget;
845 } DropMenuEnables;
846
847 DropMenuEnables dmEnables[] = {
848     { 'P', "Pawn" },
849     { 'N', "Knight" },
850     { 'B', "Bishop" },
851     { 'R', "Rook" },
852     { 'Q', "Queen" }
853 };
854
855 Arg shellArgs[] = {
856     { XtNwidth, 0 },
857     { XtNheight, 0 },
858     { XtNminWidth, 0 },
859     { XtNminHeight, 0 },
860     { XtNmaxWidth, 0 },
861     { XtNmaxHeight, 0 }
862 };
863
864 Arg layoutArgs[] = {
865     { XtNborderWidth, 0 },
866     { XtNdefaultDistance, 0 },
867 };
868
869 Arg formArgs[] = {
870     { XtNborderWidth, 0 },
871     { XtNresizable, (XtArgVal) True },
872 };
873
874 Arg boardArgs[] = {
875     { XtNborderWidth, 0 },
876     { XtNwidth, 0 },
877     { XtNheight, 0 }
878 };
879
880 Arg titleArgs[] = {
881     { XtNjustify, (XtArgVal) XtJustifyRight },
882     { XtNlabel, (XtArgVal) "..." },
883     { XtNresizable, (XtArgVal) True },
884     { XtNresize, (XtArgVal) False }
885 };
886
887 Arg messageArgs[] = {
888     { XtNjustify, (XtArgVal) XtJustifyLeft },
889     { XtNlabel, (XtArgVal) "..." },
890     { XtNresizable, (XtArgVal) True },
891     { XtNresize, (XtArgVal) False }
892 };
893
894 Arg timerArgs[] = {
895     { XtNborderWidth, 0 },
896     { XtNjustify, (XtArgVal) XtJustifyLeft }
897 };
898
899 XtResource clientResources[] = {
900     { "flashCount", "flashCount", XtRInt, sizeof(int),
901         XtOffset(AppDataPtr, flashCount), XtRImmediate,
902         (XtPointer) FLASH_COUNT  },
903 };
904
905 XrmOptionDescRec shellOptions[] = {
906     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
907     { "-flash", "flashCount", XrmoptionNoArg, "3" },
908     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
909 };
910
911 XtActionsRec boardActions[] = {
912     { "DrawPosition", DrawPositionProc },
913     { "HandleUserMove", HandleUserMove },
914     { "AnimateUserMove", AnimateUserMove },
915     { "HandlePV", HandlePV },
916     { "SelectPV", SelectPV },
917     { "StopPV", StopPV },
918     { "FileNameAction", FileNameAction },
919     { "AskQuestionProc", AskQuestionProc },
920     { "AskQuestionReplyAction", AskQuestionReplyAction },
921     { "PieceMenuPopup", PieceMenuPopup },
922     { "WhiteClock", WhiteClock },
923     { "BlackClock", BlackClock },
924     { "Iconify", Iconify },
925     { "ResetProc", ResetProc },
926     { "NewVariantProc", NewVariantProc },
927     { "LoadGameProc", LoadGameProc },
928     { "LoadNextGameProc", LoadNextGameProc },
929     { "LoadPrevGameProc", LoadPrevGameProc },
930     { "LoadSelectedProc", LoadSelectedProc },
931     { "SetFilterProc", SetFilterProc },
932     { "ReloadGameProc", ReloadGameProc },
933     { "LoadPositionProc", LoadPositionProc },
934     { "LoadNextPositionProc", LoadNextPositionProc },
935     { "LoadPrevPositionProc", LoadPrevPositionProc },
936     { "ReloadPositionProc", ReloadPositionProc },
937     { "CopyPositionProc", CopyPositionProc },
938     { "PastePositionProc", PastePositionProc },
939     { "CopyGameProc", CopyGameProc },
940     { "CopyGameListProc", CopyGameListProc },
941     { "PasteGameProc", PasteGameProc },
942     { "SaveGameProc", SaveGameProc },
943     { "SavePositionProc", SavePositionProc },
944     { "MailMoveProc", MailMoveProc },
945     { "ReloadCmailMsgProc", ReloadCmailMsgProc },
946     { "QuitProc", QuitProc },
947     { "MachineWhiteProc", MachineWhiteProc },
948     { "MachineBlackProc", MachineBlackProc },
949     { "AnalysisModeProc", AnalyzeModeProc },
950     { "AnalyzeFileProc", AnalyzeFileProc },
951     { "TwoMachinesProc", TwoMachinesProc },
952     { "IcsClientProc", IcsClientProc },
953     { "EditGameProc", EditGameProc },
954     { "EditPositionProc", EditPositionProc },
955     { "TrainingProc", EditPositionProc },
956     { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
957     { "EvalGraphProc", EvalGraphProc},       // [HGM] Winboard_x avaluation graph window
958     { "ShowGameListProc", ShowGameListProc },
959     { "ShowMoveListProc", HistoryShowProc},
960     { "EditTagsProc", EditCommentProc },
961     { "EditBookProc", EditBookProc },
962     { "EditCommentProc", EditCommentProc },
963     { "IcsInputBoxProc", IcsInputBoxProc },
964     { "PauseProc", PauseProc },
965     { "AcceptProc", AcceptProc },
966     { "DeclineProc", DeclineProc },
967     { "RematchProc", RematchProc },
968     { "CallFlagProc", CallFlagProc },
969     { "DrawProc", DrawProc },
970     { "AdjournProc", AdjournProc },
971     { "AbortProc", AbortProc },
972     { "ResignProc", ResignProc },
973     { "AdjuWhiteProc", AdjuWhiteProc },
974     { "AdjuBlackProc", AdjuBlackProc },
975     { "AdjuDrawProc", AdjuDrawProc },
976     { "TypeInProc", TypeInProc },
977     { "EnterKeyProc", EnterKeyProc },
978     { "UpKeyProc", UpKeyProc },
979     { "DownKeyProc", DownKeyProc },
980     { "StopObservingProc", StopObservingProc },
981     { "StopExaminingProc", StopExaminingProc },
982     { "UploadProc", UploadProc },
983     { "BackwardProc", BackwardProc },
984     { "ForwardProc", ForwardProc },
985     { "ToStartProc", ToStartProc },
986     { "ToEndProc", ToEndProc },
987     { "RevertProc", RevertProc },
988     { "AnnotateProc", AnnotateProc },
989     { "TruncateGameProc", TruncateGameProc },
990     { "MoveNowProc", MoveNowProc },
991     { "RetractMoveProc", RetractMoveProc },
992     { "EngineMenuProc", (XtActionProc) EngineMenuProc },
993     { "UciMenuProc", (XtActionProc) UciMenuProc },
994     { "TimeControlProc", (XtActionProc) TimeControlProc },
995     { "FlipViewProc", FlipViewProc },
996     { "PonderNextMoveProc", PonderNextMoveProc },
997 #ifndef OPTIONSDIALOG
998     { "AlwaysQueenProc", AlwaysQueenProc },
999     { "AnimateDraggingProc", AnimateDraggingProc },
1000     { "AnimateMovingProc", AnimateMovingProc },
1001     { "AutoflagProc", AutoflagProc },
1002     { "AutoflipProc", AutoflipProc },
1003     { "BlindfoldProc", BlindfoldProc },
1004     { "FlashMovesProc", FlashMovesProc },
1005 #if HIGHDRAG
1006     { "HighlightDraggingProc", HighlightDraggingProc },
1007 #endif
1008     { "HighlightLastMoveProc", HighlightLastMoveProc },
1009 //    { "IcsAlarmProc", IcsAlarmProc },
1010     { "MoveSoundProc", MoveSoundProc },
1011     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
1012     { "PopupExitMessageProc", PopupExitMessageProc },
1013     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
1014 //    { "PremoveProc", PremoveProc },
1015     { "ShowCoordsProc", ShowCoordsProc },
1016     { "ShowThinkingProc", ShowThinkingProc },
1017     { "HideThinkingProc", HideThinkingProc },
1018     { "TestLegalityProc", TestLegalityProc },
1019 #endif
1020     { "SaveSettingsProc", SaveSettingsProc },
1021     { "SaveOnExitProc", SaveOnExitProc },
1022     { "InfoProc", InfoProc },
1023     { "ManProc", ManProc },
1024     { "HintProc", HintProc },
1025     { "BookProc", BookProc },
1026     { "AboutGameProc", AboutGameProc },
1027     { "AboutProc", AboutProc },
1028     { "DebugProc", DebugProc },
1029     { "NothingProc", NothingProc },
1030     { "CommentClick", (XtActionProc) CommentClick },
1031     { "CommentPopDown", (XtActionProc) CommentPopDown },
1032     { "TagsPopDown", (XtActionProc) TagsPopDown },
1033     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1034     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1035     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1036     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1037     { "GameListPopDown", (XtActionProc) GameListPopDown },
1038     { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
1039     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1040     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
1041     { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
1042     { "GenericPopDown", (XtActionProc) GenericPopDown },
1043     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
1044     { "SelectMove", (XtActionProc) SelectMove },
1045 };
1046
1047 char globalTranslations[] =
1048   ":<Key>F9: ResignProc() \n \
1049    :Ctrl<Key>n: ResetProc() \n \
1050    :Meta<Key>V: NewVariantProc() \n \
1051    :Ctrl<Key>o: LoadGameProc() \n \
1052    :Meta<Key>Next: LoadNextGameProc() \n \
1053    :Meta<Key>Prior: LoadPrevGameProc() \n \
1054    :Ctrl<Key>s: SaveGameProc() \n \
1055    :Ctrl<Key>c: CopyGameProc() \n \
1056    :Ctrl<Key>v: PasteGameProc() \n \
1057    :Ctrl<Key>O: LoadPositionProc() \n \
1058    :Shift<Key>Next: LoadNextPositionProc() \n \
1059    :Shift<Key>Prior: LoadPrevPositionProc() \n \
1060    :Ctrl<Key>S: SavePositionProc() \n \
1061    :Ctrl<Key>C: CopyPositionProc() \n \
1062    :Ctrl<Key>V: PastePositionProc() \n \
1063    :Ctrl<Key>q: QuitProc() \n \
1064    :Ctrl<Key>w: MachineWhiteProc() \n \
1065    :Ctrl<Key>b: MachineBlackProc() \n \
1066    :Ctrl<Key>t: TwoMachinesProc() \n \
1067    :Ctrl<Key>a: AnalysisModeProc() \n \
1068    :Ctrl<Key>f: AnalyzeFileProc() \n \
1069    :Ctrl<Key>e: EditGameProc() \n \
1070    :Ctrl<Key>E: EditPositionProc() \n \
1071    :Meta<Key>O: EngineOutputProc() \n \
1072    :Meta<Key>E: EvalGraphProc() \n \
1073    :Meta<Key>G: ShowGameListProc() \n \
1074    :Meta<Key>H: ShowMoveListProc() \n \
1075    :<Key>Pause: PauseProc() \n \
1076    :<Key>F3: AcceptProc() \n \
1077    :<Key>F4: DeclineProc() \n \
1078    :<Key>F12: RematchProc() \n \
1079    :<Key>F5: CallFlagProc() \n \
1080    :<Key>F6: DrawProc() \n \
1081    :<Key>F7: AdjournProc() \n \
1082    :<Key>F8: AbortProc() \n \
1083    :<Key>F10: StopObservingProc() \n \
1084    :<Key>F11: StopExaminingProc() \n \
1085    :Meta Ctrl<Key>F12: DebugProc() \n \
1086    :Meta<Key>End: ToEndProc() \n \
1087    :Meta<Key>Right: ForwardProc() \n \
1088    :Meta<Key>Home: ToStartProc() \n \
1089    :Meta<Key>Left: BackwardProc() \n \
1090    :<Key>Home: RevertProc() \n \
1091    :<Key>End: TruncateGameProc() \n \
1092    :Ctrl<Key>m: MoveNowProc() \n \
1093    :Ctrl<Key>x: RetractMoveProc() \n \
1094    :Meta<Key>J: EngineMenuProc() \n \
1095    :Meta<Key>U: UciMenuProc() \n \
1096    :Meta<Key>T: TimeControlProc() \n \
1097    :Ctrl<Key>P: PonderNextMoveProc() \n "
1098 #ifndef OPTIONSDIALOG
1099     "\
1100    :Ctrl<Key>Q: AlwaysQueenProc() \n \
1101    :Ctrl<Key>F: AutoflagProc() \n \
1102    :Ctrl<Key>A: AnimateMovingProc() \n \
1103    :Ctrl<Key>L: TestLegalityProc() \n \
1104    :Ctrl<Key>H: HideThinkingProc() \n "
1105 #endif
1106    "\
1107    :<Key>-: Iconify() \n \
1108    :<Key>F1: ManProc() \n \
1109    :<Key>F2: FlipViewProc() \n \
1110    <KeyDown>.: BackwardProc() \n \
1111    <KeyUp>.: ForwardProc() \n \
1112    Shift<Key>1: AskQuestionProc(\"Direct command\",\
1113                                 \"Send to chess program:\",,1) \n \
1114    Shift<Key>2: AskQuestionProc(\"Direct command\",\
1115                                 \"Send to second chess program:\",,2) \n";
1116
1117 char boardTranslations[] =
1118    "<Btn1Down>: HandleUserMove(0) \n \
1119    Shift<Btn1Up>: HandleUserMove(1) \n \
1120    <Btn1Up>: HandleUserMove(0) \n \
1121    <Btn1Motion>: AnimateUserMove() \n \
1122    <Btn3Motion>: HandlePV() \n \
1123    <Btn3Up>: PieceMenuPopup(menuB) \n \
1124    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1125                  PieceMenuPopup(menuB) \n \
1126    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1127                  PieceMenuPopup(menuW) \n \
1128    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1129                  PieceMenuPopup(menuW) \n \
1130    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1131                  PieceMenuPopup(menuB) \n";
1132
1133 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1134 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1135
1136 char ICSInputTranslations[] =
1137     "<Key>Up: UpKeyProc() \n "
1138     "<Key>Down: DownKeyProc() \n "
1139     "<Key>Return: EnterKeyProc() \n";
1140
1141 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1142 //             as the widget is destroyed before the up-click can call extend-end
1143 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1144
1145 String xboardResources[] = {
1146     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1147     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1148     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1149     NULL
1150   };
1151
1152
1153 /* Max possible square size */
1154 #define MAXSQSIZE 256
1155
1156 static int xpm_avail[MAXSQSIZE];
1157
1158 #ifdef HAVE_DIR_STRUCT
1159
1160 /* Extract piece size from filename */
1161 static int
1162 xpm_getsize(name, len, ext)
1163      char *name;
1164      int len;
1165      char *ext;
1166 {
1167     char *p, *d;
1168     char buf[10];
1169
1170     if (len < 4)
1171       return 0;
1172
1173     if ((p=strchr(name, '.')) == NULL ||
1174         StrCaseCmp(p+1, ext) != 0)
1175       return 0;
1176
1177     p = name + 3;
1178     d = buf;
1179
1180     while (*p && isdigit(*p))
1181       *(d++) = *(p++);
1182
1183     *d = 0;
1184     return atoi(buf);
1185 }
1186
1187 /* Setup xpm_avail */
1188 static int
1189 xpm_getavail(dirname, ext)
1190      char *dirname;
1191      char *ext;
1192 {
1193     DIR *dir;
1194     struct dirent *ent;
1195     int  i;
1196
1197     for (i=0; i<MAXSQSIZE; ++i)
1198       xpm_avail[i] = 0;
1199
1200     if (appData.debugMode)
1201       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1202
1203     dir = opendir(dirname);
1204     if (!dir)
1205       {
1206           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1207                   programName, dirname);
1208           exit(1);
1209       }
1210
1211     while ((ent=readdir(dir)) != NULL) {
1212         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1213         if (i > 0 && i < MAXSQSIZE)
1214           xpm_avail[i] = 1;
1215     }
1216
1217     closedir(dir);
1218
1219     return 0;
1220 }
1221
1222 void
1223 xpm_print_avail(fp, ext)
1224      FILE *fp;
1225      char *ext;
1226 {
1227     int i;
1228
1229     fprintf(fp, _("Available `%s' sizes:\n"), ext);
1230     for (i=1; i<MAXSQSIZE; ++i) {
1231         if (xpm_avail[i])
1232           printf("%d\n", i);
1233     }
1234 }
1235
1236 /* Return XPM piecesize closest to size */
1237 int
1238 xpm_closest_to(dirname, size, ext)
1239      char *dirname;
1240      int size;
1241      char *ext;
1242 {
1243     int i;
1244     int sm_diff = MAXSQSIZE;
1245     int sm_index = 0;
1246     int diff;
1247
1248     xpm_getavail(dirname, ext);
1249
1250     if (appData.debugMode)
1251       xpm_print_avail(stderr, ext);
1252
1253     for (i=1; i<MAXSQSIZE; ++i) {
1254         if (xpm_avail[i]) {
1255             diff = size - i;
1256             diff = (diff<0) ? -diff : diff;
1257             if (diff < sm_diff) {
1258                 sm_diff = diff;
1259                 sm_index = i;
1260             }
1261         }
1262     }
1263
1264     if (!sm_index) {
1265         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1266         exit(1);
1267     }
1268
1269     return sm_index;
1270 }
1271 #else   /* !HAVE_DIR_STRUCT */
1272 /* If we are on a system without a DIR struct, we can't
1273    read the directory, so we can't collect a list of
1274    filenames, etc., so we can't do any size-fitting. */
1275 int
1276 xpm_closest_to(dirname, size, ext)
1277      char *dirname;
1278      int size;
1279      char *ext;
1280 {
1281     fprintf(stderr, _("\
1282 Warning: No DIR structure found on this system --\n\
1283          Unable to autosize for XPM/XIM pieces.\n\
1284    Please report this error to %s.\n\
1285    Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
1286     return size;
1287 }
1288 #endif /* HAVE_DIR_STRUCT */
1289
1290 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1291                              "magenta", "cyan", "white" };
1292 typedef struct {
1293     int attr, bg, fg;
1294 } TextColors;
1295 TextColors textColors[(int)NColorClasses];
1296
1297 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1298 static int
1299 parse_color(str, which)
1300      char *str;
1301      int which;
1302 {
1303     char *p, buf[100], *d;
1304     int i;
1305
1306     if (strlen(str) > 99)       /* watch bounds on buf */
1307       return -1;
1308
1309     p = str;
1310     d = buf;
1311     for (i=0; i<which; ++i) {
1312         p = strchr(p, ',');
1313         if (!p)
1314           return -1;
1315         ++p;
1316     }
1317
1318     /* Could be looking at something like:
1319        black, , 1
1320        .. in which case we want to stop on a comma also */
1321     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1322       ++p;
1323
1324     if (*p == ',') {
1325         return -1;              /* Use default for empty field */
1326     }
1327
1328     if (which == 2 || isdigit(*p))
1329       return atoi(p);
1330
1331     while (*p && isalpha(*p))
1332       *(d++) = *(p++);
1333
1334     *d = 0;
1335
1336     for (i=0; i<8; ++i) {
1337         if (!StrCaseCmp(buf, cnames[i]))
1338           return which? (i+40) : (i+30);
1339     }
1340     if (!StrCaseCmp(buf, "default")) return -1;
1341
1342     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1343     return -2;
1344 }
1345
1346 static int
1347 parse_cpair(cc, str)
1348      ColorClass cc;
1349      char *str;
1350 {
1351     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1352         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1353                 programName, str);
1354         return -1;
1355     }
1356
1357     /* bg and attr are optional */
1358     textColors[(int)cc].bg = parse_color(str, 1);
1359     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1360         textColors[(int)cc].attr = 0;
1361     }
1362     return 0;
1363 }
1364
1365
1366 /* Arrange to catch delete-window events */
1367 Atom wm_delete_window;
1368 void
1369 CatchDeleteWindow(Widget w, String procname)
1370 {
1371   char buf[MSG_SIZ];
1372   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1373   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1374   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1375 }
1376
1377 void
1378 BoardToTop()
1379 {
1380   Arg args[16];
1381   XtSetArg(args[0], XtNiconic, False);
1382   XtSetValues(shellWidget, args, 1);
1383
1384   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1385 }
1386
1387 //---------------------------------------------------------------------------------------------------------
1388 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1389 #define XBOARD True
1390 #define JAWS_ARGS
1391 #define CW_USEDEFAULT (1<<31)
1392 #define ICS_TEXT_MENU_SIZE 90
1393 #define DEBUG_FILE "xboard.debug"
1394 #define SetCurrentDirectory chdir
1395 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1396 #define OPTCHAR "-"
1397 #define SEPCHAR " "
1398
1399 // these two must some day move to frontend.h, when they are implemented
1400 Boolean GameListIsUp();
1401
1402 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1403 #include "args.h"
1404
1405 // front-end part of option handling
1406
1407 // [HGM] This platform-dependent table provides the location for storing the color info
1408 extern char *crWhite, * crBlack;
1409
1410 void *
1411 colorVariable[] = {
1412   &appData.whitePieceColor,
1413   &appData.blackPieceColor,
1414   &appData.lightSquareColor,
1415   &appData.darkSquareColor,
1416   &appData.highlightSquareColor,
1417   &appData.premoveHighlightColor,
1418   &appData.lowTimeWarningColor,
1419   NULL,
1420   NULL,
1421   NULL,
1422   NULL,
1423   NULL,
1424   &crWhite,
1425   &crBlack,
1426   NULL
1427 };
1428
1429 // [HGM] font: keep a font for each square size, even non-stndard ones
1430 #define NUM_SIZES 18
1431 #define MAX_SIZE 130
1432 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1433 char *fontTable[NUM_FONTS][MAX_SIZE];
1434
1435 void
1436 ParseFont(char *name, int number)
1437 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1438   int size;
1439   if(sscanf(name, "size%d:", &size)) {
1440     // [HGM] font: font is meant for specific boardSize (likely from settings file);
1441     //       defer processing it until we know if it matches our board size
1442     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1443         fontTable[number][size] = strdup(strchr(name, ':')+1);
1444         fontValid[number][size] = True;
1445     }
1446     return;
1447   }
1448   switch(number) {
1449     case 0: // CLOCK_FONT
1450         appData.clockFont = strdup(name);
1451       break;
1452     case 1: // MESSAGE_FONT
1453         appData.font = strdup(name);
1454       break;
1455     case 2: // COORD_FONT
1456         appData.coordFont = strdup(name);
1457       break;
1458     default:
1459       return;
1460   }
1461   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1462 }
1463
1464 void
1465 SetFontDefaults()
1466 { // only 2 fonts currently
1467   appData.clockFont = CLOCK_FONT_NAME;
1468   appData.coordFont = COORD_FONT_NAME;
1469   appData.font  =   DEFAULT_FONT_NAME;
1470 }
1471
1472 void
1473 CreateFonts()
1474 { // no-op, until we identify the code for this already in XBoard and move it here
1475 }
1476
1477 void
1478 ParseColor(int n, char *name)
1479 { // in XBoard, just copy the color-name string
1480   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1481 }
1482
1483 void
1484 ParseTextAttribs(ColorClass cc, char *s)
1485 {
1486     (&appData.colorShout)[cc] = strdup(s);
1487 }
1488
1489 void
1490 ParseBoardSize(void *addr, char *name)
1491 {
1492     appData.boardSize = strdup(name);
1493 }
1494
1495 void
1496 LoadAllSounds()
1497 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1498 }
1499
1500 void
1501 SetCommPortDefaults()
1502 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1503 }
1504
1505 // [HGM] args: these three cases taken out to stay in front-end
1506 void
1507 SaveFontArg(FILE *f, ArgDescriptor *ad)
1508 {
1509   char *name;
1510   int i, n = (int)(intptr_t)ad->argLoc;
1511   switch(n) {
1512     case 0: // CLOCK_FONT
1513         name = appData.clockFont;
1514       break;
1515     case 1: // MESSAGE_FONT
1516         name = appData.font;
1517       break;
1518     case 2: // COORD_FONT
1519         name = appData.coordFont;
1520       break;
1521     default:
1522       return;
1523   }
1524   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1525     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1526         fontTable[n][squareSize] = strdup(name);
1527         fontValid[n][squareSize] = True;
1528         break;
1529   }
1530   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1531     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1532 }
1533
1534 void
1535 ExportSounds()
1536 { // nothing to do, as the sounds are at all times represented by their text-string names already
1537 }
1538
1539 void
1540 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1541 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1542         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1543 }
1544
1545 void
1546 SaveColor(FILE *f, ArgDescriptor *ad)
1547 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1548         if(colorVariable[(int)(intptr_t)ad->argLoc])
1549         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1550 }
1551
1552 void
1553 SaveBoardSize(FILE *f, char *name, void *addr)
1554 { // wrapper to shield back-end from BoardSize & sizeInfo
1555   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1556 }
1557
1558 void
1559 ParseCommPortSettings(char *s)
1560 { // no such option in XBoard (yet)
1561 }
1562
1563 extern Widget engineOutputShell;
1564
1565 void
1566 GetActualPlacement(Widget wg, WindowPlacement *wp)
1567 {
1568   Arg args[16];
1569   Dimension w, h;
1570   Position x, y;
1571   int i;
1572
1573   if(!wg) return;
1574
1575     i = 0;
1576     XtSetArg(args[i], XtNx, &x); i++;
1577     XtSetArg(args[i], XtNy, &y); i++;
1578     XtSetArg(args[i], XtNwidth, &w); i++;
1579     XtSetArg(args[i], XtNheight, &h); i++;
1580     XtGetValues(wg, args, i);
1581     wp->x = x - 4;
1582     wp->y = y - 23;
1583     wp->height = h;
1584     wp->width = w;
1585 }
1586
1587 void
1588 GetWindowCoords()
1589 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1590   // In XBoard this will have to wait until awareness of window parameters is implemented
1591   GetActualPlacement(shellWidget, &wpMain);
1592   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1593   if(MoveHistoryIsUp()) GetActualPlacement(shells[7], &wpMoveHistory);
1594   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1595   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1596   if(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
1597   if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
1598 }
1599
1600 void
1601 PrintCommPortSettings(FILE *f, char *name)
1602 { // This option does not exist in XBoard
1603 }
1604
1605 int
1606 MySearchPath(char *installDir, char *name, char *fullname)
1607 { // just append installDir and name. Perhaps ExpandPath should be used here?
1608   name = ExpandPathName(name);
1609   if(name && name[0] == '/')
1610     safeStrCpy(fullname, name, MSG_SIZ );
1611   else {
1612     sprintf(fullname, "%s%c%s", installDir, '/', name);
1613   }
1614   return 1;
1615 }
1616
1617 int
1618 MyGetFullPathName(char *name, char *fullname)
1619 { // should use ExpandPath?
1620   name = ExpandPathName(name);
1621   safeStrCpy(fullname, name, MSG_SIZ );
1622   return 1;
1623 }
1624
1625 void
1626 EnsureOnScreen(int *x, int *y, int minX, int minY)
1627 {
1628   return;
1629 }
1630
1631 int
1632 MainWindowUp()
1633 { // [HGM] args: allows testing if main window is realized from back-end
1634   return xBoardWindow != 0;
1635 }
1636
1637 void
1638 PopUpStartupDialog()
1639 {  // start menu not implemented in XBoard
1640 }
1641
1642 char *
1643 ConvertToLine(int argc, char **argv)
1644 {
1645   static char line[128*1024], buf[1024];
1646   int i;
1647
1648   line[0] = NULLCHAR;
1649   for(i=1; i<argc; i++)
1650     {
1651       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1652           && argv[i][0] != '{' )
1653         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1654       else
1655         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1656       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1657     }
1658
1659   line[strlen(line)-1] = NULLCHAR;
1660   return line;
1661 }
1662
1663 //--------------------------------------------------------------------------------------------
1664
1665 extern Boolean twoBoards, partnerUp;
1666
1667 #ifdef IDSIZES
1668   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1669 #else
1670 #define BoardSize int
1671 void InitDrawingSizes(BoardSize boardSize, int flags)
1672 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1673     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1674     Arg args[16];
1675     XtGeometryResult gres;
1676     int i;
1677
1678     if(!formWidget) return;
1679
1680     /*
1681      * Enable shell resizing.
1682      */
1683     shellArgs[0].value = (XtArgVal) &w;
1684     shellArgs[1].value = (XtArgVal) &h;
1685     XtGetValues(shellWidget, shellArgs, 2);
1686
1687     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1688     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1689     XtSetValues(shellWidget, &shellArgs[2], 4);
1690
1691     XtSetArg(args[0], XtNdefaultDistance, &sep);
1692     XtGetValues(formWidget, args, 1);
1693
1694     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1695     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1696     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1697     CreateGrid();
1698     hOffset = boardWidth + 10;
1699     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1700         secondSegments[i] = gridSegments[i];
1701         secondSegments[i].x1 += hOffset;
1702         secondSegments[i].x2 += hOffset;
1703     }
1704
1705     XtSetArg(args[0], XtNwidth, boardWidth);
1706     XtSetArg(args[1], XtNheight, boardHeight);
1707     XtSetValues(boardWidget, args, 2);
1708
1709     timerWidth = (boardWidth - sep) / 2;
1710     XtSetArg(args[0], XtNwidth, timerWidth);
1711     XtSetValues(whiteTimerWidget, args, 1);
1712     XtSetValues(blackTimerWidget, args, 1);
1713
1714     XawFormDoLayout(formWidget, False);
1715
1716     if (appData.titleInWindow) {
1717         i = 0;
1718         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1719         XtSetArg(args[i], XtNheight, &h);  i++;
1720         XtGetValues(titleWidget, args, i);
1721         if (smallLayout) {
1722             w = boardWidth - 2*bor;
1723         } else {
1724             XtSetArg(args[0], XtNwidth, &w);
1725             XtGetValues(menuBarWidget, args, 1);
1726             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1727         }
1728
1729         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1730         if (gres != XtGeometryYes && appData.debugMode) {
1731             fprintf(stderr,
1732                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1733                     programName, gres, w, h, wr, hr);
1734         }
1735     }
1736
1737     XawFormDoLayout(formWidget, True);
1738
1739     /*
1740      * Inhibit shell resizing.
1741      */
1742     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1743     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1744     shellArgs[4].value = shellArgs[2].value = w;
1745     shellArgs[5].value = shellArgs[3].value = h;
1746     XtSetValues(shellWidget, &shellArgs[0], 6);
1747
1748     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1749     // (only for xpm)
1750     if(useImages) {
1751       for(i=0; i<4; i++) {
1752         int p;
1753         for(p=0; p<=(int)WhiteKing; p++)
1754            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1755         if(gameInfo.variant == VariantShogi) {
1756            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1757            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1758            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1759            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1760            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1761         }
1762 #ifdef GOTHIC
1763         if(gameInfo.variant == VariantGothic) {
1764            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1765         }
1766 #endif
1767         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1768            xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
1769            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1770         }
1771 #if !HAVE_LIBXPM
1772         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1773         for(p=0; p<=(int)WhiteKing; p++)
1774            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1775         if(gameInfo.variant == VariantShogi) {
1776            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1777            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1778            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1779            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1780            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1781         }
1782 #ifdef GOTHIC
1783         if(gameInfo.variant == VariantGothic) {
1784            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1785         }
1786 #endif
1787         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1788            ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
1789            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1790         }
1791 #endif
1792       }
1793     } else {
1794       for(i=0; i<2; i++) {
1795         int p;
1796         for(p=0; p<=(int)WhiteKing; p++)
1797            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1798         if(gameInfo.variant == VariantShogi) {
1799            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1800            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1801            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1802            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1803            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1804         }
1805 #ifdef GOTHIC
1806         if(gameInfo.variant == VariantGothic) {
1807            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1808         }
1809 #endif
1810         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1811            pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
1812            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1813         }
1814       }
1815     }
1816 #if HAVE_LIBXPM
1817     CreateAnimVars();
1818 #endif
1819 }
1820 #endif
1821
1822 void ParseIcsTextColors()
1823 {   // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1824     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1825         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1826         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
1827         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
1828         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1829         parse_cpair(ColorTell, appData.colorTell) < 0 ||
1830         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
1831         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
1832         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
1833         parse_cpair(ColorNormal, appData.colorNormal) < 0)
1834       {
1835           if (appData.colorize) {
1836               fprintf(stderr,
1837                       _("%s: can't parse color names; disabling colorization\n"),
1838                       programName);
1839           }
1840           appData.colorize = FALSE;
1841       }
1842 }
1843
1844 int MakeColors()
1845 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1846     XrmValue vFrom, vTo;
1847     int forceMono = False;
1848
1849     if (!appData.monoMode) {
1850         vFrom.addr = (caddr_t) appData.lightSquareColor;
1851         vFrom.size = strlen(appData.lightSquareColor);
1852         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1853         if (vTo.addr == NULL) {
1854           appData.monoMode = True;
1855           forceMono = True;
1856         } else {
1857           lightSquareColor = *(Pixel *) vTo.addr;
1858         }
1859     }
1860     if (!appData.monoMode) {
1861         vFrom.addr = (caddr_t) appData.darkSquareColor;
1862         vFrom.size = strlen(appData.darkSquareColor);
1863         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1864         if (vTo.addr == NULL) {
1865           appData.monoMode = True;
1866           forceMono = True;
1867         } else {
1868           darkSquareColor = *(Pixel *) vTo.addr;
1869         }
1870     }
1871     if (!appData.monoMode) {
1872         vFrom.addr = (caddr_t) appData.whitePieceColor;
1873         vFrom.size = strlen(appData.whitePieceColor);
1874         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1875         if (vTo.addr == NULL) {
1876           appData.monoMode = True;
1877           forceMono = True;
1878         } else {
1879           whitePieceColor = *(Pixel *) vTo.addr;
1880         }
1881     }
1882     if (!appData.monoMode) {
1883         vFrom.addr = (caddr_t) appData.blackPieceColor;
1884         vFrom.size = strlen(appData.blackPieceColor);
1885         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1886         if (vTo.addr == NULL) {
1887           appData.monoMode = True;
1888           forceMono = True;
1889         } else {
1890           blackPieceColor = *(Pixel *) vTo.addr;
1891         }
1892     }
1893
1894     if (!appData.monoMode) {
1895         vFrom.addr = (caddr_t) appData.highlightSquareColor;
1896         vFrom.size = strlen(appData.highlightSquareColor);
1897         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1898         if (vTo.addr == NULL) {
1899           appData.monoMode = True;
1900           forceMono = True;
1901         } else {
1902           highlightSquareColor = *(Pixel *) vTo.addr;
1903         }
1904     }
1905
1906     if (!appData.monoMode) {
1907         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1908         vFrom.size = strlen(appData.premoveHighlightColor);
1909         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1910         if (vTo.addr == NULL) {
1911           appData.monoMode = True;
1912           forceMono = True;
1913         } else {
1914           premoveHighlightColor = *(Pixel *) vTo.addr;
1915         }
1916     }
1917     return forceMono;
1918 }
1919
1920 void
1921 CreateAnyPieces()
1922 {   // [HGM] taken out of main
1923 #if HAVE_LIBXPM
1924     if (appData.monoMode && // [HGM] no sense to go on to certain doom
1925        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1926             appData.bitmapDirectory = DEF_BITMAP_DIR;
1927
1928     if (appData.bitmapDirectory[0] != NULLCHAR) {
1929       CreatePieces();
1930     } else {
1931       CreateXPMPieces();
1932       CreateXPMBoard(appData.liteBackTextureFile, 1);
1933       CreateXPMBoard(appData.darkBackTextureFile, 0);
1934     }
1935 #else
1936     CreateXIMPieces();
1937     /* Create regular pieces */
1938     if (!useImages) CreatePieces();
1939 #endif
1940 }
1941
1942 int
1943 main(argc, argv)
1944      int argc;
1945      char **argv;
1946 {
1947     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1948     XSetWindowAttributes window_attributes;
1949     Arg args[16];
1950     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1951     XrmValue vFrom, vTo;
1952     XtGeometryResult gres;
1953     char *p;
1954     XrmDatabase xdb;
1955     int forceMono = False;
1956
1957     srandom(time(0)); // [HGM] book: make random truly random
1958
1959     setbuf(stdout, NULL);
1960     setbuf(stderr, NULL);
1961     debugFP = stderr;
1962
1963     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1964         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1965         exit(0);
1966     }
1967
1968     programName = strrchr(argv[0], '/');
1969     if (programName == NULL)
1970       programName = argv[0];
1971     else
1972       programName++;
1973
1974 #ifdef ENABLE_NLS
1975     XtSetLanguageProc(NULL, NULL, NULL);
1976     bindtextdomain(PACKAGE, LOCALEDIR);
1977     textdomain(PACKAGE);
1978 #endif
1979
1980     shellWidget =
1981       XtAppInitialize(&appContext, "XBoard", shellOptions,
1982                       XtNumber(shellOptions),
1983                       &argc, argv, xboardResources, NULL, 0);
1984     appData.boardSize = "";
1985     InitAppData(ConvertToLine(argc, argv));
1986     p = getenv("HOME");
1987     if (p == NULL) p = "/tmp";
1988     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1989     gameCopyFilename = (char*) malloc(i);
1990     gamePasteFilename = (char*) malloc(i);
1991     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1992     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1993
1994     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1995                               clientResources, XtNumber(clientResources),
1996                               NULL, 0);
1997
1998     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1999         static char buf[MSG_SIZ];
2000         EscapeExpand(buf, appData.firstInitString);
2001         appData.firstInitString = strdup(buf);
2002         EscapeExpand(buf, appData.secondInitString);
2003         appData.secondInitString = strdup(buf);
2004         EscapeExpand(buf, appData.firstComputerString);
2005         appData.firstComputerString = strdup(buf);
2006         EscapeExpand(buf, appData.secondComputerString);
2007         appData.secondComputerString = strdup(buf);
2008     }
2009
2010     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
2011         chessDir = ".";
2012     } else {
2013         if (chdir(chessDir) != 0) {
2014             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
2015             perror(chessDir);
2016             exit(1);
2017         }
2018     }
2019
2020     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
2021         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
2022         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
2023            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
2024            exit(errno);
2025         }
2026         setbuf(debugFP, NULL);
2027     }
2028
2029 #if ENABLE_NLS
2030     if (appData.debugMode) {
2031       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
2032     }
2033 #endif
2034
2035     /* [HGM,HR] make sure board size is acceptable */
2036     if(appData.NrFiles > BOARD_FILES ||
2037        appData.NrRanks > BOARD_RANKS   )
2038          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
2039
2040 #if !HIGHDRAG
2041     /* This feature does not work; animation needs a rewrite */
2042     appData.highlightDragging = FALSE;
2043 #endif
2044     InitBackEnd1();
2045
2046     xDisplay = XtDisplay(shellWidget);
2047     xScreen = DefaultScreen(xDisplay);
2048     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
2049
2050         gameInfo.variant = StringToVariant(appData.variant);
2051         InitPosition(FALSE);
2052
2053 #ifdef IDSIZE
2054     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
2055 #else
2056     if (isdigit(appData.boardSize[0])) {
2057         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
2058                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
2059                    &fontPxlSize, &smallLayout, &tinyLayout);
2060         if (i == 0) {
2061             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
2062                     programName, appData.boardSize);
2063             exit(2);
2064         }
2065         if (i < 7) {
2066             /* Find some defaults; use the nearest known size */
2067             SizeDefaults *szd, *nearest;
2068             int distance = 99999;
2069             nearest = szd = sizeDefaults;
2070             while (szd->name != NULL) {
2071                 if (abs(szd->squareSize - squareSize) < distance) {
2072                     nearest = szd;
2073                     distance = abs(szd->squareSize - squareSize);
2074                     if (distance == 0) break;
2075                 }
2076                 szd++;
2077             }
2078             if (i < 2) lineGap = nearest->lineGap;
2079             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
2080             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
2081             if (i < 5) fontPxlSize = nearest->fontPxlSize;
2082             if (i < 6) smallLayout = nearest->smallLayout;
2083             if (i < 7) tinyLayout = nearest->tinyLayout;
2084         }
2085     } else {
2086         SizeDefaults *szd = sizeDefaults;
2087         if (*appData.boardSize == NULLCHAR) {
2088             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
2089                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
2090               szd++;
2091             }
2092             if (szd->name == NULL) szd--;
2093             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
2094         } else {
2095             while (szd->name != NULL &&
2096                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
2097             if (szd->name == NULL) {
2098                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
2099                         programName, appData.boardSize);
2100                 exit(2);
2101             }
2102         }
2103         squareSize = szd->squareSize;
2104         lineGap = szd->lineGap;
2105         clockFontPxlSize = szd->clockFontPxlSize;
2106         coordFontPxlSize = szd->coordFontPxlSize;
2107         fontPxlSize = szd->fontPxlSize;
2108         smallLayout = szd->smallLayout;
2109         tinyLayout = szd->tinyLayout;
2110         // [HGM] font: use defaults from settings file if available and not overruled
2111     }
2112     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
2113         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
2114     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
2115         appData.font = fontTable[MESSAGE_FONT][squareSize];
2116     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
2117         appData.coordFont = fontTable[COORD_FONT][squareSize];
2118
2119     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
2120     if (strlen(appData.pixmapDirectory) > 0) {
2121         p = ExpandPathName(appData.pixmapDirectory);
2122         if (!p) {
2123             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
2124                    appData.pixmapDirectory);
2125             exit(1);
2126         }
2127         if (appData.debugMode) {
2128           fprintf(stderr, _("\
2129 XBoard square size (hint): %d\n\
2130 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
2131         }
2132         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
2133         if (appData.debugMode) {
2134             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
2135         }
2136     }
2137     defaultLineGap = lineGap;
2138     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
2139
2140     /* [HR] height treated separately (hacked) */
2141     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
2142     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2143     if (appData.showJail == 1) {
2144         /* Jail on top and bottom */
2145         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2146         XtSetArg(boardArgs[2], XtNheight,
2147                  boardHeight + 2*(lineGap + squareSize));
2148     } else if (appData.showJail == 2) {
2149         /* Jail on sides */
2150         XtSetArg(boardArgs[1], XtNwidth,
2151                  boardWidth + 2*(lineGap + squareSize));
2152         XtSetArg(boardArgs[2], XtNheight, boardHeight);
2153     } else {
2154         /* No jail */
2155         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
2156         XtSetArg(boardArgs[2], XtNheight, boardHeight);
2157     }
2158
2159     /*
2160      * Determine what fonts to use.
2161      */
2162 #if ENABLE_NLS
2163     appData.font = InsertPxlSize(appData.font, fontPxlSize);
2164     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
2165     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
2166     fontSet = CreateFontSet(appData.font);
2167     clockFontSet = CreateFontSet(appData.clockFont);
2168     {
2169       /* For the coordFont, use the 0th font of the fontset. */
2170       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
2171       XFontStruct **font_struct_list;
2172       char **font_name_list;
2173       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
2174       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
2175       coordFontStruct = XQueryFont(xDisplay, coordFontID);
2176     }
2177 #else
2178     appData.font = FindFont(appData.font, fontPxlSize);
2179     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
2180     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
2181     clockFontID = XLoadFont(xDisplay, appData.clockFont);
2182     clockFontStruct = XQueryFont(xDisplay, clockFontID);
2183     coordFontID = XLoadFont(xDisplay, appData.coordFont);
2184     coordFontStruct = XQueryFont(xDisplay, coordFontID);
2185 #endif
2186     countFontID = coordFontID;  // [HGM] holdings
2187     countFontStruct = coordFontStruct;
2188
2189     xdb = XtDatabase(xDisplay);
2190 #if ENABLE_NLS
2191     XrmPutLineResource(&xdb, "*international: True");
2192     vTo.size = sizeof(XFontSet);
2193     vTo.addr = (XtPointer) &fontSet;
2194     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
2195 #else
2196     XrmPutStringResource(&xdb, "*font", appData.font);
2197 #endif
2198
2199     /*
2200      * Detect if there are not enough colors available and adapt.
2201      */
2202     if (DefaultDepth(xDisplay, xScreen) <= 2) {
2203       appData.monoMode = True;
2204     }
2205
2206     forceMono = MakeColors();
2207
2208     if (forceMono) {
2209       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2210               programName);
2211         appData.monoMode = True;
2212     }
2213
2214     if (appData.lowTimeWarning && !appData.monoMode) {
2215       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2216       vFrom.size = strlen(appData.lowTimeWarningColor);
2217       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2218       if (vTo.addr == NULL)
2219                 appData.monoMode = True;
2220       else
2221                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2222     }
2223
2224     if (appData.monoMode && appData.debugMode) {
2225         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2226                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2227                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2228     }
2229
2230     ParseIcsTextColors();
2231     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2232     textColors[ColorNone].attr = 0;
2233
2234     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2235
2236     /*
2237      * widget hierarchy
2238      */
2239     if (tinyLayout) {
2240         layoutName = "tinyLayout";
2241     } else if (smallLayout) {
2242         layoutName = "smallLayout";
2243     } else {
2244         layoutName = "normalLayout";
2245     }
2246     /* Outer layoutWidget is there only to provide a name for use in
2247        resources that depend on the layout style */
2248     layoutWidget =
2249       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2250                             layoutArgs, XtNumber(layoutArgs));
2251     formWidget =
2252       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2253                             formArgs, XtNumber(formArgs));
2254     XtSetArg(args[0], XtNdefaultDistance, &sep);
2255     XtGetValues(formWidget, args, 1);
2256
2257     j = 0;
2258     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2259     XtSetArg(args[0], XtNtop,    XtChainTop);
2260     XtSetArg(args[1], XtNbottom, XtChainTop);
2261     XtSetArg(args[2], XtNright,  XtChainLeft);
2262     XtSetValues(menuBarWidget, args, 3);
2263
2264     widgetList[j++] = whiteTimerWidget =
2265       XtCreateWidget("whiteTime", labelWidgetClass,
2266                      formWidget, timerArgs, XtNumber(timerArgs));
2267 #if ENABLE_NLS
2268     XtSetArg(args[0], XtNfontSet, clockFontSet);
2269 #else
2270     XtSetArg(args[0], XtNfont, clockFontStruct);
2271 #endif
2272     XtSetArg(args[1], XtNtop,    XtChainTop);
2273     XtSetArg(args[2], XtNbottom, XtChainTop);
2274     XtSetValues(whiteTimerWidget, args, 3);
2275
2276     widgetList[j++] = blackTimerWidget =
2277       XtCreateWidget("blackTime", labelWidgetClass,
2278                      formWidget, timerArgs, XtNumber(timerArgs));
2279 #if ENABLE_NLS
2280     XtSetArg(args[0], XtNfontSet, clockFontSet);
2281 #else
2282     XtSetArg(args[0], XtNfont, clockFontStruct);
2283 #endif
2284     XtSetArg(args[1], XtNtop,    XtChainTop);
2285     XtSetArg(args[2], XtNbottom, XtChainTop);
2286     XtSetValues(blackTimerWidget, args, 3);
2287
2288     if (appData.titleInWindow) {
2289         widgetList[j++] = titleWidget =
2290           XtCreateWidget("title", labelWidgetClass, formWidget,
2291                          titleArgs, XtNumber(titleArgs));
2292         XtSetArg(args[0], XtNtop,    XtChainTop);
2293         XtSetArg(args[1], XtNbottom, XtChainTop);
2294         XtSetValues(titleWidget, args, 2);
2295     }
2296
2297     if (appData.showButtonBar) {
2298       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2299       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2300       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2301       XtSetArg(args[2], XtNtop,    XtChainTop);
2302       XtSetArg(args[3], XtNbottom, XtChainTop);
2303       XtSetValues(buttonBarWidget, args, 4);
2304     }
2305
2306     widgetList[j++] = messageWidget =
2307       XtCreateWidget("message", labelWidgetClass, formWidget,
2308                      messageArgs, XtNumber(messageArgs));
2309     XtSetArg(args[0], XtNtop,    XtChainTop);
2310     XtSetArg(args[1], XtNbottom, XtChainTop);
2311     XtSetValues(messageWidget, args, 2);
2312
2313     widgetList[j++] = boardWidget =
2314       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2315                      XtNumber(boardArgs));
2316
2317     XtManageChildren(widgetList, j);
2318
2319     timerWidth = (boardWidth - sep) / 2;
2320     XtSetArg(args[0], XtNwidth, timerWidth);
2321     XtSetValues(whiteTimerWidget, args, 1);
2322     XtSetValues(blackTimerWidget, args, 1);
2323
2324     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2325     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2326     XtGetValues(whiteTimerWidget, args, 2);
2327
2328     if (appData.showButtonBar) {
2329       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2330       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2331       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2332     }
2333
2334     /*
2335      * formWidget uses these constraints but they are stored
2336      * in the children.
2337      */
2338     i = 0;
2339     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2340     XtSetValues(menuBarWidget, args, i);
2341     if (appData.titleInWindow) {
2342         if (smallLayout) {
2343             i = 0;
2344             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2345             XtSetValues(whiteTimerWidget, args, i);
2346             i = 0;
2347             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2348             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2349             XtSetValues(blackTimerWidget, args, i);
2350             i = 0;
2351             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2352             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2353             XtSetValues(titleWidget, args, i);
2354             i = 0;
2355             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2356             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2357             XtSetValues(messageWidget, args, i);
2358             if (appData.showButtonBar) {
2359               i = 0;
2360               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2361               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2362               XtSetValues(buttonBarWidget, args, i);
2363             }
2364         } else {
2365             i = 0;
2366             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2367             XtSetValues(whiteTimerWidget, args, i);
2368             i = 0;
2369             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2370             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2371             XtSetValues(blackTimerWidget, args, i);
2372             i = 0;
2373             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2374             XtSetValues(titleWidget, args, i);
2375             i = 0;
2376             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2377             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2378             XtSetValues(messageWidget, args, i);
2379             if (appData.showButtonBar) {
2380               i = 0;
2381               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2382               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2383               XtSetValues(buttonBarWidget, args, i);
2384             }
2385         }
2386     } else {
2387         i = 0;
2388         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2389         XtSetValues(whiteTimerWidget, args, i);
2390         i = 0;
2391         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2392         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2393         XtSetValues(blackTimerWidget, args, i);
2394         i = 0;
2395         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2396         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2397         XtSetValues(messageWidget, args, i);
2398         if (appData.showButtonBar) {
2399           i = 0;
2400           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2401           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2402           XtSetValues(buttonBarWidget, args, i);
2403         }
2404     }
2405     i = 0;
2406     XtSetArg(args[0], XtNfromVert, messageWidget);
2407     XtSetArg(args[1], XtNtop,    XtChainTop);
2408     XtSetArg(args[2], XtNbottom, XtChainBottom);
2409     XtSetArg(args[3], XtNleft,   XtChainLeft);
2410     XtSetArg(args[4], XtNright,  XtChainRight);
2411     XtSetValues(boardWidget, args, 5);
2412
2413     XtRealizeWidget(shellWidget);
2414
2415     if(wpMain.x > 0) {
2416       XtSetArg(args[0], XtNx, wpMain.x);
2417       XtSetArg(args[1], XtNy, wpMain.y);
2418       XtSetValues(shellWidget, args, 2);
2419     }
2420
2421     /*
2422      * Correct the width of the message and title widgets.
2423      * It is not known why some systems need the extra fudge term.
2424      * The value "2" is probably larger than needed.
2425      */
2426     XawFormDoLayout(formWidget, False);
2427
2428 #define WIDTH_FUDGE 2
2429     i = 0;
2430     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2431     XtSetArg(args[i], XtNheight, &h);  i++;
2432     XtGetValues(messageWidget, args, i);
2433     if (appData.showButtonBar) {
2434       i = 0;
2435       XtSetArg(args[i], XtNwidth, &w);  i++;
2436       XtGetValues(buttonBarWidget, args, i);
2437       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2438     } else {
2439       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2440     }
2441
2442     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2443     if (gres != XtGeometryYes && appData.debugMode) {
2444       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2445               programName, gres, w, h, wr, hr);
2446     }
2447
2448     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2449     /* The size used for the child widget in layout lags one resize behind
2450        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2451     w--;
2452     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2453     if (gres != XtGeometryYes && appData.debugMode) {
2454       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2455               programName, gres, w, h, wr, hr);
2456     }
2457     /* !! end hack */
2458     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2459     XtSetArg(args[1], XtNright, XtChainRight);
2460     XtSetValues(messageWidget, args, 2);
2461
2462     if (appData.titleInWindow) {
2463         i = 0;
2464         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2465         XtSetArg(args[i], XtNheight, &h);  i++;
2466         XtGetValues(titleWidget, args, i);
2467         if (smallLayout) {
2468             w = boardWidth - 2*bor;
2469         } else {
2470             XtSetArg(args[0], XtNwidth, &w);
2471             XtGetValues(menuBarWidget, args, 1);
2472             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2473         }
2474
2475         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2476         if (gres != XtGeometryYes && appData.debugMode) {
2477             fprintf(stderr,
2478                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2479                     programName, gres, w, h, wr, hr);
2480         }
2481     }
2482     XawFormDoLayout(formWidget, True);
2483
2484     xBoardWindow = XtWindow(boardWidget);
2485
2486     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2487     //       not need to go into InitDrawingSizes().
2488 #endif
2489
2490     /*
2491      * Create X checkmark bitmap and initialize option menu checks.
2492      */
2493     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2494                checkmark_bits, checkmark_width, checkmark_height);
2495     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2496 #ifndef OPTIONSDIALOG
2497     if (appData.alwaysPromoteToQueen) {
2498         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2499                     args, 1);
2500     }
2501     if (appData.animateDragging) {
2502         XtSetValues(XtNameToWidget(menuBarWidget,
2503                                    "menuOptions.Animate Dragging"),
2504                     args, 1);
2505     }
2506     if (appData.animate) {
2507         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2508                     args, 1);
2509     }
2510     if (appData.autoCallFlag) {
2511         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2512                     args, 1);
2513     }
2514     if (appData.autoFlipView) {
2515         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2516                     args, 1);
2517     }
2518     if (appData.blindfold) {
2519         XtSetValues(XtNameToWidget(menuBarWidget,
2520                                    "menuOptions.Blindfold"), args, 1);
2521     }
2522     if (appData.flashCount > 0) {
2523         XtSetValues(XtNameToWidget(menuBarWidget,
2524                                    "menuOptions.Flash Moves"),
2525                     args, 1);
2526     }
2527 #if HIGHDRAG
2528     if (appData.highlightDragging) {
2529         XtSetValues(XtNameToWidget(menuBarWidget,
2530                                    "menuOptions.Highlight Dragging"),
2531                     args, 1);
2532     }
2533 #endif
2534     if (appData.highlightLastMove) {
2535         XtSetValues(XtNameToWidget(menuBarWidget,
2536                                    "menuOptions.Highlight Last Move"),
2537                     args, 1);
2538     }
2539     if (appData.highlightMoveWithArrow) {
2540         XtSetValues(XtNameToWidget(menuBarWidget,
2541                                    "menuOptions.Arrow"),
2542                     args, 1);
2543     }
2544 //    if (appData.icsAlarm) {
2545 //      XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2546 //                  args, 1);
2547 //    }
2548     if (appData.ringBellAfterMoves) {
2549         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2550                     args, 1);
2551     }
2552     if (appData.oneClick) {
2553         XtSetValues(XtNameToWidget(menuBarWidget,
2554                                    "menuOptions.OneClick"), args, 1);
2555     }
2556     if (appData.periodicUpdates) {
2557         XtSetValues(XtNameToWidget(menuBarWidget,
2558                                    "menuOptions.Periodic Updates"), args, 1);
2559     }
2560     if (appData.ponderNextMove) {
2561         XtSetValues(XtNameToWidget(menuBarWidget,
2562                                    "menuOptions.Ponder Next Move"), args, 1);
2563     }
2564     if (appData.popupExitMessage) {
2565         XtSetValues(XtNameToWidget(menuBarWidget,
2566                                    "menuOptions.Popup Exit Message"), args, 1);
2567     }
2568     if (appData.popupMoveErrors) {
2569         XtSetValues(XtNameToWidget(menuBarWidget,
2570                                    "menuOptions.Popup Move Errors"), args, 1);
2571     }
2572 //    if (appData.premove) {
2573 //      XtSetValues(XtNameToWidget(menuBarWidget,
2574 //                                 "menuOptions.Premove"), args, 1);
2575 //    }
2576     if (appData.showCoords) {
2577         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2578                     args, 1);
2579     }
2580     if (appData.hideThinkingFromHuman) {
2581         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2582                     args, 1);
2583     }
2584     if (appData.testLegality) {
2585         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2586                     args, 1);
2587     }
2588 #endif
2589     if (saveSettingsOnExit) {
2590         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2591                     args, 1);
2592     }
2593
2594     /*
2595      * Create an icon.
2596      */
2597     ReadBitmap(&wIconPixmap, "icon_white.bm",
2598                icon_white_bits, icon_white_width, icon_white_height);
2599     ReadBitmap(&bIconPixmap, "icon_black.bm",
2600                icon_black_bits, icon_black_width, icon_black_height);
2601     iconPixmap = wIconPixmap;
2602     i = 0;
2603     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2604     XtSetValues(shellWidget, args, i);
2605
2606     /*
2607      * Create a cursor for the board widget.
2608      */
2609     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2610     XChangeWindowAttributes(xDisplay, xBoardWindow,
2611                             CWCursor, &window_attributes);
2612
2613     /*
2614      * Inhibit shell resizing.
2615      */
2616     shellArgs[0].value = (XtArgVal) &w;
2617     shellArgs[1].value = (XtArgVal) &h;
2618     XtGetValues(shellWidget, shellArgs, 2);
2619     shellArgs[4].value = shellArgs[2].value = w;
2620     shellArgs[5].value = shellArgs[3].value = h;
2621     XtSetValues(shellWidget, &shellArgs[2], 4);
2622     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2623     marginH =  h - boardHeight;
2624
2625     CatchDeleteWindow(shellWidget, "QuitProc");
2626
2627     CreateGCs(False);
2628     CreateGrid();
2629     CreateAnyPieces();
2630
2631     CreatePieceMenus();
2632
2633     if (appData.animate || appData.animateDragging)
2634       CreateAnimVars();
2635
2636     XtAugmentTranslations(formWidget,
2637                           XtParseTranslationTable(globalTranslations));
2638     XtAugmentTranslations(boardWidget,
2639                           XtParseTranslationTable(boardTranslations));
2640     XtAugmentTranslations(whiteTimerWidget,
2641                           XtParseTranslationTable(whiteTranslations));
2642     XtAugmentTranslations(blackTimerWidget,
2643                           XtParseTranslationTable(blackTranslations));
2644
2645     /* Why is the following needed on some versions of X instead
2646      * of a translation? */
2647     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2648                       (XtEventHandler) EventProc, NULL);
2649     /* end why */
2650     XtAddEventHandler(formWidget, KeyPressMask, False,
2651                       (XtEventHandler) MoveTypeInProc, NULL);
2652
2653     /* [AS] Restore layout */
2654     if( wpMoveHistory.visible ) {
2655       HistoryPopUp();
2656     }
2657
2658     if( wpEvalGraph.visible )
2659       {
2660         EvalGraphPopUp();
2661       };
2662
2663     if( wpEngineOutput.visible ) {
2664       EngineOutputPopUp();
2665     }
2666
2667     InitBackEnd2();
2668
2669     if (errorExitStatus == -1) {
2670         if (appData.icsActive) {
2671             /* We now wait until we see "login:" from the ICS before
2672                sending the logon script (problems with timestamp otherwise) */
2673             /*ICSInitScript();*/
2674             if (appData.icsInputBox) ICSInputBoxPopUp();
2675         }
2676
2677     #ifdef SIGWINCH
2678     signal(SIGWINCH, TermSizeSigHandler);
2679     #endif
2680         signal(SIGINT, IntSigHandler);
2681         signal(SIGTERM, IntSigHandler);
2682         if (*appData.cmailGameName != NULLCHAR) {
2683             signal(SIGUSR1, CmailSigHandler);
2684         }
2685     }
2686     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2687     InitPosition(TRUE);
2688 //    XtSetKeyboardFocus(shellWidget, formWidget);
2689     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2690
2691     XtAppMainLoop(appContext);
2692     if (appData.debugMode) fclose(debugFP); // [DM] debug
2693     return 0;
2694 }
2695
2696 static Boolean noEcho;
2697
2698 void
2699 ShutDownFrontEnd()
2700 {
2701     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2702         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2703     }
2704     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2705     unlink(gameCopyFilename);
2706     unlink(gamePasteFilename);
2707     if(noEcho) EchoOn();
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 {
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) ||
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   /*
5740    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5741    * have a notion of a game that is selected but not copied.
5742    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5743    */
5744   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5745                  CurrentTime,
5746                  SendGameSelection,
5747                  NULL/* lose_ownership_proc */ ,
5748                  NULL/* transfer_done_proc */);
5749   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5750                  CurrentTime,
5751                  SendGameSelection,
5752                  NULL/* lose_ownership_proc */ ,
5753                  NULL/* transfer_done_proc */);
5754 }
5755
5756 /* note: when called from menu all parameters are NULL, so no clue what the
5757  * Widget which was clicked on was, or what the click event was
5758  */
5759 void CopyGameProc(w, event, prms, nprms)
5760   Widget w;
5761   XEvent *event;
5762   String *prms;
5763   Cardinal *nprms;
5764 {
5765   int ret;
5766
5767   ret = SaveGameToFile(gameCopyFilename, FALSE);
5768   if (!ret) return;
5769
5770   CopySomething();
5771 }
5772
5773 void CopyGameListProc(w, event, prms, nprms)
5774   Widget w;
5775   XEvent *event;
5776   String *prms;
5777   Cardinal *nprms;
5778 {
5779   if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5780   CopySomething();
5781 }
5782
5783 /* function called when the data to Paste is ready */
5784 static void
5785 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5786             Atom *type, XtPointer value, unsigned long *len, int *format)
5787 {
5788   FILE* f;
5789   if (value == NULL || *len == 0) {
5790     return; /* nothing had been selected to copy */
5791   }
5792   f = fopen(gamePasteFilename, "w");
5793   if (f == NULL) {
5794     DisplayError(_("Can't open temp file"), errno);
5795     return;
5796   }
5797   fwrite(value, 1, *len, f);
5798   fclose(f);
5799   XtFree(value);
5800   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5801 }
5802
5803 /* called when Paste Game button is pressed,
5804  * all parameters will be NULL */
5805 void PasteGameProc(w, event, prms, nprms)
5806   Widget w;
5807   XEvent *event;
5808   String *prms;
5809   Cardinal *nprms;
5810 {
5811     XtGetSelectionValue(menuBarWidget,
5812       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5813       /* (XtSelectionCallbackProc) */ PasteGameCB,
5814       NULL, /* client_data passed to PasteGameCB */
5815
5816       /* better to use the time field from the event that triggered the
5817        * call to this function, but that isn't trivial to get
5818        */
5819       CurrentTime
5820     );
5821     return;
5822 }
5823
5824
5825 void AutoSaveGame()
5826 {
5827     SaveGameProc(NULL, NULL, NULL, NULL);
5828 }
5829
5830
5831 void QuitProc(w, event, prms, nprms)
5832      Widget w;
5833      XEvent *event;
5834      String *prms;
5835      Cardinal *nprms;
5836 {
5837     ExitEvent(0);
5838 }
5839
5840 void PauseProc(w, event, prms, nprms)
5841      Widget w;
5842      XEvent *event;
5843      String *prms;
5844      Cardinal *nprms;
5845 {
5846     PauseEvent();
5847 }
5848
5849
5850 void MachineBlackProc(w, event, prms, nprms)
5851      Widget w;
5852      XEvent *event;
5853      String *prms;
5854      Cardinal *nprms;
5855 {
5856     MachineBlackEvent();
5857 }
5858
5859 void MachineWhiteProc(w, event, prms, nprms)
5860      Widget w;
5861      XEvent *event;
5862      String *prms;
5863      Cardinal *nprms;
5864 {
5865     MachineWhiteEvent();
5866 }
5867
5868 void AnalyzeModeProc(w, event, prms, nprms)
5869      Widget w;
5870      XEvent *event;
5871      String *prms;
5872      Cardinal *nprms;
5873 {
5874     char buf[MSG_SIZ];
5875
5876     if (!first.analysisSupport) {
5877       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5878       DisplayError(buf, 0);
5879       return;
5880     }
5881     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5882     if (appData.icsActive) {
5883         if (gameMode != IcsObserving) {
5884           snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5885             DisplayError(buf, 0);
5886             /* secure check */
5887             if (appData.icsEngineAnalyze) {
5888                 if (appData.debugMode)
5889                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5890                 ExitAnalyzeMode();
5891                 ModeHighlight();
5892             }
5893             return;
5894         }
5895         /* if enable, use want disable icsEngineAnalyze */
5896         if (appData.icsEngineAnalyze) {
5897                 ExitAnalyzeMode();
5898                 ModeHighlight();
5899                 return;
5900         }
5901         appData.icsEngineAnalyze = TRUE;
5902         if (appData.debugMode)
5903             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5904     }
5905 #ifndef OPTIONSDIALOG
5906     if (!appData.showThinking)
5907       ShowThinkingProc(w,event,prms,nprms);
5908 #endif
5909
5910     AnalyzeModeEvent();
5911 }
5912
5913 void AnalyzeFileProc(w, event, prms, nprms)
5914      Widget w;
5915      XEvent *event;
5916      String *prms;
5917      Cardinal *nprms;
5918 {
5919     if (!first.analysisSupport) {
5920       char buf[MSG_SIZ];
5921       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5922       DisplayError(buf, 0);
5923       return;
5924     }
5925     Reset(FALSE, TRUE);
5926 #ifndef OPTIONSDIALOG
5927     if (!appData.showThinking)
5928       ShowThinkingProc(w,event,prms,nprms);
5929 #endif
5930     AnalyzeFileEvent();
5931     FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5932     AnalysisPeriodicEvent(1);
5933 }
5934
5935 void TwoMachinesProc(w, event, prms, nprms)
5936      Widget w;
5937      XEvent *event;
5938      String *prms;
5939      Cardinal *nprms;
5940 {
5941     TwoMachinesEvent();
5942 }
5943
5944 void MatchProc(w, event, prms, nprms)
5945      Widget w;
5946      XEvent *event;
5947      String *prms;
5948      Cardinal *nprms;
5949 {
5950     MatchEvent(2);
5951 }
5952
5953 void IcsClientProc(w, event, prms, nprms)
5954      Widget w;
5955      XEvent *event;
5956      String *prms;
5957      Cardinal *nprms;
5958 {
5959     IcsClientEvent();
5960 }
5961
5962 void EditGameProc(w, event, prms, nprms)
5963      Widget w;
5964      XEvent *event;
5965      String *prms;
5966      Cardinal *nprms;
5967 {
5968     EditGameEvent();
5969 }
5970
5971 void EditPositionProc(w, event, prms, nprms)
5972      Widget w;
5973      XEvent *event;
5974      String *prms;
5975      Cardinal *nprms;
5976 {
5977     EditPositionEvent();
5978 }
5979
5980 void TrainingProc(w, event, prms, nprms)
5981      Widget w;
5982      XEvent *event;
5983      String *prms;
5984      Cardinal *nprms;
5985 {
5986     TrainingEvent();
5987 }
5988
5989 void EditCommentProc(w, event, prms, nprms)
5990      Widget w;
5991      XEvent *event;
5992      String *prms;
5993      Cardinal *nprms;
5994 {
5995     Arg args[5];
5996     int j;
5997     if (PopDown(1)) { // popdown succesful
5998         j = 0;
5999         XtSetArg(args[j], XtNleftBitmap, None); j++;
6000         XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6001         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6002     } else // was not up
6003         EditCommentEvent();
6004 }
6005
6006 void IcsInputBoxProc(w, event, prms, nprms)
6007      Widget w;
6008      XEvent *event;
6009      String *prms;
6010      Cardinal *nprms;
6011 {
6012     if (!PopDown(4)) ICSInputBoxPopUp();
6013 }
6014
6015 void AcceptProc(w, event, prms, nprms)
6016      Widget w;
6017      XEvent *event;
6018      String *prms;
6019      Cardinal *nprms;
6020 {
6021     AcceptEvent();
6022 }
6023
6024 void DeclineProc(w, event, prms, nprms)
6025      Widget w;
6026      XEvent *event;
6027      String *prms;
6028      Cardinal *nprms;
6029 {
6030     DeclineEvent();
6031 }
6032
6033 void RematchProc(w, event, prms, nprms)
6034      Widget w;
6035      XEvent *event;
6036      String *prms;
6037      Cardinal *nprms;
6038 {
6039     RematchEvent();
6040 }
6041
6042 void CallFlagProc(w, event, prms, nprms)
6043      Widget w;
6044      XEvent *event;
6045      String *prms;
6046      Cardinal *nprms;
6047 {
6048     CallFlagEvent();
6049 }
6050
6051 void DrawProc(w, event, prms, nprms)
6052      Widget w;
6053      XEvent *event;
6054      String *prms;
6055      Cardinal *nprms;
6056 {
6057     DrawEvent();
6058 }
6059
6060 void AbortProc(w, event, prms, nprms)
6061      Widget w;
6062      XEvent *event;
6063      String *prms;
6064      Cardinal *nprms;
6065 {
6066     AbortEvent();
6067 }
6068
6069 void AdjournProc(w, event, prms, nprms)
6070      Widget w;
6071      XEvent *event;
6072      String *prms;
6073      Cardinal *nprms;
6074 {
6075     AdjournEvent();
6076 }
6077
6078 void ResignProc(w, event, prms, nprms)
6079      Widget w;
6080      XEvent *event;
6081      String *prms;
6082      Cardinal *nprms;
6083 {
6084     ResignEvent();
6085 }
6086
6087 void AdjuWhiteProc(w, event, prms, nprms)
6088      Widget w;
6089      XEvent *event;
6090      String *prms;
6091      Cardinal *nprms;
6092 {
6093     UserAdjudicationEvent(+1);
6094 }
6095
6096 void AdjuBlackProc(w, event, prms, nprms)
6097      Widget w;
6098      XEvent *event;
6099      String *prms;
6100      Cardinal *nprms;
6101 {
6102     UserAdjudicationEvent(-1);
6103 }
6104
6105 void AdjuDrawProc(w, event, prms, nprms)
6106      Widget w;
6107      XEvent *event;
6108      String *prms;
6109      Cardinal *nprms;
6110 {
6111     UserAdjudicationEvent(0);
6112 }
6113
6114 void EnterKeyProc(w, event, prms, nprms)
6115      Widget w;
6116      XEvent *event;
6117      String *prms;
6118      Cardinal *nprms;
6119 {
6120     if (shellUp[4] == True)
6121       ICSInputSendText();
6122 }
6123
6124 void UpKeyProc(w, event, prms, nprms)
6125      Widget w;
6126      XEvent *event;
6127      String *prms;
6128      Cardinal *nprms;
6129 {   // [HGM] input: let up-arrow recall previous line from history
6130     Widget edit;
6131     int j;
6132     Arg args[16];
6133     String val;
6134     XawTextBlock t;
6135
6136     if (!shellUp[4]) return;
6137     edit = boxOptions[0].handle;
6138     j = 0;
6139     XtSetArg(args[j], XtNstring, &val); j++;
6140     XtGetValues(edit, args, j);
6141     val = PrevInHistory(val);
6142     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6143     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6144     if(val) {
6145         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6146         XawTextReplace(edit, 0, 0, &t);
6147         XawTextSetInsertionPoint(edit, 9999);
6148     }
6149 }
6150
6151 void DownKeyProc(w, event, prms, nprms)
6152      Widget w;
6153      XEvent *event;
6154      String *prms;
6155      Cardinal *nprms;
6156 {   // [HGM] input: let down-arrow recall next line from history
6157     Widget edit;
6158     String val;
6159     XawTextBlock t;
6160
6161     if (!shellUp[4]) return;
6162     edit = boxOptions[0].handle;
6163     val = NextInHistory();
6164     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6165     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6166     if(val) {
6167         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6168         XawTextReplace(edit, 0, 0, &t);
6169         XawTextSetInsertionPoint(edit, 9999);
6170     }
6171 }
6172
6173 void StopObservingProc(w, event, prms, nprms)
6174      Widget w;
6175      XEvent *event;
6176      String *prms;
6177      Cardinal *nprms;
6178 {
6179     StopObservingEvent();
6180 }
6181
6182 void StopExaminingProc(w, event, prms, nprms)
6183      Widget w;
6184      XEvent *event;
6185      String *prms;
6186      Cardinal *nprms;
6187 {
6188     StopExaminingEvent();
6189 }
6190
6191 void UploadProc(w, event, prms, nprms)
6192      Widget w;
6193      XEvent *event;
6194      String *prms;
6195      Cardinal *nprms;
6196 {
6197     UploadGameEvent();
6198 }
6199
6200
6201 void ForwardProc(w, event, prms, nprms)
6202      Widget w;
6203      XEvent *event;
6204      String *prms;
6205      Cardinal *nprms;
6206 {
6207     ForwardEvent();
6208 }
6209
6210
6211 void BackwardProc(w, event, prms, nprms)
6212      Widget w;
6213      XEvent *event;
6214      String *prms;
6215      Cardinal *nprms;
6216 {
6217     BackwardEvent();
6218 }
6219
6220 void ToStartProc(w, event, prms, nprms)
6221      Widget w;
6222      XEvent *event;
6223      String *prms;
6224      Cardinal *nprms;
6225 {
6226     ToStartEvent();
6227 }
6228
6229 void ToEndProc(w, event, prms, nprms)
6230      Widget w;
6231      XEvent *event;
6232      String *prms;
6233      Cardinal *nprms;
6234 {
6235     ToEndEvent();
6236 }
6237
6238 void RevertProc(w, event, prms, nprms)
6239      Widget w;
6240      XEvent *event;
6241      String *prms;
6242      Cardinal *nprms;
6243 {
6244     RevertEvent(False);
6245 }
6246
6247 void AnnotateProc(w, event, prms, nprms)
6248      Widget w;
6249      XEvent *event;
6250      String *prms;
6251      Cardinal *nprms;
6252 {
6253     RevertEvent(True);
6254 }
6255
6256 void TruncateGameProc(w, event, prms, nprms)
6257      Widget w;
6258      XEvent *event;
6259      String *prms;
6260      Cardinal *nprms;
6261 {
6262     TruncateGameEvent();
6263 }
6264 void RetractMoveProc(w, event, prms, nprms)
6265      Widget w;
6266      XEvent *event;
6267      String *prms;
6268      Cardinal *nprms;
6269 {
6270     RetractMoveEvent();
6271 }
6272
6273 void MoveNowProc(w, event, prms, nprms)
6274      Widget w;
6275      XEvent *event;
6276      String *prms;
6277      Cardinal *nprms;
6278 {
6279     MoveNowEvent();
6280 }
6281
6282 void FlipViewProc(w, event, prms, nprms)
6283      Widget w;
6284      XEvent *event;
6285      String *prms;
6286      Cardinal *nprms;
6287 {
6288     flipView = !flipView;
6289     DrawPosition(True, NULL);
6290 }
6291
6292 void PonderNextMoveProc(w, event, prms, nprms)
6293      Widget w;
6294      XEvent *event;
6295      String *prms;
6296      Cardinal *nprms;
6297 {
6298     Arg args[16];
6299
6300     PonderNextMoveEvent(!appData.ponderNextMove);
6301 #ifndef OPTIONSDIALOG
6302     if (appData.ponderNextMove) {
6303         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6304     } else {
6305         XtSetArg(args[0], XtNleftBitmap, None);
6306     }
6307     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6308                 args, 1);
6309 #endif
6310 }
6311
6312 #ifndef OPTIONSDIALOG
6313 void AlwaysQueenProc(w, event, prms, nprms)
6314      Widget w;
6315      XEvent *event;
6316      String *prms;
6317      Cardinal *nprms;
6318 {
6319     Arg args[16];
6320
6321     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6322
6323     if (appData.alwaysPromoteToQueen) {
6324         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6325     } else {
6326         XtSetArg(args[0], XtNleftBitmap, None);
6327     }
6328     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6329                 args, 1);
6330 }
6331
6332 void AnimateDraggingProc(w, event, prms, nprms)
6333      Widget w;
6334      XEvent *event;
6335      String *prms;
6336      Cardinal *nprms;
6337 {
6338     Arg args[16];
6339
6340     appData.animateDragging = !appData.animateDragging;
6341
6342     if (appData.animateDragging) {
6343         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6344         CreateAnimVars();
6345     } else {
6346         XtSetArg(args[0], XtNleftBitmap, None);
6347     }
6348     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6349                 args, 1);
6350 }
6351
6352 void AnimateMovingProc(w, event, prms, nprms)
6353      Widget w;
6354      XEvent *event;
6355      String *prms;
6356      Cardinal *nprms;
6357 {
6358     Arg args[16];
6359
6360     appData.animate = !appData.animate;
6361
6362     if (appData.animate) {
6363         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6364         CreateAnimVars();
6365     } else {
6366         XtSetArg(args[0], XtNleftBitmap, None);
6367     }
6368     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6369                 args, 1);
6370 }
6371
6372 void AutoflagProc(w, event, prms, nprms)
6373      Widget w;
6374      XEvent *event;
6375      String *prms;
6376      Cardinal *nprms;
6377 {
6378     Arg args[16];
6379
6380     appData.autoCallFlag = !appData.autoCallFlag;
6381
6382     if (appData.autoCallFlag) {
6383         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6384     } else {
6385         XtSetArg(args[0], XtNleftBitmap, None);
6386     }
6387     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6388                 args, 1);
6389 }
6390
6391 void AutoflipProc(w, event, prms, nprms)
6392      Widget w;
6393      XEvent *event;
6394      String *prms;
6395      Cardinal *nprms;
6396 {
6397     Arg args[16];
6398
6399     appData.autoFlipView = !appData.autoFlipView;
6400
6401     if (appData.autoFlipView) {
6402         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6403     } else {
6404         XtSetArg(args[0], XtNleftBitmap, None);
6405     }
6406     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6407                 args, 1);
6408 }
6409
6410 void BlindfoldProc(w, event, prms, nprms)
6411      Widget w;
6412      XEvent *event;
6413      String *prms;
6414      Cardinal *nprms;
6415 {
6416     Arg args[16];
6417
6418     appData.blindfold = !appData.blindfold;
6419
6420     if (appData.blindfold) {
6421         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6422     } else {
6423         XtSetArg(args[0], XtNleftBitmap, None);
6424     }
6425     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6426                 args, 1);
6427
6428     DrawPosition(True, NULL);
6429 }
6430
6431 void TestLegalityProc(w, event, prms, nprms)
6432      Widget w;
6433      XEvent *event;
6434      String *prms;
6435      Cardinal *nprms;
6436 {
6437     Arg args[16];
6438
6439     appData.testLegality = !appData.testLegality;
6440
6441     if (appData.testLegality) {
6442         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6443     } else {
6444         XtSetArg(args[0], XtNleftBitmap, None);
6445     }
6446     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6447                 args, 1);
6448 }
6449
6450
6451 void FlashMovesProc(w, event, prms, nprms)
6452      Widget w;
6453      XEvent *event;
6454      String *prms;
6455      Cardinal *nprms;
6456 {
6457     Arg args[16];
6458
6459     if (appData.flashCount == 0) {
6460         appData.flashCount = 3;
6461     } else {
6462         appData.flashCount = -appData.flashCount;
6463     }
6464
6465     if (appData.flashCount > 0) {
6466         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6467     } else {
6468         XtSetArg(args[0], XtNleftBitmap, None);
6469     }
6470     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6471                 args, 1);
6472 }
6473
6474 #if HIGHDRAG
6475 void HighlightDraggingProc(w, event, prms, nprms)
6476      Widget w;
6477      XEvent *event;
6478      String *prms;
6479      Cardinal *nprms;
6480 {
6481     Arg args[16];
6482
6483     appData.highlightDragging = !appData.highlightDragging;
6484
6485     if (appData.highlightDragging) {
6486         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6487     } else {
6488         XtSetArg(args[0], XtNleftBitmap, None);
6489     }
6490     XtSetValues(XtNameToWidget(menuBarWidget,
6491                                "menuOptions.Highlight Dragging"), args, 1);
6492 }
6493 #endif
6494
6495 void HighlightLastMoveProc(w, event, prms, nprms)
6496      Widget w;
6497      XEvent *event;
6498      String *prms;
6499      Cardinal *nprms;
6500 {
6501     Arg args[16];
6502
6503     appData.highlightLastMove = !appData.highlightLastMove;
6504
6505     if (appData.highlightLastMove) {
6506         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6507     } else {
6508         XtSetArg(args[0], XtNleftBitmap, None);
6509     }
6510     XtSetValues(XtNameToWidget(menuBarWidget,
6511                                "menuOptions.Highlight Last Move"), args, 1);
6512 }
6513
6514 void HighlightArrowProc(w, event, prms, nprms)
6515      Widget w;
6516      XEvent *event;
6517      String *prms;
6518      Cardinal *nprms;
6519 {
6520     Arg args[16];
6521
6522     appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6523
6524     if (appData.highlightMoveWithArrow) {
6525         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6526     } else {
6527         XtSetArg(args[0], XtNleftBitmap, None);
6528     }
6529     XtSetValues(XtNameToWidget(menuBarWidget,
6530                                "menuOptions.Arrow"), args, 1);
6531 }
6532
6533 #if 0
6534 void IcsAlarmProc(w, event, prms, nprms)
6535      Widget w;
6536      XEvent *event;
6537      String *prms;
6538      Cardinal *nprms;
6539 {
6540     Arg args[16];
6541
6542     appData.icsAlarm = !appData.icsAlarm;
6543
6544     if (appData.icsAlarm) {
6545         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6546     } else {
6547         XtSetArg(args[0], XtNleftBitmap, None);
6548     }
6549     XtSetValues(XtNameToWidget(menuBarWidget,
6550                                "menuOptions.ICS Alarm"), args, 1);
6551 }
6552 #endif
6553
6554 void MoveSoundProc(w, event, prms, nprms)
6555      Widget w;
6556      XEvent *event;
6557      String *prms;
6558      Cardinal *nprms;
6559 {
6560     Arg args[16];
6561
6562     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6563
6564     if (appData.ringBellAfterMoves) {
6565         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6566     } else {
6567         XtSetArg(args[0], XtNleftBitmap, None);
6568     }
6569     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6570                 args, 1);
6571 }
6572
6573 void OneClickProc(w, event, prms, nprms)
6574      Widget w;
6575      XEvent *event;
6576      String *prms;
6577      Cardinal *nprms;
6578 {
6579     Arg args[16];
6580
6581     appData.oneClick = !appData.oneClick;
6582
6583     if (appData.oneClick) {
6584         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6585     } else {
6586         XtSetArg(args[0], XtNleftBitmap, None);
6587     }
6588     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6589                 args, 1);
6590 }
6591
6592 void PeriodicUpdatesProc(w, event, prms, nprms)
6593      Widget w;
6594      XEvent *event;
6595      String *prms;
6596      Cardinal *nprms;
6597 {
6598     Arg args[16];
6599
6600     PeriodicUpdatesEvent(!appData.periodicUpdates);
6601
6602     if (appData.periodicUpdates) {
6603         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6604     } else {
6605         XtSetArg(args[0], XtNleftBitmap, None);
6606     }
6607     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6608                 args, 1);
6609 }
6610
6611 void PopupExitMessageProc(w, event, prms, nprms)
6612      Widget w;
6613      XEvent *event;
6614      String *prms;
6615      Cardinal *nprms;
6616 {
6617     Arg args[16];
6618
6619     appData.popupExitMessage = !appData.popupExitMessage;
6620
6621     if (appData.popupExitMessage) {
6622         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6623     } else {
6624         XtSetArg(args[0], XtNleftBitmap, None);
6625     }
6626     XtSetValues(XtNameToWidget(menuBarWidget,
6627                                "menuOptions.Popup Exit Message"), args, 1);
6628 }
6629
6630 void PopupMoveErrorsProc(w, event, prms, nprms)
6631      Widget w;
6632      XEvent *event;
6633      String *prms;
6634      Cardinal *nprms;
6635 {
6636     Arg args[16];
6637
6638     appData.popupMoveErrors = !appData.popupMoveErrors;
6639
6640     if (appData.popupMoveErrors) {
6641         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6642     } else {
6643         XtSetArg(args[0], XtNleftBitmap, None);
6644     }
6645     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6646                 args, 1);
6647 }
6648
6649 #if 0
6650 void PremoveProc(w, event, prms, nprms)
6651      Widget w;
6652      XEvent *event;
6653      String *prms;
6654      Cardinal *nprms;
6655 {
6656     Arg args[16];
6657
6658     appData.premove = !appData.premove;
6659
6660     if (appData.premove) {
6661         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6662     } else {
6663         XtSetArg(args[0], XtNleftBitmap, None);
6664     }
6665     XtSetValues(XtNameToWidget(menuBarWidget,
6666                                "menuOptions.Premove"), args, 1);
6667 }
6668 #endif
6669
6670 void ShowCoordsProc(w, event, prms, nprms)
6671      Widget w;
6672      XEvent *event;
6673      String *prms;
6674      Cardinal *nprms;
6675 {
6676     Arg args[16];
6677
6678     appData.showCoords = !appData.showCoords;
6679
6680     if (appData.showCoords) {
6681         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6682     } else {
6683         XtSetArg(args[0], XtNleftBitmap, None);
6684     }
6685     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6686                 args, 1);
6687
6688     DrawPosition(True, NULL);
6689 }
6690
6691 void ShowThinkingProc(w, event, prms, nprms)
6692      Widget w;
6693      XEvent *event;
6694      String *prms;
6695      Cardinal *nprms;
6696 {
6697     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6698     ShowThinkingEvent();
6699 }
6700
6701 void HideThinkingProc(w, event, prms, nprms)
6702      Widget w;
6703      XEvent *event;
6704      String *prms;
6705      Cardinal *nprms;
6706 {
6707     Arg args[16];
6708
6709     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6710     ShowThinkingEvent();
6711
6712     if (appData.hideThinkingFromHuman) {
6713         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6714     } else {
6715         XtSetArg(args[0], XtNleftBitmap, None);
6716     }
6717     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6718                 args, 1);
6719 }
6720 #endif
6721
6722 void SaveOnExitProc(w, event, prms, nprms)
6723      Widget w;
6724      XEvent *event;
6725      String *prms;
6726      Cardinal *nprms;
6727 {
6728     Arg args[16];
6729
6730     saveSettingsOnExit = !saveSettingsOnExit;
6731
6732     if (saveSettingsOnExit) {
6733         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6734     } else {
6735         XtSetArg(args[0], XtNleftBitmap, None);
6736     }
6737     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6738                 args, 1);
6739 }
6740
6741 void SaveSettingsProc(w, event, prms, nprms)
6742      Widget w;
6743      XEvent *event;
6744      String *prms;
6745      Cardinal *nprms;
6746 {
6747      SaveSettings(settingsFileName);
6748 }
6749
6750 void InfoProc(w, event, prms, nprms)
6751      Widget w;
6752      XEvent *event;
6753      String *prms;
6754      Cardinal *nprms;
6755 {
6756     char buf[MSG_SIZ];
6757     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6758             INFODIR, INFOFILE);
6759     system(buf);
6760 }
6761
6762 void ManProc(w, event, prms, nprms)
6763      Widget w;
6764      XEvent *event;
6765      String *prms;
6766      Cardinal *nprms;
6767 {
6768     char buf[MSG_SIZ];
6769     String name;
6770     if (nprms && *nprms > 0)
6771       name = prms[0];
6772     else
6773       name = "xboard";
6774     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6775     system(buf);
6776 }
6777
6778 void HintProc(w, event, prms, nprms)
6779      Widget w;
6780      XEvent *event;
6781      String *prms;
6782      Cardinal *nprms;
6783 {
6784     HintEvent();
6785 }
6786
6787 void BookProc(w, event, prms, nprms)
6788      Widget w;
6789      XEvent *event;
6790      String *prms;
6791      Cardinal *nprms;
6792 {
6793     BookEvent();
6794 }
6795
6796 void AboutProc(w, event, prms, nprms)
6797      Widget w;
6798      XEvent *event;
6799      String *prms;
6800      Cardinal *nprms;
6801 {
6802     char buf[MSG_SIZ];
6803 #if ZIPPY
6804     char *zippy = " (with Zippy code)";
6805 #else
6806     char *zippy = "";
6807 #endif
6808     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6809             programVersion, zippy,
6810             "Copyright 1991 Digital Equipment Corporation",
6811             "Enhancements Copyright 1992-2009 Free Software Foundation",
6812             "Enhancements Copyright 2005 Alessandro Scotti",
6813             PACKAGE, " is free software and carries NO WARRANTY;",
6814             "see the file COPYING for more information.");
6815     ErrorPopUp(_("About XBoard"), buf, FALSE);
6816 }
6817
6818 void DebugProc(w, event, prms, nprms)
6819      Widget w;
6820      XEvent *event;
6821      String *prms;
6822      Cardinal *nprms;
6823 {
6824     appData.debugMode = !appData.debugMode;
6825 }
6826
6827 void AboutGameProc(w, event, prms, nprms)
6828      Widget w;
6829      XEvent *event;
6830      String *prms;
6831      Cardinal *nprms;
6832 {
6833     AboutGameEvent();
6834 }
6835
6836 void NothingProc(w, event, prms, nprms)
6837      Widget w;
6838      XEvent *event;
6839      String *prms;
6840      Cardinal *nprms;
6841 {
6842     return;
6843 }
6844
6845 void Iconify(w, event, prms, nprms)
6846      Widget w;
6847      XEvent *event;
6848      String *prms;
6849      Cardinal *nprms;
6850 {
6851     Arg args[16];
6852
6853     fromX = fromY = -1;
6854     XtSetArg(args[0], XtNiconic, True);
6855     XtSetValues(shellWidget, args, 1);
6856 }
6857
6858 void DisplayMessage(message, extMessage)
6859      char *message, *extMessage;
6860 {
6861   /* display a message in the message widget */
6862
6863   char buf[MSG_SIZ];
6864   Arg arg;
6865
6866   if (extMessage)
6867     {
6868       if (*message)
6869         {
6870           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
6871           message = buf;
6872         }
6873       else
6874         {
6875           message = extMessage;
6876         };
6877     };
6878
6879     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6880
6881   /* need to test if messageWidget already exists, since this function
6882      can also be called during the startup, if for example a Xresource
6883      is not set up correctly */
6884   if(messageWidget)
6885     {
6886       XtSetArg(arg, XtNlabel, message);
6887       XtSetValues(messageWidget, &arg, 1);
6888     };
6889
6890   return;
6891 }
6892
6893 void DisplayTitle(text)
6894      char *text;
6895 {
6896     Arg args[16];
6897     int i;
6898     char title[MSG_SIZ];
6899     char icon[MSG_SIZ];
6900
6901     if (text == NULL) text = "";
6902
6903     if (appData.titleInWindow) {
6904         i = 0;
6905         XtSetArg(args[i], XtNlabel, text);   i++;
6906         XtSetValues(titleWidget, args, i);
6907     }
6908
6909     if (*text != NULLCHAR) {
6910       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6911       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6912     } else if (appData.icsActive) {
6913         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6914         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6915     } else if (appData.cmailGameName[0] != NULLCHAR) {
6916         snprintf(icon, sizeof(icon), "%s", "CMail");
6917         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6918 #ifdef GOTHIC
6919     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6920     } else if (gameInfo.variant == VariantGothic) {
6921       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
6922       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
6923 #endif
6924 #ifdef FALCON
6925     } else if (gameInfo.variant == VariantFalcon) {
6926       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6927       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6928 #endif
6929     } else if (appData.noChessProgram) {
6930       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6931       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6932     } else {
6933       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6934         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6935     }
6936     i = 0;
6937     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6938     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6939     XtSetValues(shellWidget, args, i);
6940 }
6941
6942
6943 void
6944 DisplayError(message, error)
6945      String message;
6946      int error;
6947 {
6948     char buf[MSG_SIZ];
6949
6950     if (error == 0) {
6951         if (appData.debugMode || appData.matchMode) {
6952             fprintf(stderr, "%s: %s\n", programName, message);
6953         }
6954     } else {
6955         if (appData.debugMode || appData.matchMode) {
6956             fprintf(stderr, "%s: %s: %s\n",
6957                     programName, message, strerror(error));
6958         }
6959         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6960         message = buf;
6961     }
6962     ErrorPopUp(_("Error"), message, FALSE);
6963 }
6964
6965
6966 void DisplayMoveError(message)
6967      String message;
6968 {
6969     fromX = fromY = -1;
6970     ClearHighlights();
6971     DrawPosition(FALSE, NULL);
6972     if (appData.debugMode || appData.matchMode) {
6973         fprintf(stderr, "%s: %s\n", programName, message);
6974     }
6975     if (appData.popupMoveErrors) {
6976         ErrorPopUp(_("Error"), message, FALSE);
6977     } else {
6978         DisplayMessage(message, "");
6979     }
6980 }
6981
6982
6983 void DisplayFatalError(message, error, status)
6984      String message;
6985      int error, status;
6986 {
6987     char buf[MSG_SIZ];
6988
6989     errorExitStatus = status;
6990     if (error == 0) {
6991         fprintf(stderr, "%s: %s\n", programName, message);
6992     } else {
6993         fprintf(stderr, "%s: %s: %s\n",
6994                 programName, message, strerror(error));
6995         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6996         message = buf;
6997     }
6998     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6999       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7000     } else {
7001       ExitEvent(status);
7002     }
7003 }
7004
7005 void DisplayInformation(message)
7006      String message;
7007 {
7008     ErrorPopDown();
7009     ErrorPopUp(_("Information"), message, TRUE);
7010 }
7011
7012 void DisplayNote(message)
7013      String message;
7014 {
7015     ErrorPopDown();
7016     ErrorPopUp(_("Note"), message, FALSE);
7017 }
7018
7019 static int
7020 NullXErrorCheck(dpy, error_event)
7021      Display *dpy;
7022      XErrorEvent *error_event;
7023 {
7024     return 0;
7025 }
7026
7027 void DisplayIcsInteractionTitle(message)
7028      String message;
7029 {
7030   if (oldICSInteractionTitle == NULL) {
7031     /* Magic to find the old window title, adapted from vim */
7032     char *wina = getenv("WINDOWID");
7033     if (wina != NULL) {
7034       Window win = (Window) atoi(wina);
7035       Window root, parent, *children;
7036       unsigned int nchildren;
7037       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7038       for (;;) {
7039         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7040         if (!XQueryTree(xDisplay, win, &root, &parent,
7041                         &children, &nchildren)) break;
7042         if (children) XFree((void *)children);
7043         if (parent == root || parent == 0) break;
7044         win = parent;
7045       }
7046       XSetErrorHandler(oldHandler);
7047     }
7048     if (oldICSInteractionTitle == NULL) {
7049       oldICSInteractionTitle = "xterm";
7050     }
7051   }
7052   printf("\033]0;%s\007", message);
7053   fflush(stdout);
7054 }
7055
7056 char pendingReplyPrefix[MSG_SIZ];
7057 ProcRef pendingReplyPR;
7058
7059 void AskQuestionProc(w, event, prms, nprms)
7060      Widget w;
7061      XEvent *event;
7062      String *prms;
7063      Cardinal *nprms;
7064 {
7065     if (*nprms != 4) {
7066         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7067                 *nprms);
7068         return;
7069     }
7070     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7071 }
7072
7073 void AskQuestionPopDown()
7074 {
7075     if (!askQuestionUp) return;
7076     XtPopdown(askQuestionShell);
7077     XtDestroyWidget(askQuestionShell);
7078     askQuestionUp = False;
7079 }
7080
7081 void AskQuestionReplyAction(w, event, prms, nprms)
7082      Widget w;
7083      XEvent *event;
7084      String *prms;
7085      Cardinal *nprms;
7086 {
7087     char buf[MSG_SIZ];
7088     int err;
7089     String reply;
7090
7091     reply = XawDialogGetValueString(w = XtParent(w));
7092     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7093     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7094     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7095     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
7096     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7097     AskQuestionPopDown();
7098
7099     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7100 }
7101
7102 void AskQuestionCallback(w, client_data, call_data)
7103      Widget w;
7104      XtPointer client_data, call_data;
7105 {
7106     String name;
7107     Arg args[16];
7108
7109     XtSetArg(args[0], XtNlabel, &name);
7110     XtGetValues(w, args, 1);
7111
7112     if (strcmp(name, _("cancel")) == 0) {
7113         AskQuestionPopDown();
7114     } else {
7115         AskQuestionReplyAction(w, NULL, NULL, NULL);
7116     }
7117 }
7118
7119 void AskQuestion(title, question, replyPrefix, pr)
7120      char *title, *question, *replyPrefix;
7121      ProcRef pr;
7122 {
7123     Arg args[16];
7124     Widget popup, layout, dialog, edit;
7125     Window root, child;
7126     int x, y, i;
7127     int win_x, win_y;
7128     unsigned int mask;
7129
7130     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7131     pendingReplyPR = pr;
7132
7133     i = 0;
7134     XtSetArg(args[i], XtNresizable, True); i++;
7135     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7136     askQuestionShell = popup =
7137       XtCreatePopupShell(title, transientShellWidgetClass,
7138                          shellWidget, args, i);
7139
7140     layout =
7141       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7142                             layoutArgs, XtNumber(layoutArgs));
7143
7144     i = 0;
7145     XtSetArg(args[i], XtNlabel, question); i++;
7146     XtSetArg(args[i], XtNvalue, ""); i++;
7147     XtSetArg(args[i], XtNborderWidth, 0); i++;
7148     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7149                                    layout, args, i);
7150
7151     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7152                        (XtPointer) dialog);
7153     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7154                        (XtPointer) dialog);
7155
7156     XtRealizeWidget(popup);
7157     CatchDeleteWindow(popup, "AskQuestionPopDown");
7158
7159     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7160                   &x, &y, &win_x, &win_y, &mask);
7161
7162     XtSetArg(args[0], XtNx, x - 10);
7163     XtSetArg(args[1], XtNy, y - 30);
7164     XtSetValues(popup, args, 2);
7165
7166     XtPopup(popup, XtGrabExclusive);
7167     askQuestionUp = True;
7168
7169     edit = XtNameToWidget(dialog, "*value");
7170     XtSetKeyboardFocus(popup, edit);
7171 }
7172
7173
7174 void
7175 PlaySound(name)
7176      char *name;
7177 {
7178   if (*name == NULLCHAR) {
7179     return;
7180   } else if (strcmp(name, "$") == 0) {
7181     putc(BELLCHAR, stderr);
7182   } else {
7183     char buf[2048];
7184     char *prefix = "", *sep = "";
7185     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7186     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7187     system(buf);
7188   }
7189 }
7190
7191 void
7192 RingBell()
7193 {
7194   PlaySound(appData.soundMove);
7195 }
7196
7197 void
7198 PlayIcsWinSound()
7199 {
7200   PlaySound(appData.soundIcsWin);
7201 }
7202
7203 void
7204 PlayIcsLossSound()
7205 {
7206   PlaySound(appData.soundIcsLoss);
7207 }
7208
7209 void
7210 PlayIcsDrawSound()
7211 {
7212   PlaySound(appData.soundIcsDraw);
7213 }
7214
7215 void
7216 PlayIcsUnfinishedSound()
7217 {
7218   PlaySound(appData.soundIcsUnfinished);
7219 }
7220
7221 void
7222 PlayAlarmSound()
7223 {
7224   PlaySound(appData.soundIcsAlarm);
7225 }
7226
7227 void
7228 PlayTellSound()
7229 {
7230   PlaySound(appData.soundTell);
7231 }
7232
7233 void
7234 EchoOn()
7235 {
7236     system("stty echo");
7237     noEcho = False;
7238 }
7239
7240 void
7241 EchoOff()
7242 {
7243     system("stty -echo");
7244     noEcho = True;
7245 }
7246
7247 void
7248 Colorize(cc, continuation)
7249      ColorClass cc;
7250      int continuation;
7251 {
7252     char buf[MSG_SIZ];
7253     int count, outCount, error;
7254
7255     if (textColors[(int)cc].bg > 0) {
7256         if (textColors[(int)cc].fg > 0) {
7257           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7258                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7259         } else {
7260           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7261                    textColors[(int)cc].bg);
7262         }
7263     } else {
7264         if (textColors[(int)cc].fg > 0) {
7265           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7266                     textColors[(int)cc].fg);
7267         } else {
7268           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7269         }
7270     }
7271     count = strlen(buf);
7272     outCount = OutputToProcess(NoProc, buf, count, &error);
7273     if (outCount < count) {
7274         DisplayFatalError(_("Error writing to display"), error, 1);
7275     }
7276
7277     if (continuation) return;
7278     switch (cc) {
7279     case ColorShout:
7280       PlaySound(appData.soundShout);
7281       break;
7282     case ColorSShout:
7283       PlaySound(appData.soundSShout);
7284       break;
7285     case ColorChannel1:
7286       PlaySound(appData.soundChannel1);
7287       break;
7288     case ColorChannel:
7289       PlaySound(appData.soundChannel);
7290       break;
7291     case ColorKibitz:
7292       PlaySound(appData.soundKibitz);
7293       break;
7294     case ColorTell:
7295       PlaySound(appData.soundTell);
7296       break;
7297     case ColorChallenge:
7298       PlaySound(appData.soundChallenge);
7299       break;
7300     case ColorRequest:
7301       PlaySound(appData.soundRequest);
7302       break;
7303     case ColorSeek:
7304       PlaySound(appData.soundSeek);
7305       break;
7306     case ColorNormal:
7307     case ColorNone:
7308     default:
7309       break;
7310     }
7311 }
7312
7313 char *UserName()
7314 {
7315     return getpwuid(getuid())->pw_name;
7316 }
7317
7318 static char *
7319 ExpandPathName(path)
7320      char *path;
7321 {
7322     static char static_buf[4*MSG_SIZ];
7323     char *d, *s, buf[4*MSG_SIZ];
7324     struct passwd *pwd;
7325
7326     s = path;
7327     d = static_buf;
7328
7329     while (*s && isspace(*s))
7330       ++s;
7331
7332     if (!*s) {
7333         *d = 0;
7334         return static_buf;
7335     }
7336
7337     if (*s == '~') {
7338         if (*(s+1) == '/') {
7339           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7340           strcat(d, s+1);
7341         }
7342         else {
7343           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7344           { char *p; if(p = strchr(buf, '/')) *p = 0; }
7345           pwd = getpwnam(buf);
7346           if (!pwd)
7347             {
7348               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7349                       buf, path);
7350               return NULL;
7351             }
7352           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7353           strcat(d, strchr(s+1, '/'));
7354         }
7355     }
7356     else
7357       safeStrCpy(d, s, 4*MSG_SIZ );
7358
7359     return static_buf;
7360 }
7361
7362 char *HostName()
7363 {
7364     static char host_name[MSG_SIZ];
7365
7366 #if HAVE_GETHOSTNAME
7367     gethostname(host_name, MSG_SIZ);
7368     return host_name;
7369 #else  /* not HAVE_GETHOSTNAME */
7370 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7371     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7372     return host_name;
7373 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7374     return "localhost";
7375 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7376 #endif /* not HAVE_GETHOSTNAME */
7377 }
7378
7379 XtIntervalId delayedEventTimerXID = 0;
7380 DelayedEventCallback delayedEventCallback = 0;
7381
7382 void
7383 FireDelayedEvent()
7384 {
7385     delayedEventTimerXID = 0;
7386     delayedEventCallback();
7387 }
7388
7389 void
7390 ScheduleDelayedEvent(cb, millisec)
7391      DelayedEventCallback cb; long millisec;
7392 {
7393     if(delayedEventTimerXID && delayedEventCallback == cb)
7394         // [HGM] alive: replace, rather than add or flush identical event
7395         XtRemoveTimeOut(delayedEventTimerXID);
7396     delayedEventCallback = cb;
7397     delayedEventTimerXID =
7398       XtAppAddTimeOut(appContext, millisec,
7399                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7400 }
7401
7402 DelayedEventCallback
7403 GetDelayedEvent()
7404 {
7405   if (delayedEventTimerXID) {
7406     return delayedEventCallback;
7407   } else {
7408     return NULL;
7409   }
7410 }
7411
7412 void
7413 CancelDelayedEvent()
7414 {
7415   if (delayedEventTimerXID) {
7416     XtRemoveTimeOut(delayedEventTimerXID);
7417     delayedEventTimerXID = 0;
7418   }
7419 }
7420
7421 XtIntervalId loadGameTimerXID = 0;
7422
7423 int LoadGameTimerRunning()
7424 {
7425     return loadGameTimerXID != 0;
7426 }
7427
7428 int StopLoadGameTimer()
7429 {
7430     if (loadGameTimerXID != 0) {
7431         XtRemoveTimeOut(loadGameTimerXID);
7432         loadGameTimerXID = 0;
7433         return TRUE;
7434     } else {
7435         return FALSE;
7436     }
7437 }
7438
7439 void
7440 LoadGameTimerCallback(arg, id)
7441      XtPointer arg;
7442      XtIntervalId *id;
7443 {
7444     loadGameTimerXID = 0;
7445     AutoPlayGameLoop();
7446 }
7447
7448 void
7449 StartLoadGameTimer(millisec)
7450      long millisec;
7451 {
7452     loadGameTimerXID =
7453       XtAppAddTimeOut(appContext, millisec,
7454                       (XtTimerCallbackProc) LoadGameTimerCallback,
7455                       (XtPointer) 0);
7456 }
7457
7458 XtIntervalId analysisClockXID = 0;
7459
7460 void
7461 AnalysisClockCallback(arg, id)
7462      XtPointer arg;
7463      XtIntervalId *id;
7464 {
7465     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7466          || appData.icsEngineAnalyze) { // [DM]
7467         AnalysisPeriodicEvent(0);
7468         StartAnalysisClock();
7469     }
7470 }
7471
7472 void
7473 StartAnalysisClock()
7474 {
7475     analysisClockXID =
7476       XtAppAddTimeOut(appContext, 2000,
7477                       (XtTimerCallbackProc) AnalysisClockCallback,
7478                       (XtPointer) 0);
7479 }
7480
7481 XtIntervalId clockTimerXID = 0;
7482
7483 int ClockTimerRunning()
7484 {
7485     return clockTimerXID != 0;
7486 }
7487
7488 int StopClockTimer()
7489 {
7490     if (clockTimerXID != 0) {
7491         XtRemoveTimeOut(clockTimerXID);
7492         clockTimerXID = 0;
7493         return TRUE;
7494     } else {
7495         return FALSE;
7496     }
7497 }
7498
7499 void
7500 ClockTimerCallback(arg, id)
7501      XtPointer arg;
7502      XtIntervalId *id;
7503 {
7504     clockTimerXID = 0;
7505     DecrementClocks();
7506 }
7507
7508 void
7509 StartClockTimer(millisec)
7510      long millisec;
7511 {
7512     clockTimerXID =
7513       XtAppAddTimeOut(appContext, millisec,
7514                       (XtTimerCallbackProc) ClockTimerCallback,
7515                       (XtPointer) 0);
7516 }
7517
7518 void
7519 DisplayTimerLabel(w, color, timer, highlight)
7520      Widget w;
7521      char *color;
7522      long timer;
7523      int highlight;
7524 {
7525     char buf[MSG_SIZ];
7526     Arg args[16];
7527
7528     /* check for low time warning */
7529     Pixel foregroundOrWarningColor = timerForegroundPixel;
7530
7531     if (timer > 0 &&
7532         appData.lowTimeWarning &&
7533         (timer / 1000) < appData.icsAlarmTime)
7534       foregroundOrWarningColor = lowTimeWarningColor;
7535
7536     if (appData.clockMode) {
7537       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7538       XtSetArg(args[0], XtNlabel, buf);
7539     } else {
7540       snprintf(buf, MSG_SIZ, "%s  ", color);
7541       XtSetArg(args[0], XtNlabel, buf);
7542     }
7543
7544     if (highlight) {
7545
7546         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7547         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7548     } else {
7549         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7550         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7551     }
7552
7553     XtSetValues(w, args, 3);
7554 }
7555
7556 void
7557 DisplayWhiteClock(timeRemaining, highlight)
7558      long timeRemaining;
7559      int highlight;
7560 {
7561     Arg args[16];
7562
7563     if(appData.noGUI) return;
7564     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7565     if (highlight && iconPixmap == bIconPixmap) {
7566         iconPixmap = wIconPixmap;
7567         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7568         XtSetValues(shellWidget, args, 1);
7569     }
7570 }
7571
7572 void
7573 DisplayBlackClock(timeRemaining, highlight)
7574      long timeRemaining;
7575      int highlight;
7576 {
7577     Arg args[16];
7578
7579     if(appData.noGUI) return;
7580     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7581     if (highlight && iconPixmap == wIconPixmap) {
7582         iconPixmap = bIconPixmap;
7583         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7584         XtSetValues(shellWidget, args, 1);
7585     }
7586 }
7587
7588 #define CPNone 0
7589 #define CPReal 1
7590 #define CPComm 2
7591 #define CPSock 3
7592 #define CPLoop 4
7593 typedef int CPKind;
7594
7595 typedef struct {
7596     CPKind kind;
7597     int pid;
7598     int fdTo, fdFrom;
7599 } ChildProc;
7600
7601
7602 int StartChildProcess(cmdLine, dir, pr)
7603      char *cmdLine;
7604      char *dir;
7605      ProcRef *pr;
7606 {
7607     char *argv[64], *p;
7608     int i, pid;
7609     int to_prog[2], from_prog[2];
7610     ChildProc *cp;
7611     char buf[MSG_SIZ];
7612
7613     if (appData.debugMode) {
7614         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7615     }
7616
7617     /* We do NOT feed the cmdLine to the shell; we just
7618        parse it into blank-separated arguments in the
7619        most simple-minded way possible.
7620        */
7621     i = 0;
7622     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7623     p = buf;
7624     for (;;) {
7625         while(*p == ' ') p++;
7626         argv[i++] = p;
7627         if(*p == '"' || *p == '\'')
7628              p = strchr(++argv[i-1], *p);
7629         else p = strchr(p, ' ');
7630         if (p == NULL) break;
7631         *p++ = NULLCHAR;
7632     }
7633     argv[i] = NULL;
7634
7635     SetUpChildIO(to_prog, from_prog);
7636
7637     if ((pid = fork()) == 0) {
7638         /* Child process */
7639         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7640         close(to_prog[1]);     // first close the unused pipe ends
7641         close(from_prog[0]);
7642         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7643         dup2(from_prog[1], 1);
7644         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7645         close(from_prog[1]);                   // and closing again loses one of the pipes!
7646         if(fileno(stderr) >= 2) // better safe than sorry...
7647                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7648
7649         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7650             perror(dir);
7651             exit(1);
7652         }
7653
7654         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7655
7656         execvp(argv[0], argv);
7657
7658         /* If we get here, exec failed */
7659         perror(argv[0]);
7660         exit(1);
7661     }
7662
7663     /* Parent process */
7664     close(to_prog[0]);
7665     close(from_prog[1]);
7666
7667     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7668     cp->kind = CPReal;
7669     cp->pid = pid;
7670     cp->fdFrom = from_prog[0];
7671     cp->fdTo = to_prog[1];
7672     *pr = (ProcRef) cp;
7673     return 0;
7674 }
7675
7676 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7677 static RETSIGTYPE AlarmCallBack(int n)
7678 {
7679     return;
7680 }
7681
7682 void
7683 DestroyChildProcess(pr, signalType)
7684      ProcRef pr;
7685      int signalType;
7686 {
7687     ChildProc *cp = (ChildProc *) pr;
7688
7689     if (cp->kind != CPReal) return;
7690     cp->kind = CPNone;
7691     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7692         signal(SIGALRM, AlarmCallBack);
7693         alarm(3);
7694         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7695             kill(cp->pid, SIGKILL); // kill it forcefully
7696             wait((int *) 0);        // and wait again
7697         }
7698     } else {
7699         if (signalType) {
7700             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7701         }
7702         /* Process is exiting either because of the kill or because of
7703            a quit command sent by the backend; either way, wait for it to die.
7704         */
7705         wait((int *) 0);
7706     }
7707     close(cp->fdFrom);
7708     close(cp->fdTo);
7709 }
7710
7711 void
7712 InterruptChildProcess(pr)
7713      ProcRef pr;
7714 {
7715     ChildProc *cp = (ChildProc *) pr;
7716
7717     if (cp->kind != CPReal) return;
7718     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7719 }
7720
7721 int OpenTelnet(host, port, pr)
7722      char *host;
7723      char *port;
7724      ProcRef *pr;
7725 {
7726     char cmdLine[MSG_SIZ];
7727
7728     if (port[0] == NULLCHAR) {
7729       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7730     } else {
7731       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7732     }
7733     return StartChildProcess(cmdLine, "", pr);
7734 }
7735
7736 int OpenTCP(host, port, pr)
7737      char *host;
7738      char *port;
7739      ProcRef *pr;
7740 {
7741 #if OMIT_SOCKETS
7742     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7743 #else  /* !OMIT_SOCKETS */
7744     struct addrinfo hints;
7745     struct addrinfo *ais, *ai;
7746     int error;
7747     int s=0;
7748     ChildProc *cp;
7749
7750     memset(&hints, 0, sizeof(hints));
7751     hints.ai_family = AF_UNSPEC;
7752     hints.ai_socktype = SOCK_STREAM;
7753
7754     error = getaddrinfo(host, port, &hints, &ais);
7755     if (error != 0) {
7756       /* a getaddrinfo error is not an errno, so can't return it */
7757       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7758               host, port, gai_strerror(error));
7759       return ENOENT;
7760     }
7761      
7762     for (ai = ais; ai != NULL; ai = ai->ai_next) {
7763       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7764         error = errno;
7765         continue;
7766       }
7767       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7768         error = errno;
7769         continue;
7770       }
7771       error = 0;
7772       break;
7773     }
7774     freeaddrinfo(ais);
7775
7776     if (error != 0) {
7777       return error;
7778     }
7779
7780     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7781     cp->kind = CPSock;
7782     cp->pid = 0;
7783     cp->fdFrom = s;
7784     cp->fdTo = s;
7785     *pr = (ProcRef) cp;
7786 #endif /* !OMIT_SOCKETS */
7787
7788     return 0;
7789 }
7790
7791 int OpenCommPort(name, pr)
7792      char *name;
7793      ProcRef *pr;
7794 {
7795     int fd;
7796     ChildProc *cp;
7797
7798     fd = open(name, 2, 0);
7799     if (fd < 0) return errno;
7800
7801     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7802     cp->kind = CPComm;
7803     cp->pid = 0;
7804     cp->fdFrom = fd;
7805     cp->fdTo = fd;
7806     *pr = (ProcRef) cp;
7807
7808     return 0;
7809 }
7810
7811 int OpenLoopback(pr)
7812      ProcRef *pr;
7813 {
7814     ChildProc *cp;
7815     int to[2], from[2];
7816
7817     SetUpChildIO(to, from);
7818
7819     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7820     cp->kind = CPLoop;
7821     cp->pid = 0;
7822     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7823     cp->fdTo = to[1];
7824     *pr = (ProcRef) cp;
7825
7826     return 0;
7827 }
7828
7829 int OpenRcmd(host, user, cmd, pr)
7830      char *host, *user, *cmd;
7831      ProcRef *pr;
7832 {
7833     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7834     return -1;
7835 }
7836
7837 #define INPUT_SOURCE_BUF_SIZE 8192
7838
7839 typedef struct {
7840     CPKind kind;
7841     int fd;
7842     int lineByLine;
7843     char *unused;
7844     InputCallback func;
7845     XtInputId xid;
7846     char buf[INPUT_SOURCE_BUF_SIZE];
7847     VOIDSTAR closure;
7848 } InputSource;
7849
7850 void
7851 DoInputCallback(closure, source, xid)
7852      caddr_t closure;
7853      int *source;
7854      XtInputId *xid;
7855 {
7856     InputSource *is = (InputSource *) closure;
7857     int count;
7858     int error;
7859     char *p, *q;
7860
7861     if (is->lineByLine) {
7862         count = read(is->fd, is->unused,
7863                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7864         if (count <= 0) {
7865             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7866             return;
7867         }
7868         is->unused += count;
7869         p = is->buf;
7870         while (p < is->unused) {
7871             q = memchr(p, '\n', is->unused - p);
7872             if (q == NULL) break;
7873             q++;
7874             (is->func)(is, is->closure, p, q - p, 0);
7875             p = q;
7876         }
7877         q = is->buf;
7878         while (p < is->unused) {
7879             *q++ = *p++;
7880         }
7881         is->unused = q;
7882     } else {
7883         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7884         if (count == -1)
7885           error = errno;
7886         else
7887           error = 0;
7888         (is->func)(is, is->closure, is->buf, count, error);
7889     }
7890 }
7891
7892 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7893      ProcRef pr;
7894      int lineByLine;
7895      InputCallback func;
7896      VOIDSTAR closure;
7897 {
7898     InputSource *is;
7899     ChildProc *cp = (ChildProc *) pr;
7900
7901     is = (InputSource *) calloc(1, sizeof(InputSource));
7902     is->lineByLine = lineByLine;
7903     is->func = func;
7904     if (pr == NoProc) {
7905         is->kind = CPReal;
7906         is->fd = fileno(stdin);
7907     } else {
7908         is->kind = cp->kind;
7909         is->fd = cp->fdFrom;
7910     }
7911     if (lineByLine) {
7912         is->unused = is->buf;
7913     }
7914
7915     is->xid = XtAppAddInput(appContext, is->fd,
7916                             (XtPointer) (XtInputReadMask),
7917                             (XtInputCallbackProc) DoInputCallback,
7918                             (XtPointer) is);
7919     is->closure = closure;
7920     return (InputSourceRef) is;
7921 }
7922
7923 void
7924 RemoveInputSource(isr)
7925      InputSourceRef isr;
7926 {
7927     InputSource *is = (InputSource *) isr;
7928
7929     if (is->xid == 0) return;
7930     XtRemoveInput(is->xid);
7931     is->xid = 0;
7932 }
7933
7934 int OutputToProcess(pr, message, count, outError)
7935      ProcRef pr;
7936      char *message;
7937      int count;
7938      int *outError;
7939 {
7940     static int line = 0;
7941     ChildProc *cp = (ChildProc *) pr;
7942     int outCount;
7943
7944     if (pr == NoProc)
7945     {
7946         if (appData.noJoin || !appData.useInternalWrap)
7947             outCount = fwrite(message, 1, count, stdout);
7948         else
7949         {
7950             int width = get_term_width();
7951             int len = wrap(NULL, message, count, width, &line);
7952             char *msg = malloc(len);
7953             int dbgchk;
7954
7955             if (!msg)
7956                 outCount = fwrite(message, 1, count, stdout);
7957             else
7958             {
7959                 dbgchk = wrap(msg, message, count, width, &line);
7960                 if (dbgchk != len && appData.debugMode)
7961                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7962                 outCount = fwrite(msg, 1, dbgchk, stdout);
7963                 free(msg);
7964             }
7965         }
7966     }
7967     else
7968       outCount = write(cp->fdTo, message, count);
7969
7970     if (outCount == -1)
7971       *outError = errno;
7972     else
7973       *outError = 0;
7974
7975     return outCount;
7976 }
7977
7978 /* Output message to process, with "ms" milliseconds of delay
7979    between each character. This is needed when sending the logon
7980    script to ICC, which for some reason doesn't like the
7981    instantaneous send. */
7982 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7983      ProcRef pr;
7984      char *message;
7985      int count;
7986      int *outError;
7987      long msdelay;
7988 {
7989     ChildProc *cp = (ChildProc *) pr;
7990     int outCount = 0;
7991     int r;
7992
7993     while (count--) {
7994         r = write(cp->fdTo, message++, 1);
7995         if (r == -1) {
7996             *outError = errno;
7997             return outCount;
7998         }
7999         ++outCount;
8000         if (msdelay >= 0)
8001           TimeDelay(msdelay);
8002     }
8003
8004     return outCount;
8005 }
8006
8007 /****   Animation code by Hugh Fisher, DCS, ANU.
8008
8009         Known problem: if a window overlapping the board is
8010         moved away while a piece is being animated underneath,
8011         the newly exposed area won't be updated properly.
8012         I can live with this.
8013
8014         Known problem: if you look carefully at the animation
8015         of pieces in mono mode, they are being drawn as solid
8016         shapes without interior detail while moving. Fixing
8017         this would be a major complication for minimal return.
8018 ****/
8019
8020 /*      Masks for XPM pieces. Black and white pieces can have
8021         different shapes, but in the interest of retaining my
8022         sanity pieces must have the same outline on both light
8023         and dark squares, and all pieces must use the same
8024         background square colors/images.                */
8025
8026 static int xpmDone = 0;
8027
8028 static void
8029 CreateAnimMasks (pieceDepth)
8030      int pieceDepth;
8031 {
8032   ChessSquare   piece;
8033   Pixmap        buf;
8034   GC            bufGC, maskGC;
8035   int           kind, n;
8036   unsigned long plane;
8037   XGCValues     values;
8038
8039   /* Need a bitmap just to get a GC with right depth */
8040   buf = XCreatePixmap(xDisplay, xBoardWindow,
8041                         8, 8, 1);
8042   values.foreground = 1;
8043   values.background = 0;
8044   /* Don't use XtGetGC, not read only */
8045   maskGC = XCreateGC(xDisplay, buf,
8046                     GCForeground | GCBackground, &values);
8047   XFreePixmap(xDisplay, buf);
8048
8049   buf = XCreatePixmap(xDisplay, xBoardWindow,
8050                       squareSize, squareSize, pieceDepth);
8051   values.foreground = XBlackPixel(xDisplay, xScreen);
8052   values.background = XWhitePixel(xDisplay, xScreen);
8053   bufGC = XCreateGC(xDisplay, buf,
8054                     GCForeground | GCBackground, &values);
8055
8056   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8057     /* Begin with empty mask */
8058     if(!xpmDone) // [HGM] pieces: keep using existing
8059     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8060                                  squareSize, squareSize, 1);
8061     XSetFunction(xDisplay, maskGC, GXclear);
8062     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8063                    0, 0, squareSize, squareSize);
8064
8065     /* Take a copy of the piece */
8066     if (White(piece))
8067       kind = 0;
8068     else
8069       kind = 2;
8070     XSetFunction(xDisplay, bufGC, GXcopy);
8071     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8072               buf, bufGC,
8073               0, 0, squareSize, squareSize, 0, 0);
8074
8075     /* XOR the background (light) over the piece */
8076     XSetFunction(xDisplay, bufGC, GXxor);
8077     if (useImageSqs)
8078       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8079                 0, 0, squareSize, squareSize, 0, 0);
8080     else {
8081       XSetForeground(xDisplay, bufGC, lightSquareColor);
8082       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8083     }
8084
8085     /* We now have an inverted piece image with the background
8086        erased. Construct mask by just selecting all the non-zero
8087        pixels - no need to reconstruct the original image.      */
8088     XSetFunction(xDisplay, maskGC, GXor);
8089     plane = 1;
8090     /* Might be quicker to download an XImage and create bitmap
8091        data from it rather than this N copies per piece, but it
8092        only takes a fraction of a second and there is a much
8093        longer delay for loading the pieces.             */
8094     for (n = 0; n < pieceDepth; n ++) {
8095       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8096                  0, 0, squareSize, squareSize,
8097                  0, 0, plane);
8098       plane = plane << 1;
8099     }
8100   }
8101   /* Clean up */
8102   XFreePixmap(xDisplay, buf);
8103   XFreeGC(xDisplay, bufGC);
8104   XFreeGC(xDisplay, maskGC);
8105 }
8106
8107 static void
8108 InitAnimState (anim, info)
8109   AnimState * anim;
8110   XWindowAttributes * info;
8111 {
8112   XtGCMask  mask;
8113   XGCValues values;
8114
8115   /* Each buffer is square size, same depth as window */
8116   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8117                         squareSize, squareSize, info->depth);
8118   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8119                         squareSize, squareSize, info->depth);
8120
8121   /* Create a plain GC for blitting */
8122   mask = GCForeground | GCBackground | GCFunction |
8123          GCPlaneMask | GCGraphicsExposures;
8124   values.foreground = XBlackPixel(xDisplay, xScreen);
8125   values.background = XWhitePixel(xDisplay, xScreen);
8126   values.function   = GXcopy;
8127   values.plane_mask = AllPlanes;
8128   values.graphics_exposures = False;
8129   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8130
8131   /* Piece will be copied from an existing context at
8132      the start of each new animation/drag. */
8133   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8134
8135   /* Outline will be a read-only copy of an existing */
8136   anim->outlineGC = None;
8137 }
8138
8139 static void
8140 CreateAnimVars ()
8141 {
8142   XWindowAttributes info;
8143
8144   if (xpmDone && gameInfo.variant == oldVariant) return;
8145   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8146   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8147
8148   InitAnimState(&game, &info);
8149   InitAnimState(&player, &info);
8150
8151   /* For XPM pieces, we need bitmaps to use as masks. */
8152   if (useImages)
8153     CreateAnimMasks(info.depth), xpmDone = 1;
8154 }
8155
8156 #ifndef HAVE_USLEEP
8157
8158 static Boolean frameWaiting;
8159
8160 static RETSIGTYPE FrameAlarm (sig)
8161      int sig;
8162 {
8163   frameWaiting = False;
8164   /* In case System-V style signals.  Needed?? */
8165   signal(SIGALRM, FrameAlarm);
8166 }
8167
8168 static void
8169 FrameDelay (time)
8170      int time;
8171 {
8172   struct itimerval delay;
8173
8174   XSync(xDisplay, False);
8175
8176   if (time > 0) {
8177     frameWaiting = True;
8178     signal(SIGALRM, FrameAlarm);
8179     delay.it_interval.tv_sec =
8180       delay.it_value.tv_sec = time / 1000;
8181     delay.it_interval.tv_usec =
8182       delay.it_value.tv_usec = (time % 1000) * 1000;
8183     setitimer(ITIMER_REAL, &delay, NULL);
8184     while (frameWaiting) pause();
8185     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8186     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8187     setitimer(ITIMER_REAL, &delay, NULL);
8188   }
8189 }
8190
8191 #else
8192
8193 static void
8194 FrameDelay (time)
8195      int time;
8196 {
8197   XSync(xDisplay, False);
8198   if (time > 0)
8199     usleep(time * 1000);
8200 }
8201
8202 #endif
8203
8204 /*      Convert board position to corner of screen rect and color       */
8205
8206 static void
8207 ScreenSquare(column, row, pt, color)
8208      int column; int row; XPoint * pt; int * color;
8209 {
8210   if (flipView) {
8211     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8212     pt->y = lineGap + row * (squareSize + lineGap);
8213   } else {
8214     pt->x = lineGap + column * (squareSize + lineGap);
8215     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8216   }
8217   *color = SquareColor(row, column);
8218 }
8219
8220 /*      Convert window coords to square                 */
8221
8222 static void
8223 BoardSquare(x, y, column, row)
8224      int x; int y; int * column; int * row;
8225 {
8226   *column = EventToSquare(x, BOARD_WIDTH);
8227   if (flipView && *column >= 0)
8228     *column = BOARD_WIDTH - 1 - *column;
8229   *row = EventToSquare(y, BOARD_HEIGHT);
8230   if (!flipView && *row >= 0)
8231     *row = BOARD_HEIGHT - 1 - *row;
8232 }
8233
8234 /*   Utilities  */
8235
8236 #undef Max  /* just in case */
8237 #undef Min
8238 #define Max(a, b) ((a) > (b) ? (a) : (b))
8239 #define Min(a, b) ((a) < (b) ? (a) : (b))
8240
8241 static void
8242 SetRect(rect, x, y, width, height)
8243      XRectangle * rect; int x; int y; int width; int height;
8244 {
8245   rect->x = x;
8246   rect->y = y;
8247   rect->width  = width;
8248   rect->height = height;
8249 }
8250
8251 /*      Test if two frames overlap. If they do, return
8252         intersection rect within old and location of
8253         that rect within new. */
8254
8255 static Boolean
8256 Intersect(old, new, size, area, pt)
8257      XPoint * old; XPoint * new;
8258      int size; XRectangle * area; XPoint * pt;
8259 {
8260   if (old->x > new->x + size || new->x > old->x + size ||
8261       old->y > new->y + size || new->y > old->y + size) {
8262     return False;
8263   } else {
8264     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8265             size - abs(old->x - new->x), size - abs(old->y - new->y));
8266     pt->x = Max(old->x - new->x, 0);
8267     pt->y = Max(old->y - new->y, 0);
8268     return True;
8269   }
8270 }
8271
8272 /*      For two overlapping frames, return the rect(s)
8273         in the old that do not intersect with the new.   */
8274
8275 static void
8276 CalcUpdateRects(old, new, size, update, nUpdates)
8277      XPoint * old; XPoint * new; int size;
8278      XRectangle update[]; int * nUpdates;
8279 {
8280   int        count;
8281
8282   /* If old = new (shouldn't happen) then nothing to draw */
8283   if (old->x == new->x && old->y == new->y) {
8284     *nUpdates = 0;
8285     return;
8286   }
8287   /* Work out what bits overlap. Since we know the rects
8288      are the same size we don't need a full intersect calc. */
8289   count = 0;
8290   /* Top or bottom edge? */
8291   if (new->y > old->y) {
8292     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8293     count ++;
8294   } else if (old->y > new->y) {
8295     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8296                               size, old->y - new->y);
8297     count ++;
8298   }
8299   /* Left or right edge - don't overlap any update calculated above. */
8300   if (new->x > old->x) {
8301     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8302                               new->x - old->x, size - abs(new->y - old->y));
8303     count ++;
8304   } else if (old->x > new->x) {
8305     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8306                               old->x - new->x, size - abs(new->y - old->y));
8307     count ++;
8308   }
8309   /* Done */
8310   *nUpdates = count;
8311 }
8312
8313 /*      Generate a series of frame coords from start->mid->finish.
8314         The movement rate doubles until the half way point is
8315         reached, then halves back down to the final destination,
8316         which gives a nice slow in/out effect. The algorithmn
8317         may seem to generate too many intermediates for short
8318         moves, but remember that the purpose is to attract the
8319         viewers attention to the piece about to be moved and
8320         then to where it ends up. Too few frames would be less
8321         noticeable.                                             */
8322
8323 static void
8324 Tween(start, mid, finish, factor, frames, nFrames)
8325      XPoint * start; XPoint * mid;
8326      XPoint * finish; int factor;
8327      XPoint frames[]; int * nFrames;
8328 {
8329   int fraction, n, count;
8330
8331   count = 0;
8332
8333   /* Slow in, stepping 1/16th, then 1/8th, ... */
8334   fraction = 1;
8335   for (n = 0; n < factor; n++)
8336     fraction *= 2;
8337   for (n = 0; n < factor; n++) {
8338     frames[count].x = start->x + (mid->x - start->x) / fraction;
8339     frames[count].y = start->y + (mid->y - start->y) / fraction;
8340     count ++;
8341     fraction = fraction / 2;
8342   }
8343
8344   /* Midpoint */
8345   frames[count] = *mid;
8346   count ++;
8347
8348   /* Slow out, stepping 1/2, then 1/4, ... */
8349   fraction = 2;
8350   for (n = 0; n < factor; n++) {
8351     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8352     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8353     count ++;
8354     fraction = fraction * 2;
8355   }
8356   *nFrames = count;
8357 }
8358
8359 /*      Draw a piece on the screen without disturbing what's there      */
8360
8361 static void
8362 SelectGCMask(piece, clip, outline, mask)
8363      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8364 {
8365   GC source;
8366
8367   /* Bitmap for piece being moved. */
8368   if (appData.monoMode) {
8369       *mask = *pieceToSolid(piece);
8370   } else if (useImages) {
8371 #if HAVE_LIBXPM
8372       *mask = xpmMask[piece];
8373 #else
8374       *mask = ximMaskPm[piece];
8375 #endif
8376   } else {
8377       *mask = *pieceToSolid(piece);
8378   }
8379
8380   /* GC for piece being moved. Square color doesn't matter, but
8381      since it gets modified we make a copy of the original. */
8382   if (White(piece)) {
8383     if (appData.monoMode)
8384       source = bwPieceGC;
8385     else
8386       source = wlPieceGC;
8387   } else {
8388     if (appData.monoMode)
8389       source = wbPieceGC;
8390     else
8391       source = blPieceGC;
8392   }
8393   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8394
8395   /* Outline only used in mono mode and is not modified */
8396   if (White(piece))
8397     *outline = bwPieceGC;
8398   else
8399     *outline = wbPieceGC;
8400 }
8401
8402 static void
8403 OverlayPiece(piece, clip, outline,  dest)
8404      ChessSquare piece; GC clip; GC outline; Drawable dest;
8405 {
8406   int   kind;
8407
8408   if (!useImages) {
8409     /* Draw solid rectangle which will be clipped to shape of piece */
8410     XFillRectangle(xDisplay, dest, clip,
8411                    0, 0, squareSize, squareSize);
8412     if (appData.monoMode)
8413       /* Also draw outline in contrasting color for black
8414          on black / white on white cases                */
8415       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8416                  0, 0, squareSize, squareSize, 0, 0, 1);
8417   } else {
8418     /* Copy the piece */
8419     if (White(piece))
8420       kind = 0;
8421     else
8422       kind = 2;
8423     if(appData.upsideDown && flipView) kind ^= 2;
8424     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8425               dest, clip,
8426               0, 0, squareSize, squareSize,
8427               0, 0);
8428   }
8429 }
8430
8431 /* Animate the movement of a single piece */
8432
8433 static void
8434 BeginAnimation(anim, piece, startColor, start)
8435      AnimState *anim;
8436      ChessSquare piece;
8437      int startColor;
8438      XPoint * start;
8439 {
8440   Pixmap mask;
8441
8442   if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8443   /* The old buffer is initialised with the start square (empty) */
8444   BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8445   anim->prevFrame = *start;
8446
8447   /* The piece will be drawn using its own bitmap as a matte    */
8448   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8449   XSetClipMask(xDisplay, anim->pieceGC, mask);
8450 }
8451
8452 static void
8453 AnimationFrame(anim, frame, piece)
8454      AnimState *anim;
8455      XPoint *frame;
8456      ChessSquare piece;
8457 {
8458   XRectangle updates[4];
8459   XRectangle overlap;
8460   XPoint     pt;
8461   int        count, i;
8462
8463   /* Save what we are about to draw into the new buffer */
8464   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8465             frame->x, frame->y, squareSize, squareSize,
8466             0, 0);
8467
8468   /* Erase bits of the previous frame */
8469   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8470     /* Where the new frame overlapped the previous,
8471        the contents in newBuf are wrong. */
8472     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8473               overlap.x, overlap.y,
8474               overlap.width, overlap.height,
8475               pt.x, pt.y);
8476     /* Repaint the areas in the old that don't overlap new */
8477     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8478     for (i = 0; i < count; i++)
8479       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8480                 updates[i].x - anim->prevFrame.x,
8481                 updates[i].y - anim->prevFrame.y,
8482                 updates[i].width, updates[i].height,
8483                 updates[i].x, updates[i].y);
8484   } else {
8485     /* Easy when no overlap */
8486     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8487                   0, 0, squareSize, squareSize,
8488                   anim->prevFrame.x, anim->prevFrame.y);
8489   }
8490
8491   /* Save this frame for next time round */
8492   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8493                 0, 0, squareSize, squareSize,
8494                 0, 0);
8495   anim->prevFrame = *frame;
8496
8497   /* Draw piece over original screen contents, not current,
8498      and copy entire rect. Wipes out overlapping piece images. */
8499   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8500   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8501                 0, 0, squareSize, squareSize,
8502                 frame->x, frame->y);
8503 }
8504
8505 static void
8506 EndAnimation (anim, finish)
8507      AnimState *anim;
8508      XPoint *finish;
8509 {
8510   XRectangle updates[4];
8511   XRectangle overlap;
8512   XPoint     pt;
8513   int        count, i;
8514
8515   /* The main code will redraw the final square, so we
8516      only need to erase the bits that don't overlap.    */
8517   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8518     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8519     for (i = 0; i < count; i++)
8520       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8521                 updates[i].x - anim->prevFrame.x,
8522                 updates[i].y - anim->prevFrame.y,
8523                 updates[i].width, updates[i].height,
8524                 updates[i].x, updates[i].y);
8525   } else {
8526     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8527                 0, 0, squareSize, squareSize,
8528                 anim->prevFrame.x, anim->prevFrame.y);
8529   }
8530 }
8531
8532 static void
8533 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8534      AnimState *anim;
8535      ChessSquare piece; int startColor;
8536      XPoint * start; XPoint * finish;
8537      XPoint frames[]; int nFrames;
8538 {
8539   int n;
8540
8541   BeginAnimation(anim, piece, startColor, start);
8542   for (n = 0; n < nFrames; n++) {
8543     AnimationFrame(anim, &(frames[n]), piece);
8544     FrameDelay(appData.animSpeed);
8545   }
8546   EndAnimation(anim, finish);
8547 }
8548
8549 void
8550 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8551 {
8552     int i, x, y;
8553     ChessSquare piece = board[fromY][toY];
8554     board[fromY][toY] = EmptySquare;
8555     DrawPosition(FALSE, board);
8556     if (flipView) {
8557         x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8558         y = lineGap + toY * (squareSize + lineGap);
8559     } else {
8560         x = lineGap + toX * (squareSize + lineGap);
8561         y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8562     }
8563     for(i=1; i<4*kFactor; i++) {
8564         int r = squareSize * 9 * i/(20*kFactor - 5);
8565         XFillArc(xDisplay, xBoardWindow, highlineGC,
8566                 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8567         FrameDelay(appData.animSpeed);
8568     }
8569     board[fromY][toY] = piece;
8570 }
8571
8572 /* Main control logic for deciding what to animate and how */
8573
8574 void
8575 AnimateMove(board, fromX, fromY, toX, toY)
8576      Board board;
8577      int fromX;
8578      int fromY;
8579      int toX;
8580      int toY;
8581 {
8582   ChessSquare piece;
8583   int hop;
8584   XPoint      start, finish, mid;
8585   XPoint      frames[kFactor * 2 + 1];
8586   int         nFrames, startColor, endColor;
8587
8588   /* Are we animating? */
8589   if (!appData.animate || appData.blindfold)
8590     return;
8591
8592   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8593      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8594         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8595
8596   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8597   piece = board[fromY][fromX];
8598   if (piece >= EmptySquare) return;
8599
8600 #if DONT_HOP
8601   hop = FALSE;
8602 #else
8603   hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8604 #endif
8605
8606   if (appData.debugMode) {
8607       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8608                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8609              piece, fromX, fromY, toX, toY);  }
8610
8611   ScreenSquare(fromX, fromY, &start, &startColor);
8612   ScreenSquare(toX, toY, &finish, &endColor);
8613
8614   if (hop) {
8615     /* Knight: make straight movement then diagonal */
8616     if (abs(toY - fromY) < abs(toX - fromX)) {
8617        mid.x = start.x + (finish.x - start.x) / 2;
8618        mid.y = start.y;
8619      } else {
8620        mid.x = start.x;
8621        mid.y = start.y + (finish.y - start.y) / 2;
8622      }
8623   } else {
8624     mid.x = start.x + (finish.x - start.x) / 2;
8625     mid.y = start.y + (finish.y - start.y) / 2;
8626   }
8627
8628   /* Don't use as many frames for very short moves */
8629   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8630     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8631   else
8632     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8633   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8634   if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8635     int i,j;
8636     for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8637       if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8638   }
8639
8640   /* Be sure end square is redrawn */
8641   damage[0][toY][toX] = True;
8642 }
8643
8644 void
8645 DragPieceBegin(x, y, instantly)
8646      int x; int y; Boolean instantly;
8647 {
8648     int  boardX, boardY, color;
8649     XPoint corner;
8650
8651     /* Are we animating? */
8652     if (!appData.animateDragging || appData.blindfold)
8653       return;
8654
8655     /* Figure out which square we start in and the
8656        mouse position relative to top left corner. */
8657     BoardSquare(x, y, &boardX, &boardY);
8658     player.startBoardX = boardX;
8659     player.startBoardY = boardY;
8660     ScreenSquare(boardX, boardY, &corner, &color);
8661     player.startSquare  = corner;
8662     player.startColor   = color;
8663     /* As soon as we start dragging, the piece will jump slightly to
8664        be centered over the mouse pointer. */
8665     player.mouseDelta.x = squareSize/2;
8666     player.mouseDelta.y = squareSize/2;
8667     /* Initialise animation */
8668     player.dragPiece = PieceForSquare(boardX, boardY);
8669     /* Sanity check */
8670     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8671         player.dragActive = True;
8672         BeginAnimation(&player, player.dragPiece, color, &corner);
8673         /* Mark this square as needing to be redrawn. Note that
8674            we don't remove the piece though, since logically (ie
8675            as seen by opponent) the move hasn't been made yet. */
8676            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8677               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8678            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8679                      corner.x, corner.y, squareSize, squareSize,
8680                      0, 0); // [HGM] zh: unstack in stead of grab
8681            if(gatingPiece != EmptySquare) {
8682                /* Kludge alert: When gating we want the introduced
8683                   piece to appear on the from square. To generate an
8684                   image of it, we draw it on the board, copy the image,
8685                   and draw the original piece again. */
8686                ChessSquare piece = boards[currentMove][boardY][boardX];
8687                DrawSquare(boardY, boardX, gatingPiece, 0);
8688                XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8689                      corner.x, corner.y, squareSize, squareSize, 0, 0);
8690                DrawSquare(boardY, boardX, piece, 0);
8691            }
8692         damage[0][boardY][boardX] = True;
8693     } else {
8694         player.dragActive = False;
8695     }
8696 }
8697
8698 void
8699 ChangeDragPiece(ChessSquare piece)
8700 {
8701   Pixmap mask;
8702   player.dragPiece = piece;
8703   /* The piece will be drawn using its own bitmap as a matte    */
8704   SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8705   XSetClipMask(xDisplay, player.pieceGC, mask);
8706 }
8707
8708 static void
8709 DragPieceMove(x, y)
8710      int x; int y;
8711 {
8712     XPoint corner;
8713
8714     /* Are we animating? */
8715     if (!appData.animateDragging || appData.blindfold)
8716       return;
8717
8718     /* Sanity check */
8719     if (! player.dragActive)
8720       return;
8721     /* Move piece, maintaining same relative position
8722        of mouse within square    */
8723     corner.x = x - player.mouseDelta.x;
8724     corner.y = y - player.mouseDelta.y;
8725     AnimationFrame(&player, &corner, player.dragPiece);
8726 #if HIGHDRAG*0
8727     if (appData.highlightDragging) {
8728         int boardX, boardY;
8729         BoardSquare(x, y, &boardX, &boardY);
8730         SetHighlights(fromX, fromY, boardX, boardY);
8731     }
8732 #endif
8733 }
8734
8735 void
8736 DragPieceEnd(x, y)
8737      int x; int y;
8738 {
8739     int boardX, boardY, color;
8740     XPoint corner;
8741
8742     /* Are we animating? */
8743     if (!appData.animateDragging || appData.blindfold)
8744       return;
8745
8746     /* Sanity check */
8747     if (! player.dragActive)
8748       return;
8749     /* Last frame in sequence is square piece is
8750        placed on, which may not match mouse exactly. */
8751     BoardSquare(x, y, &boardX, &boardY);
8752     ScreenSquare(boardX, boardY, &corner, &color);
8753     EndAnimation(&player, &corner);
8754
8755     /* Be sure end square is redrawn */
8756     damage[0][boardY][boardX] = True;
8757
8758     /* This prevents weird things happening with fast successive
8759        clicks which on my Sun at least can cause motion events
8760        without corresponding press/release. */
8761     player.dragActive = False;
8762 }
8763
8764 /* Handle expose event while piece being dragged */
8765
8766 static void
8767 DrawDragPiece ()
8768 {
8769   if (!player.dragActive || appData.blindfold)
8770     return;
8771
8772   /* What we're doing: logically, the move hasn't been made yet,
8773      so the piece is still in it's original square. But visually
8774      it's being dragged around the board. So we erase the square
8775      that the piece is on and draw it at the last known drag point. */
8776   BlankSquare(player.startSquare.x, player.startSquare.y,
8777                 player.startColor, EmptySquare, xBoardWindow, 1);
8778   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8779   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8780 }
8781
8782 #include <sys/ioctl.h>
8783 int get_term_width()
8784 {
8785     int fd, default_width;
8786
8787     fd = STDIN_FILENO;
8788     default_width = 79; // this is FICS default anyway...
8789
8790 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8791     struct ttysize win;
8792     if (!ioctl(fd, TIOCGSIZE, &win))
8793         default_width = win.ts_cols;
8794 #elif defined(TIOCGWINSZ)
8795     struct winsize win;
8796     if (!ioctl(fd, TIOCGWINSZ, &win))
8797         default_width = win.ws_col;
8798 #endif
8799     return default_width;
8800 }
8801
8802 void
8803 update_ics_width()
8804 {
8805   static int old_width = 0;
8806   int new_width = get_term_width();
8807
8808   if (old_width != new_width)
8809     ics_printf("set width %d\n", new_width);
8810   old_width = new_width;
8811 }
8812
8813 void NotifyFrontendLogin()
8814 {
8815     update_ics_width();
8816 }
8817
8818 /* [AS] Arrow highlighting support */
8819
8820 static double A_WIDTH = 5; /* Width of arrow body */
8821
8822 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
8823 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
8824
8825 static double Sqr( double x )
8826 {
8827     return x*x;
8828 }
8829
8830 static int Round( double x )
8831 {
8832     return (int) (x + 0.5);
8833 }
8834
8835 void SquareToPos(int rank, int file, int *x, int *y)
8836 {
8837     if (flipView) {
8838         *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8839         *y = lineGap + rank * (squareSize + lineGap);
8840     } else {
8841         *x = lineGap + file * (squareSize + lineGap);
8842         *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8843     }
8844 }
8845
8846 /* Draw an arrow between two points using current settings */
8847 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8848 {
8849     XPoint arrow[7];
8850     double dx, dy, j, k, x, y;
8851
8852     if( d_x == s_x ) {
8853         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8854
8855         arrow[0].x = s_x + A_WIDTH + 0.5;
8856         arrow[0].y = s_y;
8857
8858         arrow[1].x = s_x + A_WIDTH + 0.5;
8859         arrow[1].y = d_y - h;
8860
8861         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8862         arrow[2].y = d_y - h;
8863
8864         arrow[3].x = d_x;
8865         arrow[3].y = d_y;
8866
8867         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8868         arrow[5].y = d_y - h;
8869
8870         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8871         arrow[4].y = d_y - h;
8872
8873         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8874         arrow[6].y = s_y;
8875     }
8876     else if( d_y == s_y ) {
8877         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8878
8879         arrow[0].x = s_x;
8880         arrow[0].y = s_y + A_WIDTH + 0.5;
8881
8882         arrow[1].x = d_x - w;
8883         arrow[1].y = s_y + A_WIDTH + 0.5;
8884
8885         arrow[2].x = d_x - w;
8886         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8887
8888         arrow[3].x = d_x;
8889         arrow[3].y = d_y;
8890
8891         arrow[5].x = d_x - w;
8892         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8893
8894         arrow[4].x = d_x - w;
8895         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8896
8897         arrow[6].x = s_x;
8898         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8899     }
8900     else {
8901         /* [AS] Needed a lot of paper for this! :-) */
8902         dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8903         dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8904
8905         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8906
8907         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8908
8909         x = s_x;
8910         y = s_y;
8911
8912         arrow[0].x = Round(x - j);
8913         arrow[0].y = Round(y + j*dx);
8914
8915         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
8916         arrow[1].y = Round(arrow[0].y - 2*j*dx);
8917
8918         if( d_x > s_x ) {
8919             x = (double) d_x - k;
8920             y = (double) d_y - k*dy;
8921         }
8922         else {
8923             x = (double) d_x + k;
8924             y = (double) d_y + k*dy;
8925         }
8926
8927         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8928
8929         arrow[6].x = Round(x - j);
8930         arrow[6].y = Round(y + j*dx);
8931
8932         arrow[2].x = Round(arrow[6].x + 2*j);
8933         arrow[2].y = Round(arrow[6].y - 2*j*dx);
8934
8935         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8936         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8937
8938         arrow[4].x = d_x;
8939         arrow[4].y = d_y;
8940
8941         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8942         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8943     }
8944
8945     XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8946 //    Polygon( hdc, arrow, 7 );
8947 }
8948
8949 /* [AS] Draw an arrow between two squares */
8950 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8951 {
8952     int s_x, s_y, d_x, d_y, hor, vert, i;
8953
8954     if( s_col == d_col && s_row == d_row ) {
8955         return;
8956     }
8957
8958     /* Get source and destination points */
8959     SquareToPos( s_row, s_col, &s_x, &s_y);
8960     SquareToPos( d_row, d_col, &d_x, &d_y);
8961
8962     if( d_y > s_y ) {
8963         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8964     }
8965     else if( d_y < s_y ) {
8966         d_y += squareSize / 2 + squareSize / 4;
8967     }
8968     else {
8969         d_y += squareSize / 2;
8970     }
8971
8972     if( d_x > s_x ) {
8973         d_x += squareSize / 2 - squareSize / 4;
8974     }
8975     else if( d_x < s_x ) {
8976         d_x += squareSize / 2 + squareSize / 4;
8977     }
8978     else {
8979         d_x += squareSize / 2;
8980     }
8981
8982     s_x += squareSize / 2;
8983     s_y += squareSize / 2;
8984
8985     /* Adjust width */
8986     A_WIDTH = squareSize / 14.; //[HGM] make float
8987
8988     DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8989
8990     hor = 64*s_col + 32; vert = 64*s_row + 32;
8991     for(i=0; i<= 64; i++) {
8992             damage[0][vert+6>>6][hor+6>>6] = True;
8993             damage[0][vert-6>>6][hor+6>>6] = True;
8994             damage[0][vert+6>>6][hor-6>>6] = True;
8995             damage[0][vert-6>>6][hor-6>>6] = True;
8996             hor += d_col - s_col; vert += d_row - s_row;
8997     }
8998 }
8999
9000 Boolean IsDrawArrowEnabled()
9001 {
9002     return appData.highlightMoveWithArrow && squareSize >= 32;
9003 }
9004
9005 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9006 {
9007     if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9008         DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9009 }
9010
9011 void UpdateLogos(int displ)
9012 {
9013     return; // no logos in XBoard yet
9014 }
9015