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