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