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