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