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