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