Bring structure in appData engine options
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #define HIGHDRAG 1
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <pwd.h>
63 #include <math.h>
64
65 #if !OMIT_SOCKETS
66 # if HAVE_SYS_SOCKET_H
67 #  include <sys/socket.h>
68 #  include <netinet/in.h>
69 #  include <netdb.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 #  if HAVE_LAN_SOCKET_H
72 #   include <lan/socket.h>
73 #   include <lan/in.h>
74 #   include <lan/netdb.h>
75 #  else /* not HAVE_LAN_SOCKET_H */
76 #   define OMIT_SOCKETS 1
77 #  endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
80
81 #if STDC_HEADERS
82 # include <stdlib.h>
83 # include <string.h>
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
86 # if HAVE_STRING_H
87 #  include <string.h>
88 # else /* not HAVE_STRING_H */
89 #  include <strings.h>
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
92
93 #if HAVE_SYS_FCNTL_H
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
96 # if HAVE_FCNTL_H
97 #  include <fcntl.h>
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
100
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
104
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
107 # include <time.h>
108 #else
109 # if HAVE_SYS_TIME_H
110 #  include <sys/time.h>
111 # else
112 #  include <time.h>
113 # endif
114 #endif
115
116 #if HAVE_UNISTD_H
117 # include <unistd.h>
118 #endif
119
120 #if HAVE_SYS_WAIT_H
121 # include <sys/wait.h>
122 #endif
123
124 #if HAVE_DIRENT_H
125 # include <dirent.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
128 #else
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
131 # if HAVE_SYS_NDIR_H
132 #  include <sys/ndir.h>
133 #  define HAVE_DIR_STRUCT
134 # endif
135 # if HAVE_SYS_DIR_H
136 #  include <sys/dir.h>
137 #  define HAVE_DIR_STRUCT
138 # endif
139 # if HAVE_NDIR_H
140 #  include <ndir.h>
141 #  define HAVE_DIR_STRUCT
142 # endif
143 #endif
144
145 #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.firstInitString);
1983         appData.firstInitString = 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 #endif
2794     { "menuEngine.Engine #1 Settings", False },
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      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
2941   }
2942 #endif
2943 }
2944
2945 void
2946 SetNCPMode()
2947 {
2948   SetMenuEnables(ncpEnables);
2949 }
2950
2951 void
2952 SetGNUMode()
2953 {
2954   SetMenuEnables(gnuEnables);
2955 }
2956
2957 void
2958 SetCmailMode()
2959 {
2960   SetMenuEnables(cmailEnables);
2961 }
2962
2963 void
2964 SetTrainingModeOn()
2965 {
2966   SetMenuEnables(trainingOnEnables);
2967   if (appData.showButtonBar) {
2968     XtSetSensitive(buttonBarWidget, False);
2969   }
2970   CommentPopDown();
2971 }
2972
2973 void
2974 SetTrainingModeOff()
2975 {
2976   SetMenuEnables(trainingOffEnables);
2977   if (appData.showButtonBar) {
2978     XtSetSensitive(buttonBarWidget, True);
2979   }
2980 }
2981
2982 void
2983 SetUserThinkingEnables()
2984 {
2985   if (appData.noChessProgram) return;
2986   SetMenuEnables(userThinkingEnables);
2987 }
2988
2989 void
2990 SetMachineThinkingEnables()
2991 {
2992   if (appData.noChessProgram) return;
2993   SetMenuEnables(machineThinkingEnables);
2994   switch (gameMode) {
2995   case MachinePlaysBlack:
2996   case MachinePlaysWhite:
2997   case TwoMachinesPlay:
2998     XtSetSensitive(XtNameToWidget(menuBarWidget,
2999                                   ModeToWidgetName(gameMode)), True);
3000     break;
3001   default:
3002     break;
3003   }
3004 }
3005
3006 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
3007 #define HISTORY_SIZE 64
3008 static char *history[HISTORY_SIZE];
3009 int histIn = 0, histP = 0;
3010
3011 void
3012 SaveInHistory(char *cmd)
3013 {
3014   if (history[histIn] != NULL) {
3015     free(history[histIn]);
3016     history[histIn] = NULL;
3017   }
3018   if (*cmd == NULLCHAR) return;
3019   history[histIn] = StrSave(cmd);
3020   histIn = (histIn + 1) % HISTORY_SIZE;
3021   if (history[histIn] != NULL) {
3022     free(history[histIn]);
3023     history[histIn] = NULL;
3024   }
3025   histP = histIn;
3026 }
3027
3028 char *
3029 PrevInHistory(char *cmd)
3030 {
3031   int newhp;
3032   if (histP == histIn) {
3033     if (history[histIn] != NULL) free(history[histIn]);
3034     history[histIn] = StrSave(cmd);
3035   }
3036   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
3037   if (newhp == histIn || history[newhp] == NULL) return NULL;
3038   histP = newhp;
3039   return history[histP];
3040 }
3041
3042 char *
3043 NextInHistory()
3044 {
3045   if (histP == histIn) return NULL;
3046   histP = (histP + 1) % HISTORY_SIZE;
3047   return history[histP];   
3048 }
3049 // end of borrowed code
3050
3051 #define Abs(n) ((n)<0 ? -(n) : (n))
3052
3053 /*
3054  * Find a font that matches "pattern" that is as close as
3055  * possible to the targetPxlSize.  Prefer fonts that are k
3056  * pixels smaller to fonts that are k pixels larger.  The
3057  * pattern must be in the X Consortium standard format,
3058  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3059  * The return value should be freed with XtFree when no
3060  * longer needed.
3061  */
3062 char *
3063 FindFont(pattern, targetPxlSize)
3064      char *pattern;
3065      int targetPxlSize;
3066 {
3067     char **fonts, *p, *best, *scalable, *scalableTail;
3068     int i, j, nfonts, minerr, err, pxlSize;
3069
3070 #ifdef ENABLE_NLS
3071     char **missing_list;
3072     int missing_count;
3073     char *def_string, *base_fnt_lst, strInt[3];
3074     XFontSet fntSet;
3075     XFontStruct **fnt_list;
3076     base_fnt_lst = calloc(1, strlen(pattern) + 3);
3077     snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3078     p = strstr(pattern, "--");
3079     strncpy(base_fnt_lst, pattern, p - pattern + 2);
3080     strcat(base_fnt_lst, strInt);
3081     strcat(base_fnt_lst, strchr(p + 2, '-'));
3082
3083     if ((fntSet = XCreateFontSet(xDisplay,
3084                                  base_fnt_lst,
3085                                  &missing_list,
3086                                  &missing_count,
3087                                  &def_string)) == NULL) {
3088
3089        fprintf(stderr, _("Unable to create font set.\n"));
3090        exit (2);
3091     }
3092
3093     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3094 #else
3095     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3096     if (nfonts < 1) {
3097         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3098                 programName, pattern);
3099         exit(2);
3100     }
3101 #endif
3102
3103     best = fonts[0];
3104     scalable = NULL;
3105     minerr = 999999;
3106     for (i=0; i<nfonts; i++) {
3107         j = 0;
3108         p = fonts[i];
3109         if (*p != '-') continue;
3110         while (j < 7) {
3111             if (*p == NULLCHAR) break;
3112             if (*p++ == '-') j++;
3113         }
3114         if (j < 7) continue;
3115         pxlSize = atoi(p);
3116         if (pxlSize == 0) {
3117             scalable = fonts[i];
3118             scalableTail = p;
3119         } else {
3120             err = pxlSize - targetPxlSize;
3121             if (Abs(err) < Abs(minerr) ||
3122                 (minerr > 0 && err < 0 && -err == minerr)) {
3123                 best = fonts[i];
3124                 minerr = err;
3125             }
3126         }
3127     }
3128     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3129         /* If the error is too big and there is a scalable font,
3130            use the scalable font. */
3131         int headlen = scalableTail - scalable;
3132         p = (char *) XtMalloc(strlen(scalable) + 10);
3133         while (isdigit(*scalableTail)) scalableTail++;
3134         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3135     } else {
3136         p = (char *) XtMalloc(strlen(best) + 2);
3137         safeStrCpy(p, best, strlen(best)+1 );
3138     }
3139     if (appData.debugMode) {
3140         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
3141                 pattern, targetPxlSize, p);
3142     }
3143 #ifdef ENABLE_NLS
3144     if (missing_count > 0)
3145        XFreeStringList(missing_list);
3146     XFreeFontSet(xDisplay, fntSet);
3147 #else
3148      XFreeFontNames(fonts);
3149 #endif
3150     return p;
3151 }
3152
3153 void DeleteGCs()
3154 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
3155     // must be called before all non-first callse to CreateGCs()
3156     XtReleaseGC(shellWidget, highlineGC);
3157     XtReleaseGC(shellWidget, lightSquareGC);
3158     XtReleaseGC(shellWidget, darkSquareGC);
3159     XtReleaseGC(shellWidget, lineGC);
3160     if (appData.monoMode) {
3161         if (DefaultDepth(xDisplay, xScreen) == 1) {
3162             XtReleaseGC(shellWidget, wbPieceGC);
3163         } else {
3164             XtReleaseGC(shellWidget, bwPieceGC);
3165         }
3166     } else {
3167         XtReleaseGC(shellWidget, prelineGC);
3168         XtReleaseGC(shellWidget, jailSquareGC);
3169         XtReleaseGC(shellWidget, wdPieceGC);
3170         XtReleaseGC(shellWidget, wlPieceGC);
3171         XtReleaseGC(shellWidget, wjPieceGC);
3172         XtReleaseGC(shellWidget, bdPieceGC);
3173         XtReleaseGC(shellWidget, blPieceGC);
3174         XtReleaseGC(shellWidget, bjPieceGC);
3175     }
3176 }
3177
3178 void CreateGCs(int redo)
3179 {
3180     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3181       | GCBackground | GCFunction | GCPlaneMask;
3182     XGCValues gc_values;
3183     GC copyInvertedGC;
3184
3185     gc_values.plane_mask = AllPlanes;
3186     gc_values.line_width = lineGap;
3187     gc_values.line_style = LineSolid;
3188     gc_values.function = GXcopy;
3189
3190   if(redo) {
3191     DeleteGCs(); // called a second time; clean up old GCs first
3192   } else { // [HGM] grid and font GCs created on first call only
3193     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3194     gc_values.background = XWhitePixel(xDisplay, xScreen);
3195     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3196     XSetFont(xDisplay, coordGC, coordFontID);
3197
3198     // [HGM] make font for holdings counts (white on black)
3199     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3200     gc_values.background = XBlackPixel(xDisplay, xScreen);
3201     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3202     XSetFont(xDisplay, countGC, countFontID);
3203   }
3204     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3205     gc_values.background = XBlackPixel(xDisplay, xScreen);
3206     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3207
3208     if (appData.monoMode) {
3209         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3210         gc_values.background = XWhitePixel(xDisplay, xScreen);
3211         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3212
3213         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3214         gc_values.background = XBlackPixel(xDisplay, xScreen);
3215         lightSquareGC = wbPieceGC
3216           = XtGetGC(shellWidget, value_mask, &gc_values);
3217
3218         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3219         gc_values.background = XWhitePixel(xDisplay, xScreen);
3220         darkSquareGC = bwPieceGC
3221           = XtGetGC(shellWidget, value_mask, &gc_values);
3222
3223         if (DefaultDepth(xDisplay, xScreen) == 1) {
3224             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3225             gc_values.function = GXcopyInverted;
3226             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3227             gc_values.function = GXcopy;
3228             if (XBlackPixel(xDisplay, xScreen) == 1) {
3229                 bwPieceGC = darkSquareGC;
3230                 wbPieceGC = copyInvertedGC;
3231             } else {
3232                 bwPieceGC = copyInvertedGC;
3233                 wbPieceGC = lightSquareGC;
3234             }
3235         }
3236     } else {
3237         gc_values.foreground = highlightSquareColor;
3238         gc_values.background = highlightSquareColor;
3239         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3240
3241         gc_values.foreground = premoveHighlightColor;
3242         gc_values.background = premoveHighlightColor;
3243         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3244
3245         gc_values.foreground = lightSquareColor;
3246         gc_values.background = darkSquareColor;
3247         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3248
3249         gc_values.foreground = darkSquareColor;
3250         gc_values.background = lightSquareColor;
3251         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3252
3253         gc_values.foreground = jailSquareColor;
3254         gc_values.background = jailSquareColor;
3255         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3256
3257         gc_values.foreground = whitePieceColor;
3258         gc_values.background = darkSquareColor;
3259         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3260
3261         gc_values.foreground = whitePieceColor;
3262         gc_values.background = lightSquareColor;
3263         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3264
3265         gc_values.foreground = whitePieceColor;
3266         gc_values.background = jailSquareColor;
3267         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3268
3269         gc_values.foreground = blackPieceColor;
3270         gc_values.background = darkSquareColor;
3271         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3272
3273         gc_values.foreground = blackPieceColor;
3274         gc_values.background = lightSquareColor;
3275         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3276
3277         gc_values.foreground = blackPieceColor;
3278         gc_values.background = jailSquareColor;
3279         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3280     }
3281 }
3282
3283 void loadXIM(xim, xmask, filename, dest, mask)
3284      XImage *xim;
3285      XImage *xmask;
3286      char *filename;
3287      Pixmap *dest;
3288      Pixmap *mask;
3289 {
3290     int x, y, w, h, p;
3291     FILE *fp;
3292     Pixmap temp;
3293     XGCValues   values;
3294     GC maskGC;
3295
3296     fp = fopen(filename, "rb");
3297     if (!fp) {
3298         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3299         exit(1);
3300     }
3301
3302     w = fgetc(fp);
3303     h = fgetc(fp);
3304
3305     for (y=0; y<h; ++y) {
3306         for (x=0; x<h; ++x) {
3307             p = fgetc(fp);
3308
3309             switch (p) {
3310               case 0:
3311                 XPutPixel(xim, x, y, blackPieceColor);
3312                 if (xmask)
3313                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3314                 break;
3315               case 1:
3316                 XPutPixel(xim, x, y, darkSquareColor);
3317                 if (xmask)
3318                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3319                 break;
3320               case 2:
3321                 XPutPixel(xim, x, y, whitePieceColor);
3322                 if (xmask)
3323                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3324                 break;
3325               case 3:
3326                 XPutPixel(xim, x, y, lightSquareColor);
3327                 if (xmask)
3328                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3329                 break;
3330             }
3331         }
3332     }
3333
3334     fclose(fp);
3335
3336     /* create Pixmap of piece */
3337     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3338                           w, h, xim->depth);
3339     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3340               0, 0, 0, 0, w, h);
3341
3342     /* create Pixmap of clipmask
3343        Note: We assume the white/black pieces have the same
3344              outline, so we make only 6 masks. This is okay
3345              since the XPM clipmask routines do the same. */
3346     if (xmask) {
3347       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3348                             w, h, xim->depth);
3349       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3350               0, 0, 0, 0, w, h);
3351
3352       /* now create the 1-bit version */
3353       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3354                           w, h, 1);
3355
3356       values.foreground = 1;
3357       values.background = 0;
3358
3359       /* Don't use XtGetGC, not read only */
3360       maskGC = XCreateGC(xDisplay, *mask,
3361                     GCForeground | GCBackground, &values);
3362       XCopyPlane(xDisplay, temp, *mask, maskGC,
3363                   0, 0, squareSize, squareSize, 0, 0, 1);
3364       XFreePixmap(xDisplay, temp);
3365     }
3366 }
3367
3368
3369 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3370
3371 void CreateXIMPieces()
3372 {
3373     int piece, kind;
3374     char buf[MSG_SIZ];
3375     u_int ss;
3376     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3377     XImage *ximtemp;
3378
3379     ss = squareSize;
3380
3381     /* The XSynchronize calls were copied from CreatePieces.
3382        Not sure if needed, but can't hurt */
3383     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3384                                      buffering bug */
3385
3386     /* temp needed by loadXIM() */
3387     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3388                  0, 0, ss, ss, AllPlanes, XYPixmap);
3389
3390     if (strlen(appData.pixmapDirectory) == 0) {
3391       useImages = 0;
3392     } else {
3393         useImages = 1;
3394         if (appData.monoMode) {
3395           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3396                             0, 2);
3397           ExitEvent(2);
3398         }
3399         fprintf(stderr, _("\nLoading XIMs...\n"));
3400         /* Load pieces */
3401         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3402             fprintf(stderr, "%d", piece+1);
3403             for (kind=0; kind<4; kind++) {
3404                 fprintf(stderr, ".");
3405                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3406                         ExpandPathName(appData.pixmapDirectory),
3407                         piece <= (int) WhiteKing ? "" : "w",
3408                         pieceBitmapNames[piece],
3409                         ximkind[kind], ss);
3410                 ximPieceBitmap[kind][piece] =
3411                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3412                             0, 0, ss, ss, AllPlanes, XYPixmap);
3413                 if (appData.debugMode)
3414                   fprintf(stderr, _("(File:%s:) "), buf);
3415                 loadXIM(ximPieceBitmap[kind][piece],
3416                         ximtemp, buf,
3417                         &(xpmPieceBitmap2[kind][piece]),
3418                         &(ximMaskPm2[piece]));
3419                 if(piece <= (int)WhiteKing)
3420                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3421             }
3422             fprintf(stderr," ");
3423         }
3424         /* Load light and dark squares */
3425         /* If the LSQ and DSQ pieces don't exist, we will
3426            draw them with solid squares. */
3427         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3428         if (access(buf, 0) != 0) {
3429             useImageSqs = 0;
3430         } else {
3431             useImageSqs = 1;
3432             fprintf(stderr, _("light square "));
3433             ximLightSquare=
3434               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3435                         0, 0, ss, ss, AllPlanes, XYPixmap);
3436             if (appData.debugMode)
3437               fprintf(stderr, _("(File:%s:) "), buf);
3438
3439             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3440             fprintf(stderr, _("dark square "));
3441             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3442                     ExpandPathName(appData.pixmapDirectory), ss);
3443             if (appData.debugMode)
3444               fprintf(stderr, _("(File:%s:) "), buf);
3445             ximDarkSquare=
3446               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3447                         0, 0, ss, ss, AllPlanes, XYPixmap);
3448             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3449             xpmJailSquare = xpmLightSquare;
3450         }
3451         fprintf(stderr, _("Done.\n"));
3452     }
3453     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3454 }
3455
3456 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
3457
3458 #if HAVE_LIBXPM
3459 void CreateXPMBoard(char *s, int kind)
3460 {
3461     XpmAttributes attr;
3462     attr.valuemask = 0;
3463     if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3464     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3465         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3466     }
3467 }
3468
3469 void FreeXPMPieces()
3470 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3471     // thisroutine has to be called t free the old piece pixmaps
3472     int piece, kind;
3473     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3474         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3475     if(useImageSqs) {
3476         XFreePixmap(xDisplay, xpmLightSquare);
3477         XFreePixmap(xDisplay, xpmDarkSquare);
3478     }
3479 }
3480
3481 void CreateXPMPieces()
3482 {
3483     int piece, kind, r;
3484     char buf[MSG_SIZ];
3485     u_int ss = squareSize;
3486     XpmAttributes attr;
3487     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3488     XpmColorSymbol symbols[4];
3489     static int redo = False;
3490
3491     if(redo) FreeXPMPieces(); else redo = 1;
3492
3493     /* The XSynchronize calls were copied from CreatePieces.
3494        Not sure if needed, but can't hurt */
3495     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3496
3497     /* Setup translations so piece colors match square colors */
3498     symbols[0].name = "light_piece";
3499     symbols[0].value = appData.whitePieceColor;
3500     symbols[1].name = "dark_piece";
3501     symbols[1].value = appData.blackPieceColor;
3502     symbols[2].name = "light_square";
3503     symbols[2].value = appData.lightSquareColor;
3504     symbols[3].name = "dark_square";
3505     symbols[3].value = appData.darkSquareColor;
3506
3507     attr.valuemask = XpmColorSymbols;
3508     attr.colorsymbols = symbols;
3509     attr.numsymbols = 4;
3510
3511     if (appData.monoMode) {
3512       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3513                         0, 2);
3514       ExitEvent(2);
3515     }
3516     if (strlen(appData.pixmapDirectory) == 0) {
3517         XpmPieces* pieces = builtInXpms;
3518         useImages = 1;
3519         /* Load pieces */
3520         while (pieces->size != squareSize && pieces->size) pieces++;
3521         if (!pieces->size) {
3522           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3523           exit(1);
3524         }
3525         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3526             for (kind=0; kind<4; kind++) {
3527
3528                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3529                                                pieces->xpm[piece][kind],
3530                                                &(xpmPieceBitmap2[kind][piece]),
3531                                                NULL, &attr)) != 0) {
3532                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3533                           r, buf);
3534                   exit(1);
3535                 }
3536                 if(piece <= (int) WhiteKing)
3537                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3538             }
3539         }
3540         useImageSqs = 0;
3541         xpmJailSquare = xpmLightSquare;
3542     } else {
3543         useImages = 1;
3544
3545         fprintf(stderr, _("\nLoading XPMs...\n"));
3546
3547         /* Load pieces */
3548         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3549             fprintf(stderr, "%d ", piece+1);
3550             for (kind=0; kind<4; kind++) {
3551               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3552                         ExpandPathName(appData.pixmapDirectory),
3553                         piece > (int) WhiteKing ? "w" : "",
3554                         pieceBitmapNames[piece],
3555                         xpmkind[kind], ss);
3556                 if (appData.debugMode) {
3557                     fprintf(stderr, _("(File:%s:) "), buf);
3558                 }
3559                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3560                                            &(xpmPieceBitmap2[kind][piece]),
3561                                            NULL, &attr)) != 0) {
3562                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3563                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3564                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3565                                 ExpandPathName(appData.pixmapDirectory),
3566                                 xpmkind[kind], ss);
3567                         if (appData.debugMode) {
3568                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3569                         }
3570                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3571                                                 &(xpmPieceBitmap2[kind][piece]),
3572                                                 NULL, &attr);
3573                     }
3574                     if (r != 0) {
3575                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3576                                 r, buf);
3577                         exit(1);
3578                     }
3579                 }
3580                 if(piece <= (int) WhiteKing)
3581                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3582             }
3583         }
3584         /* Load light and dark squares */
3585         /* If the LSQ and DSQ pieces don't exist, we will
3586            draw them with solid squares. */
3587         fprintf(stderr, _("light square "));
3588         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3589         if (access(buf, 0) != 0) {
3590             useImageSqs = 0;
3591         } else {
3592             useImageSqs = 1;
3593             if (appData.debugMode)
3594               fprintf(stderr, _("(File:%s:) "), buf);
3595
3596             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3597                                        &xpmLightSquare, NULL, &attr)) != 0) {
3598                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3599                 exit(1);
3600             }
3601             fprintf(stderr, _("dark square "));
3602             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3603                     ExpandPathName(appData.pixmapDirectory), ss);
3604             if (appData.debugMode) {
3605                 fprintf(stderr, _("(File:%s:) "), buf);
3606             }
3607             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3608                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3609                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3610                 exit(1);
3611             }
3612         }
3613         xpmJailSquare = xpmLightSquare;
3614         fprintf(stderr, _("Done.\n"));
3615     }
3616     oldVariant = -1; // kludge to force re-makig of animation masks
3617     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3618                                       buffering bug */
3619 }
3620 #endif /* HAVE_LIBXPM */
3621
3622 #if HAVE_LIBXPM
3623 /* No built-in bitmaps */
3624 void CreatePieces()
3625 {
3626     int piece, kind;
3627     char buf[MSG_SIZ];
3628     u_int ss = squareSize;
3629
3630     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3631                                      buffering bug */
3632
3633     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3634         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3635           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3636                    pieceBitmapNames[piece],
3637                    ss, kind == SOLID ? 's' : 'o');
3638           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3639           if(piece <= (int)WhiteKing)
3640             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3641         }
3642     }
3643
3644     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3645                                       buffering bug */
3646 }
3647 #else
3648 /* With built-in bitmaps */
3649 void CreatePieces()
3650 {
3651     BuiltInBits* bib = builtInBits;
3652     int piece, kind;
3653     char buf[MSG_SIZ];
3654     u_int ss = squareSize;
3655
3656     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3657                                      buffering bug */
3658
3659     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3660
3661     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3662         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3663           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3664                    pieceBitmapNames[piece],
3665                    ss, kind == SOLID ? 's' : 'o');
3666           ReadBitmap(&pieceBitmap2[kind][piece], buf,
3667                      bib->bits[kind][piece], ss, ss);
3668           if(piece <= (int)WhiteKing)
3669             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3670         }
3671     }
3672
3673     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3674                                       buffering bug */
3675 }
3676 #endif
3677
3678 void ReadBitmap(pm, name, bits, wreq, hreq)
3679      Pixmap *pm;
3680      String name;
3681      unsigned char bits[];
3682      u_int wreq, hreq;
3683 {
3684     int x_hot, y_hot;
3685     u_int w, h;
3686     int errcode;
3687     char msg[MSG_SIZ], fullname[MSG_SIZ];
3688
3689     if (*appData.bitmapDirectory != NULLCHAR) {
3690       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3691       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3692       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3693       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3694                                 &w, &h, pm, &x_hot, &y_hot);
3695       fprintf(stderr, "load %s\n", name);
3696         if (errcode != BitmapSuccess) {
3697             switch (errcode) {
3698               case BitmapOpenFailed:
3699                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3700                 break;
3701               case BitmapFileInvalid:
3702                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3703                 break;
3704               case BitmapNoMemory:
3705                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3706                         fullname);
3707                 break;
3708               default:
3709                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3710                         errcode, fullname);
3711                 break;
3712             }
3713             fprintf(stderr, _("%s: %s...using built-in\n"),
3714                     programName, msg);
3715         } else if (w != wreq || h != hreq) {
3716             fprintf(stderr,
3717                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3718                     programName, fullname, w, h, wreq, hreq);
3719         } else {
3720             return;
3721         }
3722     }
3723     if (bits != NULL) {
3724         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3725                                     wreq, hreq);
3726     }
3727 }
3728
3729 void CreateGrid()
3730 {
3731     int i, j;
3732
3733     if (lineGap == 0) return;
3734
3735     /* [HR] Split this into 2 loops for non-square boards. */
3736
3737     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3738         gridSegments[i].x1 = 0;
3739         gridSegments[i].x2 =
3740           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3741         gridSegments[i].y1 = gridSegments[i].y2
3742           = lineGap / 2 + (i * (squareSize + lineGap));
3743     }
3744
3745     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3746         gridSegments[j + i].y1 = 0;
3747         gridSegments[j + i].y2 =
3748           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3749         gridSegments[j + i].x1 = gridSegments[j + i].x2
3750           = lineGap / 2 + (j * (squareSize + lineGap));
3751     }
3752 }
3753
3754 static void MenuBarSelect(w, addr, index)
3755      Widget w;
3756      caddr_t addr;
3757      caddr_t index;
3758 {
3759     XtActionProc proc = (XtActionProc) addr;
3760
3761     (proc)(NULL, NULL, NULL, NULL);
3762 }
3763
3764 void CreateMenuBarPopup(parent, name, mb)
3765      Widget parent;
3766      String name;
3767      Menu *mb;
3768 {
3769     int j;
3770     Widget menu, entry;
3771     MenuItem *mi;
3772     Arg args[16];
3773
3774     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3775                               parent, NULL, 0);
3776     j = 0;
3777     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3778     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3779     mi = mb->mi;
3780     while (mi->string != NULL) {
3781         if (strcmp(mi->string, "----") == 0) {
3782             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3783                                           menu, args, j);
3784         } else {
3785           XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3786             entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3787                                           menu, args, j+1);
3788             XtAddCallback(entry, XtNcallback,
3789                           (XtCallbackProc) MenuBarSelect,
3790                           (caddr_t) mi->proc);
3791         }
3792         mi++;
3793     }
3794 }
3795
3796 Widget CreateMenuBar(mb)
3797      Menu *mb;
3798 {
3799     int j;
3800     Widget anchor, menuBar;
3801     Arg args[16];
3802     char menuName[MSG_SIZ];
3803
3804     j = 0;
3805     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3806     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3807     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3808     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3809                              formWidget, args, j);
3810
3811     while (mb->name != NULL) {
3812         safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3813         strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3814         j = 0;
3815         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3816         if (tinyLayout) {
3817             char shortName[2];
3818             shortName[0] = mb->name[0];
3819             shortName[1] = NULLCHAR;
3820             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3821         }
3822       else {
3823           XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3824       }
3825
3826         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3827         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3828                                        menuBar, args, j);
3829         CreateMenuBarPopup(menuBar, menuName, mb);
3830         mb++;
3831     }
3832     return menuBar;
3833 }
3834
3835 Widget CreateButtonBar(mi)
3836      MenuItem *mi;
3837 {
3838     int j;
3839     Widget button, buttonBar;
3840     Arg args[16];
3841
3842     j = 0;
3843     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3844     if (tinyLayout) {
3845         XtSetArg(args[j], XtNhSpace, 0); j++;
3846     }
3847     XtSetArg(args[j], XtNborderWidth, 0); j++;
3848     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3849     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3850                                formWidget, args, j);
3851
3852     while (mi->string != NULL) {
3853         j = 0;
3854         if (tinyLayout) {
3855             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3856             XtSetArg(args[j], XtNborderWidth, 0); j++;
3857         }
3858       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3859         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3860                                        buttonBar, args, j);
3861         XtAddCallback(button, XtNcallback,
3862                       (XtCallbackProc) MenuBarSelect,
3863                       (caddr_t) mi->proc);
3864         mi++;
3865     }
3866     return buttonBar;
3867 }
3868
3869 Widget
3870 CreatePieceMenu(name, color)
3871      char *name;
3872      int color;
3873 {
3874     int i;
3875     Widget entry, menu;
3876     Arg args[16];
3877     ChessSquare selection;
3878
3879     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3880                               boardWidget, args, 0);
3881
3882     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3883         String item = pieceMenuStrings[color][i];
3884
3885         if (strcmp(item, "----") == 0) {
3886             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3887                                           menu, NULL, 0);
3888         } else {
3889           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3890             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3891                                 menu, args, 1);
3892             selection = pieceMenuTranslation[color][i];
3893             XtAddCallback(entry, XtNcallback,
3894                           (XtCallbackProc) PieceMenuSelect,
3895                           (caddr_t) selection);
3896             if (selection == WhitePawn || selection == BlackPawn) {
3897                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3898                 XtSetValues(menu, args, 1);
3899             }
3900         }
3901     }
3902     return menu;
3903 }
3904
3905 void
3906 CreatePieceMenus()
3907 {
3908     int i;
3909     Widget entry;
3910     Arg args[16];
3911     ChessSquare selection;
3912
3913     whitePieceMenu = CreatePieceMenu("menuW", 0);
3914     blackPieceMenu = CreatePieceMenu("menuB", 1);
3915
3916     XtRegisterGrabAction(PieceMenuPopup, True,
3917                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3918                          GrabModeAsync, GrabModeAsync);
3919
3920     XtSetArg(args[0], XtNlabel, _("Drop"));
3921     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3922                                   boardWidget, args, 1);
3923     for (i = 0; i < DROP_MENU_SIZE; i++) {
3924         String item = dropMenuStrings[i];
3925
3926         if (strcmp(item, "----") == 0) {
3927             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3928                                           dropMenu, NULL, 0);
3929         } else {
3930           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3931             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3932                                 dropMenu, args, 1);
3933             selection = dropMenuTranslation[i];
3934             XtAddCallback(entry, XtNcallback,
3935                           (XtCallbackProc) DropMenuSelect,
3936                           (caddr_t) selection);
3937         }
3938     }
3939 }
3940
3941 void SetupDropMenu()
3942 {
3943     int i, j, count;
3944     char label[32];
3945     Arg args[16];
3946     Widget entry;
3947     char* p;
3948
3949     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3950         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3951         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3952                    dmEnables[i].piece);
3953         XtSetSensitive(entry, p != NULL || !appData.testLegality
3954                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3955                                        && !appData.icsActive));
3956         count = 0;
3957         while (p && *p++ == dmEnables[i].piece) count++;
3958         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3959         j = 0;
3960         XtSetArg(args[j], XtNlabel, label); j++;
3961         XtSetValues(entry, args, j);
3962     }
3963 }
3964
3965 void PieceMenuPopup(w, event, params, num_params)
3966      Widget w;
3967      XEvent *event;
3968      String *params;
3969      Cardinal *num_params;
3970 {
3971     String whichMenu; int menuNr;
3972     shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3973     if (event->type == ButtonRelease)
3974         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3975     else if (event->type == ButtonPress)
3976         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3977     switch(menuNr) {
3978       case 0: whichMenu = params[0]; break;
3979       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3980       case 2:
3981       case -1: if (errorUp) ErrorPopDown();
3982       default: return;
3983     }
3984     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3985 }
3986
3987 static void PieceMenuSelect(w, piece, junk)
3988      Widget w;
3989      ChessSquare piece;
3990      caddr_t junk;
3991 {
3992     if (pmFromX < 0 || pmFromY < 0) return;
3993     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3994 }
3995
3996 static void DropMenuSelect(w, piece, junk)
3997      Widget w;
3998      ChessSquare piece;
3999      caddr_t junk;
4000 {
4001     if (pmFromX < 0 || pmFromY < 0) return;
4002     DropMenuEvent(piece, pmFromX, pmFromY);
4003 }
4004
4005 void WhiteClock(w, event, prms, nprms)
4006      Widget w;
4007      XEvent *event;
4008      String *prms;
4009      Cardinal *nprms;
4010 {
4011     ClockClick(0);
4012 }
4013
4014 void BlackClock(w, event, prms, nprms)
4015      Widget w;
4016      XEvent *event;
4017      String *prms;
4018      Cardinal *nprms;
4019 {
4020     ClockClick(1);
4021 }
4022
4023
4024 /*
4025  * If the user selects on a border boundary, return -1; if off the board,
4026  *   return -2.  Otherwise map the event coordinate to the square.
4027  */
4028 int EventToSquare(x, limit)
4029      int x;
4030 {
4031     if (x <= 0)
4032       return -2;
4033     if (x < lineGap)
4034       return -1;
4035     x -= lineGap;
4036     if ((x % (squareSize + lineGap)) >= squareSize)
4037       return -1;
4038     x /= (squareSize + lineGap);
4039     if (x >= limit)
4040       return -2;
4041     return x;
4042 }
4043
4044 static void do_flash_delay(msec)
4045      unsigned long msec;
4046 {
4047     TimeDelay(msec);
4048 }
4049
4050 static void drawHighlight(file, rank, gc)
4051      int file, rank;
4052      GC gc;
4053 {
4054     int x, y;
4055
4056     if (lineGap == 0) return;
4057
4058     if (flipView) {
4059         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4060           (squareSize + lineGap);
4061         y = lineGap/2 + rank * (squareSize + lineGap);
4062     } else {
4063         x = lineGap/2 + file * (squareSize + lineGap);
4064         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4065           (squareSize + lineGap);
4066     }
4067
4068     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4069                    squareSize+lineGap, squareSize+lineGap);
4070 }
4071
4072 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4073 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4074
4075 void
4076 SetHighlights(fromX, fromY, toX, toY)
4077      int fromX, fromY, toX, toY;
4078 {
4079     if (hi1X != fromX || hi1Y != fromY) {
4080         if (hi1X >= 0 && hi1Y >= 0) {
4081             drawHighlight(hi1X, hi1Y, lineGC);
4082         }
4083     } // [HGM] first erase both, then draw new!
4084     if (hi2X != toX || hi2Y != toY) {
4085         if (hi2X >= 0 && hi2Y >= 0) {
4086             drawHighlight(hi2X, hi2Y, lineGC);
4087         }
4088     }
4089     if (hi1X != fromX || hi1Y != fromY) {
4090         if (fromX >= 0 && fromY >= 0) {
4091             drawHighlight(fromX, fromY, highlineGC);
4092         }
4093     }
4094     if (hi2X != toX || hi2Y != toY) {
4095         if (toX >= 0 && toY >= 0) {
4096             drawHighlight(toX, toY, highlineGC);
4097         }
4098     }
4099     hi1X = fromX;
4100     hi1Y = fromY;
4101     hi2X = toX;
4102     hi2Y = toY;
4103 }
4104
4105 void
4106 ClearHighlights()
4107 {
4108     SetHighlights(-1, -1, -1, -1);
4109 }
4110
4111
4112 void
4113 SetPremoveHighlights(fromX, fromY, toX, toY)
4114      int fromX, fromY, toX, toY;
4115 {
4116     if (pm1X != fromX || pm1Y != fromY) {
4117         if (pm1X >= 0 && pm1Y >= 0) {
4118             drawHighlight(pm1X, pm1Y, lineGC);
4119         }
4120         if (fromX >= 0 && fromY >= 0) {
4121             drawHighlight(fromX, fromY, prelineGC);
4122         }
4123     }
4124     if (pm2X != toX || pm2Y != toY) {
4125         if (pm2X >= 0 && pm2Y >= 0) {
4126             drawHighlight(pm2X, pm2Y, lineGC);
4127         }
4128         if (toX >= 0 && toY >= 0) {
4129             drawHighlight(toX, toY, prelineGC);
4130         }
4131     }
4132     pm1X = fromX;
4133     pm1Y = fromY;
4134     pm2X = toX;
4135     pm2Y = toY;
4136 }
4137
4138 void
4139 ClearPremoveHighlights()
4140 {
4141   SetPremoveHighlights(-1, -1, -1, -1);
4142 }
4143
4144 static int CutOutSquare(x, y, x0, y0, kind)
4145      int x, y, *x0, *y0, kind;
4146 {
4147     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4148     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4149     *x0 = 0; *y0 = 0;
4150     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4151     if(textureW[kind] < W*squareSize)
4152         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4153     else
4154         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4155     if(textureH[kind] < H*squareSize)
4156         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4157     else
4158         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4159     return 1;
4160 }
4161
4162 static void BlankSquare(x, y, color, piece, dest, fac)
4163      int x, y, color, fac;
4164      ChessSquare piece;
4165      Drawable dest;
4166 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4167     int x0, y0;
4168     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4169         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4170                   squareSize, squareSize, x*fac, y*fac);
4171     } else
4172     if (useImages && useImageSqs) {
4173         Pixmap pm;
4174         switch (color) {
4175           case 1: /* light */
4176             pm = xpmLightSquare;
4177             break;
4178           case 0: /* dark */
4179             pm = xpmDarkSquare;
4180             break;
4181           case 2: /* neutral */
4182           default:
4183             pm = xpmJailSquare;
4184             break;
4185         }
4186         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4187                   squareSize, squareSize, x*fac, y*fac);
4188     } else {
4189         GC gc;
4190         switch (color) {
4191           case 1: /* light */
4192             gc = lightSquareGC;
4193             break;
4194           case 0: /* dark */
4195             gc = darkSquareGC;
4196             break;
4197           case 2: /* neutral */
4198           default:
4199             gc = jailSquareGC;
4200             break;
4201         }
4202         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4203     }
4204 }
4205
4206 /*
4207    I split out the routines to draw a piece so that I could
4208    make a generic flash routine.
4209 */
4210 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4211      ChessSquare piece;
4212      int square_color, x, y;
4213      Drawable dest;
4214 {
4215     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4216     switch (square_color) {
4217       case 1: /* light */
4218       case 2: /* neutral */
4219       default:
4220         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4221                   ? *pieceToOutline(piece)
4222                   : *pieceToSolid(piece),
4223                   dest, bwPieceGC, 0, 0,
4224                   squareSize, squareSize, x, y);
4225         break;
4226       case 0: /* dark */
4227         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4228                   ? *pieceToSolid(piece)
4229                   : *pieceToOutline(piece),
4230                   dest, wbPieceGC, 0, 0,
4231                   squareSize, squareSize, x, y);
4232         break;
4233     }
4234 }
4235
4236 static void monoDrawPiece(piece, square_color, x, y, dest)
4237      ChessSquare piece;
4238      int square_color, x, y;
4239      Drawable dest;
4240 {
4241     switch (square_color) {
4242       case 1: /* light */
4243       case 2: /* neutral */
4244       default:
4245         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4246                    ? *pieceToOutline(piece)
4247                    : *pieceToSolid(piece),
4248                    dest, bwPieceGC, 0, 0,
4249                    squareSize, squareSize, x, y, 1);
4250         break;
4251       case 0: /* dark */
4252         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4253                    ? *pieceToSolid(piece)
4254                    : *pieceToOutline(piece),
4255                    dest, wbPieceGC, 0, 0,
4256                    squareSize, squareSize, x, y, 1);
4257         break;
4258     }
4259 }
4260
4261 static void colorDrawPiece(piece, square_color, x, y, dest)
4262      ChessSquare piece;
4263      int square_color, x, y;
4264      Drawable dest;
4265 {
4266     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4267     switch (square_color) {
4268       case 1: /* light */
4269         XCopyPlane(xDisplay, *pieceToSolid(piece),
4270                    dest, (int) piece < (int) BlackPawn
4271                    ? wlPieceGC : blPieceGC, 0, 0,
4272                    squareSize, squareSize, x, y, 1);
4273         break;
4274       case 0: /* dark */
4275         XCopyPlane(xDisplay, *pieceToSolid(piece),
4276                    dest, (int) piece < (int) BlackPawn
4277                    ? wdPieceGC : bdPieceGC, 0, 0,
4278                    squareSize, squareSize, x, y, 1);
4279         break;
4280       case 2: /* neutral */
4281       default:
4282         XCopyPlane(xDisplay, *pieceToSolid(piece),
4283                    dest, (int) piece < (int) BlackPawn
4284                    ? wjPieceGC : bjPieceGC, 0, 0,
4285                    squareSize, squareSize, x, y, 1);
4286         break;
4287     }
4288 }
4289
4290 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4291      ChessSquare piece;
4292      int square_color, x, y;
4293      Drawable dest;
4294 {
4295     int kind, p = piece;
4296
4297     switch (square_color) {
4298       case 1: /* light */
4299       case 2: /* neutral */
4300       default:
4301         if ((int)piece < (int) BlackPawn) {
4302             kind = 0;
4303         } else {
4304             kind = 2;
4305             piece -= BlackPawn;
4306         }
4307         break;
4308       case 0: /* dark */
4309         if ((int)piece < (int) BlackPawn) {
4310             kind = 1;
4311         } else {
4312             kind = 3;
4313             piece -= BlackPawn;
4314         }
4315         break;
4316     }
4317     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4318     if(useTexture & square_color+1) {
4319         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4320         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4321         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4322         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4323         XSetClipMask(xDisplay, wlPieceGC, None);
4324         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4325     } else
4326     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4327               dest, wlPieceGC, 0, 0,
4328               squareSize, squareSize, x, y);
4329 }
4330
4331 typedef void (*DrawFunc)();
4332
4333 DrawFunc ChooseDrawFunc()
4334 {
4335     if (appData.monoMode) {
4336         if (DefaultDepth(xDisplay, xScreen) == 1) {
4337             return monoDrawPiece_1bit;
4338         } else {
4339             return monoDrawPiece;
4340         }
4341     } else {
4342         if (useImages)
4343           return colorDrawPieceImage;
4344         else
4345           return colorDrawPiece;
4346     }
4347 }
4348
4349 /* [HR] determine square color depending on chess variant. */
4350 static int SquareColor(row, column)
4351      int row, column;
4352 {
4353     int square_color;
4354
4355     if (gameInfo.variant == VariantXiangqi) {
4356         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4357             square_color = 1;
4358         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4359             square_color = 0;
4360         } else if (row <= 4) {
4361             square_color = 0;
4362         } else {
4363             square_color = 1;
4364         }
4365     } else {
4366         square_color = ((column + row) % 2) == 1;
4367     }
4368
4369     /* [hgm] holdings: next line makes all holdings squares light */
4370     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4371
4372     return square_color;
4373 }
4374
4375 void DrawSquare(row, column, piece, do_flash)
4376      int row, column, do_flash;
4377      ChessSquare piece;
4378 {
4379     int square_color, x, y, direction, font_ascent, font_descent;
4380     int i;
4381     char string[2];
4382     XCharStruct overall;
4383     DrawFunc drawfunc;
4384     int flash_delay;
4385
4386     /* Calculate delay in milliseconds (2-delays per complete flash) */
4387     flash_delay = 500 / appData.flashRate;
4388
4389     if (flipView) {
4390         x = lineGap + ((BOARD_WIDTH-1)-column) *
4391           (squareSize + lineGap);
4392         y = lineGap + row * (squareSize + lineGap);
4393     } else {
4394         x = lineGap + column * (squareSize + lineGap);
4395         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4396           (squareSize + lineGap);
4397     }
4398
4399     if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4400
4401     square_color = SquareColor(row, column);
4402
4403     if ( // [HGM] holdings: blank out area between board and holdings
4404                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4405               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4406                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4407                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4408
4409                         // [HGM] print piece counts next to holdings
4410                         string[1] = NULLCHAR;
4411                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4412                             string[0] = '0' + piece;
4413                             XTextExtents(countFontStruct, string, 1, &direction,
4414                                  &font_ascent, &font_descent, &overall);
4415                             if (appData.monoMode) {
4416                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4417                                                  x + squareSize - overall.width - 2,
4418                                                  y + font_ascent + 1, string, 1);
4419                             } else {
4420                                 XDrawString(xDisplay, xBoardWindow, countGC,
4421                                             x + squareSize - overall.width - 2,
4422                                             y + font_ascent + 1, string, 1);
4423                             }
4424                         }
4425                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4426                             string[0] = '0' + piece;
4427                             XTextExtents(countFontStruct, string, 1, &direction,
4428                                          &font_ascent, &font_descent, &overall);
4429                             if (appData.monoMode) {
4430                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4431                                                  x + 2, y + font_ascent + 1, string, 1);
4432                             } else {
4433                                 XDrawString(xDisplay, xBoardWindow, countGC,
4434                                             x + 2, y + font_ascent + 1, string, 1);
4435                             }
4436                         }
4437     } else {
4438             if (piece == EmptySquare || appData.blindfold) {
4439                         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4440             } else {
4441                         drawfunc = ChooseDrawFunc();
4442
4443                         if (do_flash && appData.flashCount > 0) {
4444                             for (i=0; i<appData.flashCount; ++i) {
4445                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4446                                         XSync(xDisplay, False);
4447                                         do_flash_delay(flash_delay);
4448
4449                                         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4450                                         XSync(xDisplay, False);
4451                                         do_flash_delay(flash_delay);
4452                             }
4453                         }
4454                         drawfunc(piece, square_color, x, y, xBoardWindow);
4455         }
4456         }
4457
4458     string[1] = NULLCHAR;
4459     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4460                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4461         string[0] = 'a' + column - BOARD_LEFT;
4462         XTextExtents(coordFontStruct, string, 1, &direction,
4463                      &font_ascent, &font_descent, &overall);
4464         if (appData.monoMode) {
4465             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4466                              x + squareSize - overall.width - 2,
4467                              y + squareSize - font_descent - 1, string, 1);
4468         } else {
4469             XDrawString(xDisplay, xBoardWindow, coordGC,
4470                         x + squareSize - overall.width - 2,
4471                         y + squareSize - font_descent - 1, string, 1);
4472         }
4473     }
4474     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4475         string[0] = ONE + row;
4476         XTextExtents(coordFontStruct, string, 1, &direction,
4477                      &font_ascent, &font_descent, &overall);
4478         if (appData.monoMode) {
4479             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4480                              x + 2, y + font_ascent + 1, string, 1);
4481         } else {
4482             XDrawString(xDisplay, xBoardWindow, coordGC,
4483                         x + 2, y + font_ascent + 1, string, 1);
4484         }
4485     }
4486     if(!partnerUp && marker[row][column]) {
4487         XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4488                 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4489     }
4490 }
4491
4492
4493 /* Why is this needed on some versions of X? */
4494 void EventProc(widget, unused, event)
4495      Widget widget;
4496      caddr_t unused;
4497      XEvent *event;
4498 {
4499     if (!XtIsRealized(widget))
4500       return;
4501
4502     switch (event->type) {
4503       case Expose:
4504         if (event->xexpose.count > 0) return;  /* no clipping is done */
4505         XDrawPosition(widget, True, NULL);
4506         if(twoBoards) { // [HGM] dual: draw other board in other orientation
4507             flipView = !flipView; partnerUp = !partnerUp;
4508             XDrawPosition(widget, True, NULL);
4509             flipView = !flipView; partnerUp = !partnerUp;
4510         }
4511         break;
4512       case MotionNotify:
4513         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4514       default:
4515         return;
4516     }
4517 }
4518 /* end why */
4519
4520 void DrawPosition(fullRedraw, board)
4521      /*Boolean*/int fullRedraw;
4522      Board board;
4523 {
4524     XDrawPosition(boardWidget, fullRedraw, board);
4525 }
4526
4527 /* Returns 1 if there are "too many" differences between b1 and b2
4528    (i.e. more than 1 move was made) */
4529 static int too_many_diffs(b1, b2)
4530      Board b1, b2;
4531 {
4532     int i, j;
4533     int c = 0;
4534
4535     for (i=0; i<BOARD_HEIGHT; ++i) {
4536         for (j=0; j<BOARD_WIDTH; ++j) {
4537             if (b1[i][j] != b2[i][j]) {
4538                 if (++c > 4)    /* Castling causes 4 diffs */
4539                   return 1;
4540             }
4541         }
4542     }
4543     return 0;
4544 }
4545
4546 /* Matrix describing castling maneuvers */
4547 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4548 static int castling_matrix[4][5] = {
4549     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4550     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4551     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4552     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4553 };
4554
4555 /* Checks whether castling occurred. If it did, *rrow and *rcol
4556    are set to the destination (row,col) of the rook that moved.
4557
4558    Returns 1 if castling occurred, 0 if not.
4559
4560    Note: Only handles a max of 1 castling move, so be sure
4561    to call too_many_diffs() first.
4562    */
4563 static int check_castle_draw(newb, oldb, rrow, rcol)
4564      Board newb, oldb;
4565      int *rrow, *rcol;
4566 {
4567     int i, *r, j;
4568     int match;
4569
4570     /* For each type of castling... */
4571     for (i=0; i<4; ++i) {
4572         r = castling_matrix[i];
4573
4574         /* Check the 4 squares involved in the castling move */
4575         match = 0;
4576         for (j=1; j<=4; ++j) {
4577             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4578                 match = 1;
4579                 break;
4580             }
4581         }
4582
4583         if (!match) {
4584             /* All 4 changed, so it must be a castling move */
4585             *rrow = r[0];
4586             *rcol = r[3];
4587             return 1;
4588         }
4589     }
4590     return 0;
4591 }
4592
4593 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4594 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4595 {
4596       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4597 }
4598
4599 void DrawSeekBackground( int left, int top, int right, int bottom )
4600 {
4601     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4602 }
4603
4604 void DrawSeekText(char *buf, int x, int y)
4605 {
4606     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4607 }
4608
4609 void DrawSeekDot(int x, int y, int colorNr)
4610 {
4611     int square = colorNr & 0x80;
4612     GC color;
4613     colorNr &= 0x7F;
4614     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4615     if(square)
4616         XFillRectangle(xDisplay, xBoardWindow, color,
4617                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4618     else
4619         XFillArc(xDisplay, xBoardWindow, color,
4620                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4621 }
4622
4623 static int damage[2][BOARD_RANKS][BOARD_FILES];
4624
4625 /*
4626  * event handler for redrawing the board
4627  */
4628 void XDrawPosition(w, repaint, board)
4629      Widget w;
4630      /*Boolean*/int repaint;
4631      Board board;
4632 {
4633     int i, j, do_flash;
4634     static int lastFlipView = 0;
4635     static int lastBoardValid[2] = {0, 0};
4636     static Board lastBoard[2];
4637     Arg args[16];
4638     int rrow, rcol;
4639     int nr = twoBoards*partnerUp;
4640
4641     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4642
4643     if (board == NULL) {
4644         if (!lastBoardValid[nr]) return;
4645         board = lastBoard[nr];
4646     }
4647     if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4648         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4649         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4650                     args, 1);
4651     }
4652
4653     /*
4654      * It would be simpler to clear the window with XClearWindow()
4655      * but this causes a very distracting flicker.
4656      */
4657
4658     if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4659
4660         if ( lineGap && IsDrawArrowEnabled())
4661             XDrawSegments(xDisplay, xBoardWindow, lineGC,
4662                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4663
4664         /* If too much changes (begin observing new game, etc.), don't
4665            do flashing */
4666         do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4667
4668         /* Special check for castling so we don't flash both the king
4669            and the rook (just flash the king). */
4670         if (do_flash) {
4671             if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4672                 /* Draw rook with NO flashing. King will be drawn flashing later */
4673                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4674                 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4675             }
4676         }
4677
4678         /* First pass -- Draw (newly) empty squares and repair damage.
4679            This prevents you from having a piece show up twice while it
4680            is flashing on its new square */
4681         for (i = 0; i < BOARD_HEIGHT; i++)
4682           for (j = 0; j < BOARD_WIDTH; j++)
4683             if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4684                 || damage[nr][i][j]) {
4685                 DrawSquare(i, j, board[i][j], 0);
4686                 damage[nr][i][j] = False;
4687             }
4688
4689         /* Second pass -- Draw piece(s) in new position and flash them */
4690         for (i = 0; i < BOARD_HEIGHT; i++)
4691           for (j = 0; j < BOARD_WIDTH; j++)
4692             if (board[i][j] != lastBoard[nr][i][j]) {
4693                 DrawSquare(i, j, board[i][j], do_flash);
4694             }
4695     } else {
4696         if (lineGap > 0)
4697           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4698                         twoBoards & partnerUp ? secondSegments : // [HGM] dual
4699                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4700
4701         for (i = 0; i < BOARD_HEIGHT; i++)
4702           for (j = 0; j < BOARD_WIDTH; j++) {
4703               DrawSquare(i, j, board[i][j], 0);
4704               damage[nr][i][j] = False;
4705           }
4706     }
4707
4708     CopyBoard(lastBoard[nr], board);
4709     lastBoardValid[nr] = 1;
4710   if(nr == 0) { // [HGM] dual: no highlights on second board yet
4711     lastFlipView = flipView;
4712
4713     /* Draw highlights */
4714     if (pm1X >= 0 && pm1Y >= 0) {
4715       drawHighlight(pm1X, pm1Y, prelineGC);
4716     }
4717     if (pm2X >= 0 && pm2Y >= 0) {
4718       drawHighlight(pm2X, pm2Y, prelineGC);
4719     }
4720     if (hi1X >= 0 && hi1Y >= 0) {
4721       drawHighlight(hi1X, hi1Y, highlineGC);
4722     }
4723     if (hi2X >= 0 && hi2Y >= 0) {
4724       drawHighlight(hi2X, hi2Y, highlineGC);
4725     }
4726     DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4727   }
4728     /* If piece being dragged around board, must redraw that too */
4729     DrawDragPiece();
4730
4731     XSync(xDisplay, False);
4732 }
4733
4734
4735 /*
4736  * event handler for redrawing the board
4737  */
4738 void DrawPositionProc(w, event, prms, nprms)
4739      Widget w;
4740      XEvent *event;
4741      String *prms;
4742      Cardinal *nprms;
4743 {
4744     XDrawPosition(w, True, NULL);
4745 }
4746
4747
4748 /*
4749  * event handler for parsing user moves
4750  */
4751 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4752 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4753 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4754 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
4755 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4756 //       and at the end FinishMove() to perform the move after optional promotion popups.
4757 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4758 void HandleUserMove(w, event, prms, nprms)
4759      Widget w;
4760      XEvent *event;
4761      String *prms;
4762      Cardinal *nprms;
4763 {
4764     if (w != boardWidget || errorExitStatus != -1) return;
4765     if(nprms) shiftKey = !strcmp(prms[0], "1");
4766
4767     if (promotionUp) {
4768         if (event->type == ButtonPress) {
4769             XtPopdown(promotionShell);
4770             XtDestroyWidget(promotionShell);
4771             promotionUp = False;
4772             ClearHighlights();
4773             fromX = fromY = -1;
4774         } else {
4775             return;
4776         }
4777     }
4778
4779     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4780     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
4781     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4782 }
4783
4784 void AnimateUserMove (Widget w, XEvent * event,
4785                       String * params, Cardinal * nParams)
4786 {
4787     if(!PromoScroll(event->xmotion.x, event->xmotion.y))
4788     DragPieceMove(event->xmotion.x, event->xmotion.y);
4789 }
4790
4791 void HandlePV (Widget w, XEvent * event,
4792                       String * params, Cardinal * nParams)
4793 {   // [HGM] pv: walk PV
4794     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4795 }
4796
4797 static int savedIndex;  /* gross that this is global */
4798
4799 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4800 {
4801         String val;
4802         XawTextPosition index, dummy;
4803         Arg arg;
4804
4805         XawTextGetSelectionPos(w, &index, &dummy);
4806         XtSetArg(arg, XtNstring, &val);
4807         XtGetValues(w, &arg, 1);
4808         ReplaceComment(savedIndex, val);
4809         if(savedIndex != currentMove) ToNrEvent(savedIndex);
4810         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4811 }
4812
4813 void EditCommentPopUp(index, title, text)
4814      int index;
4815      char *title, *text;
4816 {
4817     savedIndex = index;
4818     if (text == NULL) text = "";
4819     NewCommentPopup(title, text, index);
4820 }
4821
4822 void ICSInputBoxPopUp()
4823 {
4824     InputBoxPopup();
4825 }
4826
4827 extern Option boxOptions[];
4828
4829 void ICSInputSendText()
4830 {
4831     Widget edit;
4832     int j;
4833     Arg args[16];
4834     String val;
4835
4836     edit = boxOptions[0].handle;
4837     j = 0;
4838     XtSetArg(args[j], XtNstring, &val); j++;
4839     XtGetValues(edit, args, j);
4840     SaveInHistory(val);
4841     SendMultiLineToICS(val);
4842     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4843     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4844 }
4845
4846 void ICSInputBoxPopDown()
4847 {
4848     PopDown(4);
4849 }
4850
4851 void CommentPopUp(title, text)
4852      char *title, *text;
4853 {
4854     savedIndex = currentMove; // [HGM] vari
4855     NewCommentPopup(title, text, currentMove);
4856 }
4857
4858 void CommentPopDown()
4859 {
4860     PopDown(1);
4861 }
4862
4863 void FileNamePopUp(label, def, filter, proc, openMode)
4864      char *label;
4865      char *def;
4866      char *filter;
4867      FileProc proc;
4868      char *openMode;
4869 {
4870     fileProc = proc;            /* I can't see a way not */
4871     fileOpenMode = openMode;    /*   to use globals here */
4872     {   // [HGM] use file-selector dialog stolen from Ghostview
4873         char *name;
4874         int index; // this is not supported yet
4875         FILE *f;
4876         if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
4877                            (def[0] ? def : NULL), filter, openMode, NULL, &name))
4878           (void) (*fileProc)(f, index=0, name);
4879     }
4880 }
4881
4882 void FileNamePopDown()
4883 {
4884     if (!filenameUp) return;
4885     XtPopdown(fileNameShell);
4886     XtDestroyWidget(fileNameShell);
4887     filenameUp = False;
4888     ModeHighlight();
4889 }
4890
4891 void FileNameCallback(w, client_data, call_data)
4892      Widget w;
4893      XtPointer client_data, call_data;
4894 {
4895     String name;
4896     Arg args[16];
4897
4898     XtSetArg(args[0], XtNlabel, &name);
4899     XtGetValues(w, args, 1);
4900
4901     if (strcmp(name, _("cancel")) == 0) {
4902         FileNamePopDown();
4903         return;
4904     }
4905
4906     FileNameAction(w, NULL, NULL, NULL);
4907 }
4908
4909 void FileNameAction(w, event, prms, nprms)
4910      Widget w;
4911      XEvent *event;
4912      String *prms;
4913      Cardinal *nprms;
4914 {
4915     char buf[MSG_SIZ];
4916     String name;
4917     FILE *f;
4918     char *p, *fullname;
4919     int index;
4920
4921     name = XawDialogGetValueString(w = XtParent(w));
4922
4923     if ((name != NULL) && (*name != NULLCHAR)) {
4924         safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
4925         XtPopdown(w = XtParent(XtParent(w)));
4926         XtDestroyWidget(w);
4927         filenameUp = False;
4928
4929         p = strrchr(buf, ' ');
4930         if (p == NULL) {
4931             index = 0;
4932         } else {
4933             *p++ = NULLCHAR;
4934             index = atoi(p);
4935         }
4936         fullname = ExpandPathName(buf);
4937         if (!fullname) {
4938             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
4939         }
4940         else {
4941             f = fopen(fullname, fileOpenMode);
4942             if (f == NULL) {
4943                 DisplayError(_("Failed to open file"), errno);
4944             } else {
4945                 (void) (*fileProc)(f, index, buf);
4946             }
4947         }
4948         ModeHighlight();
4949         return;
4950     }
4951
4952     XtPopdown(w = XtParent(XtParent(w)));
4953     XtDestroyWidget(w);
4954     filenameUp = False;
4955     ModeHighlight();
4956 }
4957
4958 void PromotionPopUp()
4959 {
4960     Arg args[16];
4961     Widget dialog, layout;
4962     Position x, y;
4963     Dimension bw_width, pw_width;
4964     int j;
4965
4966     j = 0;
4967     XtSetArg(args[j], XtNwidth, &bw_width); j++;
4968     XtGetValues(boardWidget, args, j);
4969
4970     j = 0;
4971     XtSetArg(args[j], XtNresizable, True); j++;
4972     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
4973     promotionShell =
4974       XtCreatePopupShell("Promotion", transientShellWidgetClass,
4975                          shellWidget, args, j);
4976     layout =
4977       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
4978                             layoutArgs, XtNumber(layoutArgs));
4979
4980     j = 0;
4981     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
4982     XtSetArg(args[j], XtNborderWidth, 0); j++;
4983     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
4984                                    layout, args, j);
4985
4986   if(gameInfo.variant != VariantShogi) {
4987    if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
4988       XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
4989                          (XtPointer) dialog);
4990       XawDialogAddButton(dialog, _("General"), PromotionCallback,
4991                          (XtPointer) dialog);
4992       XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
4993                          (XtPointer) dialog);
4994       XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
4995                          (XtPointer) dialog);
4996     } else {\r
4997     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
4998                        (XtPointer) dialog);
4999     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5000                        (XtPointer) dialog);
5001     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5002                        (XtPointer) dialog);
5003     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5004                        (XtPointer) dialog);
5005     }
5006     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5007         gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
5008         gameInfo.variant == VariantGiveaway) {
5009       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5010                          (XtPointer) dialog);
5011     }
5012     if(gameInfo.variant == VariantCapablanca ||
5013        gameInfo.variant == VariantGothic ||
5014        gameInfo.variant == VariantCapaRandom) {
5015       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5016                          (XtPointer) dialog);
5017       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5018                          (XtPointer) dialog);
5019     }
5020   } else // [HGM] shogi
5021   {
5022       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5023                          (XtPointer) dialog);
5024       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5025                          (XtPointer) dialog);
5026   }
5027     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5028                        (XtPointer) dialog);
5029
5030     XtRealizeWidget(promotionShell);
5031     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5032
5033     j = 0;
5034     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5035     XtGetValues(promotionShell, args, j);
5036
5037     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5038                       lineGap + squareSize/3 +
5039                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5040                        0 : 6*(squareSize + lineGap)), &x, &y);
5041
5042     j = 0;
5043     XtSetArg(args[j], XtNx, x); j++;
5044     XtSetArg(args[j], XtNy, y); j++;
5045     XtSetValues(promotionShell, args, j);
5046
5047     XtPopup(promotionShell, XtGrabNone);
5048
5049     promotionUp = True;
5050 }
5051
5052 void PromotionPopDown()
5053 {
5054     if (!promotionUp) return;
5055     XtPopdown(promotionShell);
5056     XtDestroyWidget(promotionShell);
5057     promotionUp = False;
5058 }
5059
5060 void PromotionCallback(w, client_data, call_data)
5061      Widget w;
5062      XtPointer client_data, call_data;
5063 {
5064     String name;
5065     Arg args[16];
5066     int promoChar;
5067
5068     XtSetArg(args[0], XtNlabel, &name);
5069     XtGetValues(w, args, 1);
5070
5071     PromotionPopDown();
5072
5073     if (fromX == -1) return;
5074
5075     if (strcmp(name, _("cancel")) == 0) {
5076         fromX = fromY = -1;
5077         ClearHighlights();
5078         return;
5079     } else if (strcmp(name, _("Knight")) == 0) {
5080         promoChar = 'n';
5081     } else if (strcmp(name, _("Promote")) == 0) {
5082         promoChar = '+';
5083     } else if (strcmp(name, _("Defer")) == 0) {
5084         promoChar = '=';
5085     } else {
5086         promoChar = ToLower(name[0]);
5087     }
5088
5089     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5090
5091     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5092     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5093     fromX = fromY = -1;
5094 }
5095
5096
5097 void ErrorCallback(w, client_data, call_data)
5098      Widget w;
5099      XtPointer client_data, call_data;
5100 {
5101     errorUp = False;
5102     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5103     XtDestroyWidget(w);
5104     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5105 }
5106
5107
5108 void ErrorPopDown()
5109 {
5110     if (!errorUp) return;
5111     errorUp = False;
5112     XtPopdown(errorShell);
5113     XtDestroyWidget(errorShell);
5114     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5115 }
5116
5117 void ErrorPopUp(title, label, modal)
5118      char *title, *label;
5119      int modal;
5120 {
5121     Arg args[16];
5122     Widget dialog, layout;
5123     Position x, y;
5124     int xx, yy;
5125     Window junk;
5126     Dimension bw_width, pw_width;
5127     Dimension pw_height;
5128     int i;
5129
5130     i = 0;
5131     XtSetArg(args[i], XtNresizable, True);  i++;
5132     XtSetArg(args[i], XtNtitle, title); i++;
5133     errorShell =
5134       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5135                          shellWidget, args, i);
5136     layout =
5137       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5138                             layoutArgs, XtNumber(layoutArgs));
5139
5140     i = 0;
5141     XtSetArg(args[i], XtNlabel, label); i++;
5142     XtSetArg(args[i], XtNborderWidth, 0); i++;
5143     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5144                                    layout, args, i);
5145
5146     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5147
5148     XtRealizeWidget(errorShell);
5149     CatchDeleteWindow(errorShell, "ErrorPopDown");
5150
5151     i = 0;
5152     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5153     XtGetValues(boardWidget, args, i);
5154     i = 0;
5155     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5156     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5157     XtGetValues(errorShell, args, i);
5158
5159 #ifdef NOTDEF
5160     /* This code seems to tickle an X bug if it is executed too soon
5161        after xboard starts up.  The coordinates get transformed as if
5162        the main window was positioned at (0, 0).
5163        */
5164     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5165                       0 - pw_height + squareSize / 3, &x, &y);
5166 #else
5167     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5168                           RootWindowOfScreen(XtScreen(boardWidget)),
5169                           (bw_width - pw_width) / 2,
5170                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5171     x = xx;
5172     y = yy;
5173 #endif
5174     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5175
5176     i = 0;
5177     XtSetArg(args[i], XtNx, x);  i++;
5178     XtSetArg(args[i], XtNy, y);  i++;
5179     XtSetValues(errorShell, args, i);
5180
5181     errorUp = True;
5182     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5183 }
5184
5185 /* Disable all user input other than deleting the window */
5186 static int frozen = 0;
5187 void FreezeUI()
5188 {
5189   if (frozen) return;
5190   /* Grab by a widget that doesn't accept input */
5191   XtAddGrab(messageWidget, TRUE, FALSE);
5192   frozen = 1;
5193 }
5194
5195 /* Undo a FreezeUI */
5196 void ThawUI()
5197 {
5198   if (!frozen) return;
5199   XtRemoveGrab(messageWidget);
5200   frozen = 0;
5201 }
5202
5203 char *ModeToWidgetName(mode)
5204      GameMode mode;
5205 {
5206     switch (mode) {
5207       case BeginningOfGame:
5208         if (appData.icsActive)
5209           return "menuMode.ICS Client";
5210         else if (appData.noChessProgram ||
5211                  *appData.cmailGameName != NULLCHAR)
5212           return "menuMode.Edit Game";
5213         else
5214           return "menuMode.Machine Black";
5215       case MachinePlaysBlack:
5216         return "menuMode.Machine Black";
5217       case MachinePlaysWhite:
5218         return "menuMode.Machine White";
5219       case AnalyzeMode:
5220         return "menuMode.Analysis Mode";
5221       case AnalyzeFile:
5222         return "menuMode.Analyze File";
5223       case TwoMachinesPlay:
5224         return "menuMode.Two Machines";
5225       case EditGame:
5226         return "menuMode.Edit Game";
5227       case PlayFromGameFile:
5228         return "menuFile.Load Game";
5229       case EditPosition:
5230         return "menuMode.Edit Position";
5231       case Training:
5232         return "menuMode.Training";
5233       case IcsPlayingWhite:
5234       case IcsPlayingBlack:
5235       case IcsObserving:
5236       case IcsIdle:
5237       case IcsExamining:
5238         return "menuMode.ICS Client";
5239       default:
5240       case EndOfGame:
5241         return NULL;
5242     }
5243 }
5244
5245 void ModeHighlight()
5246 {
5247     Arg args[16];
5248     static int oldPausing = FALSE;
5249     static GameMode oldmode = (GameMode) -1;
5250     char *wname;
5251
5252     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5253
5254     if (pausing != oldPausing) {
5255         oldPausing = pausing;
5256         if (pausing) {
5257             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5258         } else {
5259             XtSetArg(args[0], XtNleftBitmap, None);
5260         }
5261         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5262                     args, 1);
5263
5264         if (appData.showButtonBar) {
5265           /* Always toggle, don't set.  Previous code messes up when
5266              invoked while the button is pressed, as releasing it
5267              toggles the state again. */
5268           {
5269             Pixel oldbg, oldfg;
5270             XtSetArg(args[0], XtNbackground, &oldbg);
5271             XtSetArg(args[1], XtNforeground, &oldfg);
5272             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5273                         args, 2);
5274             XtSetArg(args[0], XtNbackground, oldfg);
5275             XtSetArg(args[1], XtNforeground, oldbg);
5276           }
5277           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5278         }
5279     }
5280
5281     wname = ModeToWidgetName(oldmode);
5282     if (wname != NULL) {
5283         XtSetArg(args[0], XtNleftBitmap, None);
5284         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5285     }
5286     wname = ModeToWidgetName(gameMode);
5287     if (wname != NULL) {
5288         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5289         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5290     }
5291     oldmode = gameMode;
5292
5293     /* Maybe all the enables should be handled here, not just this one */
5294     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5295                    gameMode == Training || gameMode == PlayFromGameFile);
5296 }
5297
5298
5299 /*
5300  * Button/menu procedures
5301  */
5302 void ResetProc(w, event, prms, nprms)
5303      Widget w;
5304      XEvent *event;
5305      String *prms;
5306      Cardinal *nprms;
5307 {
5308     ResetGameEvent();
5309 }
5310
5311 int LoadGamePopUp(f, gameNumber, title)
5312      FILE *f;
5313      int gameNumber;
5314      char *title;
5315 {
5316     cmailMsgLoaded = FALSE;
5317     if (gameNumber == 0) {
5318         int error = GameListBuild(f);
5319         if (error) {
5320             DisplayError(_("Cannot build game list"), error);
5321         } else if (!ListEmpty(&gameList) &&
5322                    ((ListGame *) gameList.tailPred)->number > 1) {
5323             GameListPopUp(f, title);
5324             return TRUE;
5325         }
5326         GameListDestroy();
5327         gameNumber = 1;
5328     }
5329     return LoadGame(f, gameNumber, title, FALSE);
5330 }
5331
5332 void LoadGameProc(w, event, prms, nprms)
5333      Widget w;
5334      XEvent *event;
5335      String *prms;
5336      Cardinal *nprms;
5337 {
5338     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5339         Reset(FALSE, TRUE);
5340     }
5341     FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
5342 }
5343
5344 void LoadNextGameProc(w, event, prms, nprms)
5345      Widget w;
5346      XEvent *event;
5347      String *prms;
5348      Cardinal *nprms;
5349 {
5350     ReloadGame(1);
5351 }
5352
5353 void LoadPrevGameProc(w, event, prms, nprms)
5354      Widget w;
5355      XEvent *event;
5356      String *prms;
5357      Cardinal *nprms;
5358 {
5359     ReloadGame(-1);
5360 }
5361
5362 void ReloadGameProc(w, event, prms, nprms)
5363      Widget w;
5364      XEvent *event;
5365      String *prms;
5366      Cardinal *nprms;
5367 {
5368     ReloadGame(0);
5369 }
5370
5371 void LoadNextPositionProc(w, event, prms, nprms)
5372      Widget w;
5373      XEvent *event;
5374      String *prms;
5375      Cardinal *nprms;
5376 {
5377     ReloadPosition(1);
5378 }
5379
5380 void LoadPrevPositionProc(w, event, prms, nprms)
5381      Widget w;
5382      XEvent *event;
5383      String *prms;
5384      Cardinal *nprms;
5385 {
5386     ReloadPosition(-1);
5387 }
5388
5389 void ReloadPositionProc(w, event, prms, nprms)
5390      Widget w;
5391      XEvent *event;
5392      String *prms;
5393      Cardinal *nprms;
5394 {
5395     ReloadPosition(0);
5396 }
5397
5398 void LoadPositionProc(w, event, prms, nprms)
5399      Widget w;
5400      XEvent *event;
5401      String *prms;
5402      Cardinal *nprms;
5403 {
5404     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5405         Reset(FALSE, TRUE);
5406     }
5407     FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
5408 }
5409
5410 void SaveGameProc(w, event, prms, nprms)
5411      Widget w;
5412      XEvent *event;
5413      String *prms;
5414      Cardinal *nprms;
5415 {
5416     FileNamePopUp(_("Save game file name?"),
5417                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5418                   appData.oldSaveStyle ? ".game" : ".pgn",
5419                   SaveGame, "a");
5420 }
5421
5422 void SavePositionProc(w, event, prms, nprms)
5423      Widget w;
5424      XEvent *event;
5425      String *prms;
5426      Cardinal *nprms;
5427 {
5428     FileNamePopUp(_("Save position file name?"),
5429                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5430                   appData.oldSaveStyle ? ".pos" : ".fen",
5431                   SavePosition, "a");
5432 }
5433
5434 void ReloadCmailMsgProc(w, event, prms, nprms)
5435      Widget w;
5436      XEvent *event;
5437      String *prms;
5438      Cardinal *nprms;
5439 {
5440     ReloadCmailMsgEvent(FALSE);
5441 }
5442
5443 void MailMoveProc(w, event, prms, nprms)
5444      Widget w;
5445      XEvent *event;
5446      String *prms;
5447      Cardinal *nprms;
5448 {
5449     MailMoveEvent();
5450 }
5451
5452 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5453 char *selected_fen_position=NULL;
5454
5455 Boolean
5456 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5457                  Atom *type_return, XtPointer *value_return,
5458                  unsigned long *length_return, int *format_return)
5459 {
5460   char *selection_tmp;
5461
5462   if (!selected_fen_position) return False; /* should never happen */
5463   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5464     /* note: since no XtSelectionDoneProc was registered, Xt will
5465      * automatically call XtFree on the value returned.  So have to
5466      * make a copy of it allocated with XtMalloc */
5467     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5468     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5469
5470     *value_return=selection_tmp;
5471     *length_return=strlen(selection_tmp);
5472     *type_return=*target;
5473     *format_return = 8; /* bits per byte */
5474     return True;
5475   } else if (*target == XA_TARGETS(xDisplay)) {
5476     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5477     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5478     targets_tmp[1] = XA_STRING;
5479     *value_return = targets_tmp;
5480     *type_return = XA_ATOM;
5481     *length_return = 2;
5482     *format_return = 8 * sizeof(Atom);
5483     if (*format_return > 32) {
5484       *length_return *= *format_return / 32;
5485       *format_return = 32;
5486     }
5487     return True;
5488   } else {
5489     return False;
5490   }
5491 }
5492
5493 /* note: when called from menu all parameters are NULL, so no clue what the
5494  * Widget which was clicked on was, or what the click event was
5495  */
5496 void CopyPositionProc(w, event, prms, nprms)
5497   Widget w;
5498   XEvent *event;
5499   String *prms;
5500   Cardinal *nprms;
5501   {
5502     /*
5503      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5504      * have a notion of a position that is selected but not copied.
5505      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5506      */
5507     if(gameMode == EditPosition) EditPositionDone(TRUE);
5508     if (selected_fen_position) free(selected_fen_position);
5509     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5510     if (!selected_fen_position) return;
5511     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5512                    CurrentTime,
5513                    SendPositionSelection,
5514                    NULL/* lose_ownership_proc */ ,
5515                    NULL/* transfer_done_proc */);
5516     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5517                    CurrentTime,
5518                    SendPositionSelection,
5519                    NULL/* lose_ownership_proc */ ,
5520                    NULL/* transfer_done_proc */);
5521   }
5522
5523 /* function called when the data to Paste is ready */
5524 static void
5525 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5526            Atom *type, XtPointer value, unsigned long *len, int *format)
5527 {
5528   char *fenstr=value;
5529   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5530   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5531   EditPositionPasteFEN(fenstr);
5532   XtFree(value);
5533 }
5534
5535 /* called when Paste Position button is pressed,
5536  * all parameters will be NULL */
5537 void PastePositionProc(w, event, prms, nprms)
5538   Widget w;
5539   XEvent *event;
5540   String *prms;
5541   Cardinal *nprms;
5542 {
5543     XtGetSelectionValue(menuBarWidget,
5544       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5545       /* (XtSelectionCallbackProc) */ PastePositionCB,
5546       NULL, /* client_data passed to PastePositionCB */
5547
5548       /* better to use the time field from the event that triggered the
5549        * call to this function, but that isn't trivial to get
5550        */
5551       CurrentTime
5552     );
5553     return;
5554 }
5555
5556 static Boolean
5557 SendGameSelection(Widget w, Atom *selection, Atom *target,
5558                   Atom *type_return, XtPointer *value_return,
5559                   unsigned long *length_return, int *format_return)
5560 {
5561   char *selection_tmp;
5562
5563   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5564     FILE* f = fopen(gameCopyFilename, "r");
5565     long len;
5566     size_t count;
5567     if (f == NULL) return False;
5568     fseek(f, 0, 2);
5569     len = ftell(f);
5570     rewind(f);
5571     selection_tmp = XtMalloc(len + 1);
5572     count = fread(selection_tmp, 1, len, f);
5573     fclose(f);
5574     if (len != count) {
5575       XtFree(selection_tmp);
5576       return False;
5577     }
5578     selection_tmp[len] = NULLCHAR;
5579     *value_return = selection_tmp;
5580     *length_return = len;
5581     *type_return = *target;
5582     *format_return = 8; /* bits per byte */
5583     return True;
5584   } else if (*target == XA_TARGETS(xDisplay)) {
5585     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5586     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5587     targets_tmp[1] = XA_STRING;
5588     *value_return = targets_tmp;
5589     *type_return = XA_ATOM;
5590     *length_return = 2;
5591     *format_return = 8 * sizeof(Atom);
5592     if (*format_return > 32) {
5593       *length_return *= *format_return / 32;
5594       *format_return = 32;
5595     }
5596     return True;
5597   } else {
5598     return False;
5599   }
5600 }
5601
5602 void CopySomething()
5603 {
5604   int ret;
5605
5606   /*
5607    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5608    * have a notion of a game that is selected but not copied.
5609    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5610    */
5611   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5612                  CurrentTime,
5613                  SendGameSelection,
5614                  NULL/* lose_ownership_proc */ ,
5615                  NULL/* transfer_done_proc */);
5616   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5617                  CurrentTime,
5618                  SendGameSelection,
5619                  NULL/* lose_ownership_proc */ ,
5620                  NULL/* transfer_done_proc */);
5621 }
5622
5623 /* note: when called from menu all parameters are NULL, so no clue what the
5624  * Widget which was clicked on was, or what the click event was
5625  */
5626 void CopyGameProc(w, event, prms, nprms)
5627   Widget w;
5628   XEvent *event;
5629   String *prms;
5630   Cardinal *nprms;
5631 {
5632   int ret;
5633
5634   ret = SaveGameToFile(gameCopyFilename, FALSE);
5635   if (!ret) return;
5636
5637   CopySomething();
5638 }
5639
5640 void CopyGameListProc(w, event, prms, nprms)
5641   Widget w;
5642   XEvent *event;
5643   String *prms;
5644   Cardinal *nprms;
5645 {
5646   if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5647   CopySomething();
5648 }
5649
5650 /* function called when the data to Paste is ready */
5651 static void
5652 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5653             Atom *type, XtPointer value, unsigned long *len, int *format)
5654 {
5655   FILE* f;
5656   if (value == NULL || *len == 0) {
5657     return; /* nothing had been selected to copy */
5658   }
5659   f = fopen(gamePasteFilename, "w");
5660   if (f == NULL) {
5661     DisplayError(_("Can't open temp file"), errno);
5662     return;
5663   }
5664   fwrite(value, 1, *len, f);
5665   fclose(f);
5666   XtFree(value);
5667   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5668 }
5669
5670 /* called when Paste Game button is pressed,
5671  * all parameters will be NULL */
5672 void PasteGameProc(w, event, prms, nprms)
5673   Widget w;
5674   XEvent *event;
5675   String *prms;
5676   Cardinal *nprms;
5677 {
5678     XtGetSelectionValue(menuBarWidget,
5679       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5680       /* (XtSelectionCallbackProc) */ PasteGameCB,
5681       NULL, /* client_data passed to PasteGameCB */
5682
5683       /* better to use the time field from the event that triggered the
5684        * call to this function, but that isn't trivial to get
5685        */
5686       CurrentTime
5687     );
5688     return;
5689 }
5690
5691
5692 void AutoSaveGame()
5693 {
5694     SaveGameProc(NULL, NULL, NULL, NULL);
5695 }
5696
5697
5698 void QuitProc(w, event, prms, nprms)
5699      Widget w;
5700      XEvent *event;
5701      String *prms;
5702      Cardinal *nprms;
5703 {
5704     ExitEvent(0);
5705 }
5706
5707 void PauseProc(w, event, prms, nprms)
5708      Widget w;
5709      XEvent *event;
5710      String *prms;
5711      Cardinal *nprms;
5712 {
5713     PauseEvent();
5714 }
5715
5716
5717 void MachineBlackProc(w, event, prms, nprms)
5718      Widget w;
5719      XEvent *event;
5720      String *prms;
5721      Cardinal *nprms;
5722 {
5723     MachineBlackEvent();
5724 }
5725
5726 void MachineWhiteProc(w, event, prms, nprms)
5727      Widget w;
5728      XEvent *event;
5729      String *prms;
5730      Cardinal *nprms;
5731 {
5732     MachineWhiteEvent();
5733 }
5734
5735 void AnalyzeModeProc(w, event, prms, nprms)
5736      Widget w;
5737      XEvent *event;
5738      String *prms;
5739      Cardinal *nprms;
5740 {
5741     char buf[MSG_SIZ];
5742
5743     if (!first.analysisSupport) {
5744       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5745       DisplayError(buf, 0);
5746       return;
5747     }
5748     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5749     if (appData.icsActive) {
5750         if (gameMode != IcsObserving) {
5751           snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5752             DisplayError(buf, 0);
5753             /* secure check */
5754             if (appData.icsEngineAnalyze) {
5755                 if (appData.debugMode)
5756                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5757                 ExitAnalyzeMode();
5758                 ModeHighlight();
5759             }
5760             return;
5761         }
5762         /* if enable, use want disable icsEngineAnalyze */
5763         if (appData.icsEngineAnalyze) {
5764                 ExitAnalyzeMode();
5765                 ModeHighlight();
5766                 return;
5767         }
5768         appData.icsEngineAnalyze = TRUE;
5769         if (appData.debugMode)
5770             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5771     }
5772 #ifndef OPTIONSDIALOG
5773     if (!appData.showThinking)
5774       ShowThinkingProc(w,event,prms,nprms);
5775 #endif
5776
5777     AnalyzeModeEvent();
5778 }
5779
5780 void AnalyzeFileProc(w, event, prms, nprms)
5781      Widget w;
5782      XEvent *event;
5783      String *prms;
5784      Cardinal *nprms;
5785 {
5786     if (!first.analysisSupport) {
5787       char buf[MSG_SIZ];
5788       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5789       DisplayError(buf, 0);
5790       return;
5791     }
5792     Reset(FALSE, TRUE);
5793 #ifndef OPTIONSDIALOG
5794     if (!appData.showThinking)
5795       ShowThinkingProc(w,event,prms,nprms);
5796 #endif
5797     AnalyzeFileEvent();
5798     FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5799     AnalysisPeriodicEvent(1);
5800 }
5801
5802 void TwoMachinesProc(w, event, prms, nprms)
5803      Widget w;
5804      XEvent *event;
5805      String *prms;
5806      Cardinal *nprms;
5807 {
5808     TwoMachinesEvent();
5809 }
5810
5811 void MatchProc(w, event, prms, nprms)
5812      Widget w;
5813      XEvent *event;
5814      String *prms;
5815      Cardinal *nprms;
5816 {
5817     if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
5818     appData.matchGames = appData.defaultMatchGames;
5819     MatchEvent(2);\r
5820 }
5821
5822 void IcsClientProc(w, event, prms, nprms)
5823      Widget w;
5824      XEvent *event;
5825      String *prms;
5826      Cardinal *nprms;
5827 {
5828     IcsClientEvent();
5829 }
5830
5831 void EditGameProc(w, event, prms, nprms)
5832      Widget w;
5833      XEvent *event;
5834      String *prms;
5835      Cardinal *nprms;
5836 {
5837     EditGameEvent();
5838 }
5839
5840 void EditPositionProc(w, event, prms, nprms)
5841      Widget w;
5842      XEvent *event;
5843      String *prms;
5844      Cardinal *nprms;
5845 {
5846     EditPositionEvent();
5847 }
5848
5849 void TrainingProc(w, event, prms, nprms)
5850      Widget w;
5851      XEvent *event;
5852      String *prms;
5853      Cardinal *nprms;
5854 {
5855     TrainingEvent();
5856 }
5857
5858 void EditCommentProc(w, event, prms, nprms)
5859      Widget w;
5860      XEvent *event;
5861      String *prms;
5862      Cardinal *nprms;
5863 {
5864     Arg args[5];
5865     int j;
5866     if (PopDown(1)) { // popdown succesful
5867         j = 0;
5868         XtSetArg(args[j], XtNleftBitmap, None); j++;
5869         XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
5870         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
5871     } else // was not up
5872         EditCommentEvent();
5873 }
5874
5875 void IcsInputBoxProc(w, event, prms, nprms)
5876      Widget w;
5877      XEvent *event;
5878      String *prms;
5879      Cardinal *nprms;
5880 {
5881     if (!PopDown(4)) ICSInputBoxPopUp();
5882 }
5883
5884 void AcceptProc(w, event, prms, nprms)
5885      Widget w;
5886      XEvent *event;
5887      String *prms;
5888      Cardinal *nprms;
5889 {
5890     AcceptEvent();
5891 }
5892
5893 void DeclineProc(w, event, prms, nprms)
5894      Widget w;
5895      XEvent *event;
5896      String *prms;
5897      Cardinal *nprms;
5898 {
5899     DeclineEvent();
5900 }
5901
5902 void RematchProc(w, event, prms, nprms)
5903      Widget w;
5904      XEvent *event;
5905      String *prms;
5906      Cardinal *nprms;
5907 {
5908     RematchEvent();
5909 }
5910
5911 void CallFlagProc(w, event, prms, nprms)
5912      Widget w;
5913      XEvent *event;
5914      String *prms;
5915      Cardinal *nprms;
5916 {
5917     CallFlagEvent();
5918 }
5919
5920 void DrawProc(w, event, prms, nprms)
5921      Widget w;
5922      XEvent *event;
5923      String *prms;
5924      Cardinal *nprms;
5925 {
5926     DrawEvent();
5927 }
5928
5929 void AbortProc(w, event, prms, nprms)
5930      Widget w;
5931      XEvent *event;
5932      String *prms;
5933      Cardinal *nprms;
5934 {
5935     AbortEvent();
5936 }
5937
5938 void AdjournProc(w, event, prms, nprms)
5939      Widget w;
5940      XEvent *event;
5941      String *prms;
5942      Cardinal *nprms;
5943 {
5944     AdjournEvent();
5945 }
5946
5947 void ResignProc(w, event, prms, nprms)
5948      Widget w;
5949      XEvent *event;
5950      String *prms;
5951      Cardinal *nprms;
5952 {
5953     ResignEvent();
5954 }
5955
5956 void AdjuWhiteProc(w, event, prms, nprms)
5957      Widget w;
5958      XEvent *event;
5959      String *prms;
5960      Cardinal *nprms;
5961 {
5962     UserAdjudicationEvent(+1);
5963 }
5964
5965 void AdjuBlackProc(w, event, prms, nprms)
5966      Widget w;
5967      XEvent *event;
5968      String *prms;
5969      Cardinal *nprms;
5970 {
5971     UserAdjudicationEvent(-1);
5972 }
5973
5974 void AdjuDrawProc(w, event, prms, nprms)
5975      Widget w;
5976      XEvent *event;
5977      String *prms;
5978      Cardinal *nprms;
5979 {
5980     UserAdjudicationEvent(0);
5981 }
5982
5983 void EnterKeyProc(w, event, prms, nprms)
5984      Widget w;
5985      XEvent *event;
5986      String *prms;
5987      Cardinal *nprms;
5988 {
5989     if (shellUp[4] == True)
5990       ICSInputSendText();
5991 }
5992
5993 void UpKeyProc(w, event, prms, nprms)
5994      Widget w;
5995      XEvent *event;
5996      String *prms;
5997      Cardinal *nprms;
5998 {   // [HGM] input: let up-arrow recall previous line from history
5999     Widget edit;
6000     int j;
6001     Arg args[16];
6002     String val;
6003     XawTextBlock t;
6004
6005     if (!shellUp[4]) return;
6006     edit = boxOptions[0].handle;
6007     j = 0;
6008     XtSetArg(args[j], XtNstring, &val); j++;
6009     XtGetValues(edit, args, j);
6010     val = PrevInHistory(val);
6011     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6012     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6013     if(val) {
6014         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6015         XawTextReplace(edit, 0, 0, &t);
6016         XawTextSetInsertionPoint(edit, 9999);
6017     }
6018 }
6019
6020 void DownKeyProc(w, event, prms, nprms)
6021      Widget w;
6022      XEvent *event;
6023      String *prms;
6024      Cardinal *nprms;
6025 {   // [HGM] input: let down-arrow recall next line from history
6026     Widget edit;
6027     String val;
6028     XawTextBlock t;
6029
6030     if (!shellUp[4]) return;
6031     edit = boxOptions[0].handle;
6032     val = NextInHistory();
6033     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6034     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6035     if(val) {
6036         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6037         XawTextReplace(edit, 0, 0, &t);
6038         XawTextSetInsertionPoint(edit, 9999);
6039     }
6040 }
6041
6042 void StopObservingProc(w, event, prms, nprms)
6043      Widget w;
6044      XEvent *event;
6045      String *prms;
6046      Cardinal *nprms;
6047 {
6048     StopObservingEvent();
6049 }
6050
6051 void StopExaminingProc(w, event, prms, nprms)
6052      Widget w;
6053      XEvent *event;
6054      String *prms;
6055      Cardinal *nprms;
6056 {
6057     StopExaminingEvent();
6058 }
6059
6060 void UploadProc(w, event, prms, nprms)
6061      Widget w;
6062      XEvent *event;
6063      String *prms;
6064      Cardinal *nprms;
6065 {
6066     UploadGameEvent();
6067 }
6068
6069
6070 void ForwardProc(w, event, prms, nprms)
6071      Widget w;
6072      XEvent *event;
6073      String *prms;
6074      Cardinal *nprms;
6075 {
6076     ForwardEvent();
6077 }
6078
6079
6080 void BackwardProc(w, event, prms, nprms)
6081      Widget w;
6082      XEvent *event;
6083      String *prms;
6084      Cardinal *nprms;
6085 {
6086     BackwardEvent();
6087 }
6088
6089 void ToStartProc(w, event, prms, nprms)
6090      Widget w;
6091      XEvent *event;
6092      String *prms;
6093      Cardinal *nprms;
6094 {
6095     ToStartEvent();
6096 }
6097
6098 void ToEndProc(w, event, prms, nprms)
6099      Widget w;
6100      XEvent *event;
6101      String *prms;
6102      Cardinal *nprms;
6103 {
6104     ToEndEvent();
6105 }
6106
6107 void RevertProc(w, event, prms, nprms)
6108      Widget w;
6109      XEvent *event;
6110      String *prms;
6111      Cardinal *nprms;
6112 {
6113     RevertEvent(False);
6114 }
6115
6116 void AnnotateProc(w, event, prms, nprms)
6117      Widget w;
6118      XEvent *event;
6119      String *prms;
6120      Cardinal *nprms;
6121 {
6122     RevertEvent(True);
6123 }
6124
6125 void TruncateGameProc(w, event, prms, nprms)
6126      Widget w;
6127      XEvent *event;
6128      String *prms;
6129      Cardinal *nprms;
6130 {
6131     TruncateGameEvent();
6132 }
6133 void RetractMoveProc(w, event, prms, nprms)
6134      Widget w;
6135      XEvent *event;
6136      String *prms;
6137      Cardinal *nprms;
6138 {
6139     RetractMoveEvent();
6140 }
6141
6142 void MoveNowProc(w, event, prms, nprms)
6143      Widget w;
6144      XEvent *event;
6145      String *prms;
6146      Cardinal *nprms;
6147 {
6148     MoveNowEvent();
6149 }
6150
6151 void FlipViewProc(w, event, prms, nprms)
6152      Widget w;
6153      XEvent *event;
6154      String *prms;
6155      Cardinal *nprms;
6156 {
6157     flipView = !flipView;
6158     DrawPosition(True, NULL);
6159 }
6160
6161 void PonderNextMoveProc(w, event, prms, nprms)
6162      Widget w;
6163      XEvent *event;
6164      String *prms;
6165      Cardinal *nprms;
6166 {
6167     Arg args[16];
6168
6169     PonderNextMoveEvent(!appData.ponderNextMove);
6170 #ifndef OPTIONSDIALOG
6171     if (appData.ponderNextMove) {
6172         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6173     } else {
6174         XtSetArg(args[0], XtNleftBitmap, None);
6175     }
6176     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6177                 args, 1);
6178 #endif
6179 }
6180
6181 #ifndef OPTIONSDIALOG
6182 void AlwaysQueenProc(w, event, prms, nprms)
6183      Widget w;
6184      XEvent *event;
6185      String *prms;
6186      Cardinal *nprms;
6187 {
6188     Arg args[16];
6189
6190     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6191
6192     if (appData.alwaysPromoteToQueen) {
6193         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6194     } else {
6195         XtSetArg(args[0], XtNleftBitmap, None);
6196     }
6197     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6198                 args, 1);
6199 }
6200
6201 void AnimateDraggingProc(w, event, prms, nprms)
6202      Widget w;
6203      XEvent *event;
6204      String *prms;
6205      Cardinal *nprms;
6206 {
6207     Arg args[16];
6208
6209     appData.animateDragging = !appData.animateDragging;
6210
6211     if (appData.animateDragging) {
6212         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6213         CreateAnimVars();
6214     } else {
6215         XtSetArg(args[0], XtNleftBitmap, None);
6216     }
6217     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6218                 args, 1);
6219 }
6220
6221 void AnimateMovingProc(w, event, prms, nprms)
6222      Widget w;
6223      XEvent *event;
6224      String *prms;
6225      Cardinal *nprms;
6226 {
6227     Arg args[16];
6228
6229     appData.animate = !appData.animate;
6230
6231     if (appData.animate) {
6232         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6233         CreateAnimVars();
6234     } else {
6235         XtSetArg(args[0], XtNleftBitmap, None);
6236     }
6237     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6238                 args, 1);
6239 }
6240
6241 void AutoflagProc(w, event, prms, nprms)
6242      Widget w;
6243      XEvent *event;
6244      String *prms;
6245      Cardinal *nprms;
6246 {
6247     Arg args[16];
6248
6249     appData.autoCallFlag = !appData.autoCallFlag;
6250
6251     if (appData.autoCallFlag) {
6252         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6253     } else {
6254         XtSetArg(args[0], XtNleftBitmap, None);
6255     }
6256     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6257                 args, 1);
6258 }
6259
6260 void AutoflipProc(w, event, prms, nprms)
6261      Widget w;
6262      XEvent *event;
6263      String *prms;
6264      Cardinal *nprms;
6265 {
6266     Arg args[16];
6267
6268     appData.autoFlipView = !appData.autoFlipView;
6269
6270     if (appData.autoFlipView) {
6271         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6272     } else {
6273         XtSetArg(args[0], XtNleftBitmap, None);
6274     }
6275     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6276                 args, 1);
6277 }
6278
6279 void BlindfoldProc(w, event, prms, nprms)
6280      Widget w;
6281      XEvent *event;
6282      String *prms;
6283      Cardinal *nprms;
6284 {
6285     Arg args[16];
6286
6287     appData.blindfold = !appData.blindfold;
6288
6289     if (appData.blindfold) {
6290         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6291     } else {
6292         XtSetArg(args[0], XtNleftBitmap, None);
6293     }
6294     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6295                 args, 1);
6296
6297     DrawPosition(True, NULL);
6298 }
6299
6300 void TestLegalityProc(w, event, prms, nprms)
6301      Widget w;
6302      XEvent *event;
6303      String *prms;
6304      Cardinal *nprms;
6305 {
6306     Arg args[16];
6307
6308     appData.testLegality = !appData.testLegality;
6309
6310     if (appData.testLegality) {
6311         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6312     } else {
6313         XtSetArg(args[0], XtNleftBitmap, None);
6314     }
6315     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6316                 args, 1);
6317 }
6318
6319
6320 void FlashMovesProc(w, event, prms, nprms)
6321      Widget w;
6322      XEvent *event;
6323      String *prms;
6324      Cardinal *nprms;
6325 {
6326     Arg args[16];
6327
6328     if (appData.flashCount == 0) {
6329         appData.flashCount = 3;
6330     } else {
6331         appData.flashCount = -appData.flashCount;
6332     }
6333
6334     if (appData.flashCount > 0) {
6335         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6336     } else {
6337         XtSetArg(args[0], XtNleftBitmap, None);
6338     }
6339     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6340                 args, 1);
6341 }
6342
6343 #if HIGHDRAG
6344 void HighlightDraggingProc(w, event, prms, nprms)
6345      Widget w;
6346      XEvent *event;
6347      String *prms;
6348      Cardinal *nprms;
6349 {
6350     Arg args[16];
6351
6352     appData.highlightDragging = !appData.highlightDragging;
6353
6354     if (appData.highlightDragging) {
6355         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6356     } else {
6357         XtSetArg(args[0], XtNleftBitmap, None);
6358     }
6359     XtSetValues(XtNameToWidget(menuBarWidget,
6360                                "menuOptions.Highlight Dragging"), args, 1);
6361 }
6362 #endif
6363
6364 void HighlightLastMoveProc(w, event, prms, nprms)
6365      Widget w;
6366      XEvent *event;
6367      String *prms;
6368      Cardinal *nprms;
6369 {
6370     Arg args[16];
6371
6372     appData.highlightLastMove = !appData.highlightLastMove;
6373
6374     if (appData.highlightLastMove) {
6375         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6376     } else {
6377         XtSetArg(args[0], XtNleftBitmap, None);
6378     }
6379     XtSetValues(XtNameToWidget(menuBarWidget,
6380                                "menuOptions.Highlight Last Move"), args, 1);
6381 }
6382
6383 void HighlightArrowProc(w, event, prms, nprms)
6384      Widget w;
6385      XEvent *event;
6386      String *prms;
6387      Cardinal *nprms;
6388 {
6389     Arg args[16];
6390
6391     appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6392
6393     if (appData.highlightMoveWithArrow) {
6394         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6395     } else {
6396         XtSetArg(args[0], XtNleftBitmap, None);
6397     }
6398     XtSetValues(XtNameToWidget(menuBarWidget,
6399                                "menuOptions.Arrow"), args, 1);
6400 }
6401
6402 #if 0
6403 void IcsAlarmProc(w, event, prms, nprms)
6404      Widget w;
6405      XEvent *event;
6406      String *prms;
6407      Cardinal *nprms;
6408 {
6409     Arg args[16];
6410
6411     appData.icsAlarm = !appData.icsAlarm;
6412
6413     if (appData.icsAlarm) {
6414         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6415     } else {
6416         XtSetArg(args[0], XtNleftBitmap, None);
6417     }
6418     XtSetValues(XtNameToWidget(menuBarWidget,
6419                                "menuOptions.ICS Alarm"), args, 1);
6420 }
6421 #endif
6422
6423 void MoveSoundProc(w, event, prms, nprms)
6424      Widget w;
6425      XEvent *event;
6426      String *prms;
6427      Cardinal *nprms;
6428 {
6429     Arg args[16];
6430
6431     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6432
6433     if (appData.ringBellAfterMoves) {
6434         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6435     } else {
6436         XtSetArg(args[0], XtNleftBitmap, None);
6437     }
6438     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6439                 args, 1);
6440 }
6441
6442 void OneClickProc(w, event, prms, nprms)
6443      Widget w;
6444      XEvent *event;
6445      String *prms;
6446      Cardinal *nprms;
6447 {
6448     Arg args[16];
6449
6450     appData.oneClick = !appData.oneClick;
6451
6452     if (appData.oneClick) {
6453         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6454     } else {
6455         XtSetArg(args[0], XtNleftBitmap, None);
6456     }
6457     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6458                 args, 1);
6459 }
6460
6461 void PeriodicUpdatesProc(w, event, prms, nprms)
6462      Widget w;
6463      XEvent *event;
6464      String *prms;
6465      Cardinal *nprms;
6466 {
6467     Arg args[16];
6468
6469     PeriodicUpdatesEvent(!appData.periodicUpdates);
6470
6471     if (appData.periodicUpdates) {
6472         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6473     } else {
6474         XtSetArg(args[0], XtNleftBitmap, None);
6475     }
6476     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6477                 args, 1);
6478 }
6479
6480 void PopupExitMessageProc(w, event, prms, nprms)
6481      Widget w;
6482      XEvent *event;
6483      String *prms;
6484      Cardinal *nprms;
6485 {
6486     Arg args[16];
6487
6488     appData.popupExitMessage = !appData.popupExitMessage;
6489
6490     if (appData.popupExitMessage) {
6491         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6492     } else {
6493         XtSetArg(args[0], XtNleftBitmap, None);
6494     }
6495     XtSetValues(XtNameToWidget(menuBarWidget,
6496                                "menuOptions.Popup Exit Message"), args, 1);
6497 }
6498
6499 void PopupMoveErrorsProc(w, event, prms, nprms)
6500      Widget w;
6501      XEvent *event;
6502      String *prms;
6503      Cardinal *nprms;
6504 {
6505     Arg args[16];
6506
6507     appData.popupMoveErrors = !appData.popupMoveErrors;
6508
6509     if (appData.popupMoveErrors) {
6510         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6511     } else {
6512         XtSetArg(args[0], XtNleftBitmap, None);
6513     }
6514     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6515                 args, 1);
6516 }
6517
6518 #if 0
6519 void PremoveProc(w, event, prms, nprms)
6520      Widget w;
6521      XEvent *event;
6522      String *prms;
6523      Cardinal *nprms;
6524 {
6525     Arg args[16];
6526
6527     appData.premove = !appData.premove;
6528
6529     if (appData.premove) {
6530         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6531     } else {
6532         XtSetArg(args[0], XtNleftBitmap, None);
6533     }
6534     XtSetValues(XtNameToWidget(menuBarWidget,
6535                                "menuOptions.Premove"), args, 1);
6536 }
6537 #endif
6538
6539 void ShowCoordsProc(w, event, prms, nprms)
6540      Widget w;
6541      XEvent *event;
6542      String *prms;
6543      Cardinal *nprms;
6544 {
6545     Arg args[16];
6546
6547     appData.showCoords = !appData.showCoords;
6548
6549     if (appData.showCoords) {
6550         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6551     } else {
6552         XtSetArg(args[0], XtNleftBitmap, None);
6553     }
6554     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6555                 args, 1);
6556
6557     DrawPosition(True, NULL);
6558 }
6559
6560 void ShowThinkingProc(w, event, prms, nprms)
6561      Widget w;
6562      XEvent *event;
6563      String *prms;
6564      Cardinal *nprms;
6565 {
6566     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6567     ShowThinkingEvent();
6568 }
6569
6570 void HideThinkingProc(w, event, prms, nprms)
6571      Widget w;
6572      XEvent *event;
6573      String *prms;
6574      Cardinal *nprms;
6575 {
6576     Arg args[16];
6577
6578     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6579     ShowThinkingEvent();
6580
6581     if (appData.hideThinkingFromHuman) {
6582         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6583     } else {
6584         XtSetArg(args[0], XtNleftBitmap, None);
6585     }
6586     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6587                 args, 1);
6588 }
6589 #endif
6590
6591 void SaveOnExitProc(w, event, prms, nprms)
6592      Widget w;
6593      XEvent *event;
6594      String *prms;
6595      Cardinal *nprms;
6596 {
6597     Arg args[16];
6598
6599     saveSettingsOnExit = !saveSettingsOnExit;
6600
6601     if (saveSettingsOnExit) {
6602         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6603     } else {
6604         XtSetArg(args[0], XtNleftBitmap, None);
6605     }
6606     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6607                 args, 1);
6608 }
6609
6610 void SaveSettingsProc(w, event, prms, nprms)
6611      Widget w;
6612      XEvent *event;
6613      String *prms;
6614      Cardinal *nprms;
6615 {
6616      SaveSettings(settingsFileName);
6617 }
6618
6619 void InfoProc(w, event, prms, nprms)
6620      Widget w;
6621      XEvent *event;
6622      String *prms;
6623      Cardinal *nprms;
6624 {
6625     char buf[MSG_SIZ];
6626     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6627             INFODIR, INFOFILE);
6628     system(buf);
6629 }
6630
6631 void ManProc(w, event, prms, nprms)
6632      Widget w;
6633      XEvent *event;
6634      String *prms;
6635      Cardinal *nprms;
6636 {
6637     char buf[MSG_SIZ];
6638     String name;
6639     if (nprms && *nprms > 0)
6640       name = prms[0];
6641     else
6642       name = "xboard";
6643     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6644     system(buf);
6645 }
6646
6647 void HintProc(w, event, prms, nprms)
6648      Widget w;
6649      XEvent *event;
6650      String *prms;
6651      Cardinal *nprms;
6652 {
6653     HintEvent();
6654 }
6655
6656 void BookProc(w, event, prms, nprms)
6657      Widget w;
6658      XEvent *event;
6659      String *prms;
6660      Cardinal *nprms;
6661 {
6662     BookEvent();
6663 }
6664
6665 void AboutProc(w, event, prms, nprms)
6666      Widget w;
6667      XEvent *event;
6668      String *prms;
6669      Cardinal *nprms;
6670 {
6671     char buf[MSG_SIZ];
6672 #if ZIPPY
6673     char *zippy = " (with Zippy code)";
6674 #else
6675     char *zippy = "";
6676 #endif
6677     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6678             programVersion, zippy,
6679             "Copyright 1991 Digital Equipment Corporation",
6680             "Enhancements Copyright 1992-2009 Free Software Foundation",
6681             "Enhancements Copyright 2005 Alessandro Scotti",
6682             PACKAGE, " is free software and carries NO WARRANTY;",
6683             "see the file COPYING for more information.");
6684     ErrorPopUp(_("About XBoard"), buf, FALSE);
6685 }
6686
6687 void DebugProc(w, event, prms, nprms)
6688      Widget w;
6689      XEvent *event;
6690      String *prms;
6691      Cardinal *nprms;
6692 {
6693     appData.debugMode = !appData.debugMode;
6694 }
6695
6696 void AboutGameProc(w, event, prms, nprms)
6697      Widget w;
6698      XEvent *event;
6699      String *prms;
6700      Cardinal *nprms;
6701 {
6702     AboutGameEvent();
6703 }
6704
6705 void NothingProc(w, event, prms, nprms)
6706      Widget w;
6707      XEvent *event;
6708      String *prms;
6709      Cardinal *nprms;
6710 {
6711     return;
6712 }
6713
6714 void Iconify(w, event, prms, nprms)
6715      Widget w;
6716      XEvent *event;
6717      String *prms;
6718      Cardinal *nprms;
6719 {
6720     Arg args[16];
6721
6722     fromX = fromY = -1;
6723     XtSetArg(args[0], XtNiconic, True);
6724     XtSetValues(shellWidget, args, 1);
6725 }
6726
6727 void DisplayMessage(message, extMessage)
6728      char *message, *extMessage;
6729 {
6730   /* display a message in the message widget */
6731
6732   char buf[MSG_SIZ];
6733   Arg arg;
6734
6735   if (extMessage)
6736     {
6737       if (*message)
6738         {
6739           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
6740           message = buf;
6741         }
6742       else
6743         {
6744           message = extMessage;
6745         };
6746     };
6747
6748   /* need to test if messageWidget already exists, since this function
6749      can also be called during the startup, if for example a Xresource
6750      is not set up correctly */
6751   if(messageWidget)
6752     {
6753       XtSetArg(arg, XtNlabel, message);
6754       XtSetValues(messageWidget, &arg, 1);
6755     };
6756
6757   return;
6758 }
6759
6760 void DisplayTitle(text)
6761      char *text;
6762 {
6763     Arg args[16];
6764     int i;
6765     char title[MSG_SIZ];
6766     char icon[MSG_SIZ];
6767
6768     if (text == NULL) text = "";
6769
6770     if (appData.titleInWindow) {
6771         i = 0;
6772         XtSetArg(args[i], XtNlabel, text);   i++;
6773         XtSetValues(titleWidget, args, i);
6774     }
6775
6776     if (*text != NULLCHAR) {
6777       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6778       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6779     } else if (appData.icsActive) {
6780         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6781         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6782     } else if (appData.cmailGameName[0] != NULLCHAR) {
6783         snprintf(icon, sizeof(icon), "%s", "CMail");
6784         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6785 #ifdef GOTHIC
6786     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6787     } else if (gameInfo.variant == VariantGothic) {
6788       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
6789       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
6790 #endif
6791 #ifdef FALCON
6792     } else if (gameInfo.variant == VariantFalcon) {
6793       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6794       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6795 #endif
6796     } else if (appData.noChessProgram) {
6797       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6798       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6799     } else {
6800       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6801         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6802     }
6803     i = 0;
6804     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6805     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6806     XtSetValues(shellWidget, args, i);
6807 }
6808
6809
6810 void
6811 DisplayError(message, error)
6812      String message;
6813      int error;
6814 {
6815     char buf[MSG_SIZ];
6816
6817     if (error == 0) {
6818         if (appData.debugMode || appData.matchMode) {
6819             fprintf(stderr, "%s: %s\n", programName, message);
6820         }
6821     } else {
6822         if (appData.debugMode || appData.matchMode) {
6823             fprintf(stderr, "%s: %s: %s\n",
6824                     programName, message, strerror(error));
6825         }
6826         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6827         message = buf;
6828     }
6829     ErrorPopUp(_("Error"), message, FALSE);
6830 }
6831
6832
6833 void DisplayMoveError(message)
6834      String message;
6835 {
6836     fromX = fromY = -1;
6837     ClearHighlights();
6838     DrawPosition(FALSE, NULL);
6839     if (appData.debugMode || appData.matchMode) {
6840         fprintf(stderr, "%s: %s\n", programName, message);
6841     }
6842     if (appData.popupMoveErrors) {
6843         ErrorPopUp(_("Error"), message, FALSE);
6844     } else {
6845         DisplayMessage(message, "");
6846     }
6847 }
6848
6849
6850 void DisplayFatalError(message, error, status)
6851      String message;
6852      int error, status;
6853 {
6854     char buf[MSG_SIZ];
6855
6856     errorExitStatus = status;
6857     if (error == 0) {
6858         fprintf(stderr, "%s: %s\n", programName, message);
6859     } else {
6860         fprintf(stderr, "%s: %s: %s\n",
6861                 programName, message, strerror(error));
6862         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6863         message = buf;
6864     }
6865     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6866       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6867     } else {
6868       ExitEvent(status);
6869     }
6870 }
6871
6872 void DisplayInformation(message)
6873      String message;
6874 {
6875     ErrorPopDown();
6876     ErrorPopUp(_("Information"), message, TRUE);
6877 }
6878
6879 void DisplayNote(message)
6880      String message;
6881 {
6882     ErrorPopDown();
6883     ErrorPopUp(_("Note"), message, FALSE);
6884 }
6885
6886 static int
6887 NullXErrorCheck(dpy, error_event)
6888      Display *dpy;
6889      XErrorEvent *error_event;
6890 {
6891     return 0;
6892 }
6893
6894 void DisplayIcsInteractionTitle(message)
6895      String message;
6896 {
6897   if (oldICSInteractionTitle == NULL) {
6898     /* Magic to find the old window title, adapted from vim */
6899     char *wina = getenv("WINDOWID");
6900     if (wina != NULL) {
6901       Window win = (Window) atoi(wina);
6902       Window root, parent, *children;
6903       unsigned int nchildren;
6904       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6905       for (;;) {
6906         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6907         if (!XQueryTree(xDisplay, win, &root, &parent,
6908                         &children, &nchildren)) break;
6909         if (children) XFree((void *)children);
6910         if (parent == root || parent == 0) break;
6911         win = parent;
6912       }
6913       XSetErrorHandler(oldHandler);
6914     }
6915     if (oldICSInteractionTitle == NULL) {
6916       oldICSInteractionTitle = "xterm";
6917     }
6918   }
6919   printf("\033]0;%s\007", message);
6920   fflush(stdout);
6921 }
6922
6923 char pendingReplyPrefix[MSG_SIZ];
6924 ProcRef pendingReplyPR;
6925
6926 void AskQuestionProc(w, event, prms, nprms)
6927      Widget w;
6928      XEvent *event;
6929      String *prms;
6930      Cardinal *nprms;
6931 {
6932     if (*nprms != 4) {
6933         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
6934                 *nprms);
6935         return;
6936     }
6937     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6938 }
6939
6940 void AskQuestionPopDown()
6941 {
6942     if (!askQuestionUp) return;
6943     XtPopdown(askQuestionShell);
6944     XtDestroyWidget(askQuestionShell);
6945     askQuestionUp = False;
6946 }
6947
6948 void AskQuestionReplyAction(w, event, prms, nprms)
6949      Widget w;
6950      XEvent *event;
6951      String *prms;
6952      Cardinal *nprms;
6953 {
6954     char buf[MSG_SIZ];
6955     int err;
6956     String reply;
6957
6958     reply = XawDialogGetValueString(w = XtParent(w));
6959     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
6960     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
6961     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
6962     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
6963     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6964     AskQuestionPopDown();
6965
6966     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
6967 }
6968
6969 void AskQuestionCallback(w, client_data, call_data)
6970      Widget w;
6971      XtPointer client_data, call_data;
6972 {
6973     String name;
6974     Arg args[16];
6975
6976     XtSetArg(args[0], XtNlabel, &name);
6977     XtGetValues(w, args, 1);
6978
6979     if (strcmp(name, _("cancel")) == 0) {
6980         AskQuestionPopDown();
6981     } else {
6982         AskQuestionReplyAction(w, NULL, NULL, NULL);
6983     }
6984 }
6985
6986 void AskQuestion(title, question, replyPrefix, pr)
6987      char *title, *question, *replyPrefix;
6988      ProcRef pr;
6989 {
6990     Arg args[16];
6991     Widget popup, layout, dialog, edit;
6992     Window root, child;
6993     int x, y, i;
6994     int win_x, win_y;
6995     unsigned int mask;
6996
6997     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
6998     pendingReplyPR = pr;
6999
7000     i = 0;
7001     XtSetArg(args[i], XtNresizable, True); i++;
7002     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7003     askQuestionShell = popup =
7004       XtCreatePopupShell(title, transientShellWidgetClass,
7005                          shellWidget, args, i);
7006
7007     layout =
7008       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7009                             layoutArgs, XtNumber(layoutArgs));
7010
7011     i = 0;
7012     XtSetArg(args[i], XtNlabel, question); i++;
7013     XtSetArg(args[i], XtNvalue, ""); i++;
7014     XtSetArg(args[i], XtNborderWidth, 0); i++;
7015     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7016                                    layout, args, i);
7017
7018     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7019                        (XtPointer) dialog);
7020     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7021                        (XtPointer) dialog);
7022
7023     XtRealizeWidget(popup);
7024     CatchDeleteWindow(popup, "AskQuestionPopDown");
7025
7026     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7027                   &x, &y, &win_x, &win_y, &mask);
7028
7029     XtSetArg(args[0], XtNx, x - 10);
7030     XtSetArg(args[1], XtNy, y - 30);
7031     XtSetValues(popup, args, 2);
7032
7033     XtPopup(popup, XtGrabExclusive);
7034     askQuestionUp = True;
7035
7036     edit = XtNameToWidget(dialog, "*value");
7037     XtSetKeyboardFocus(popup, edit);
7038 }
7039
7040
7041 void
7042 PlaySound(name)
7043      char *name;
7044 {
7045   if (*name == NULLCHAR) {
7046     return;
7047   } else if (strcmp(name, "$") == 0) {
7048     putc(BELLCHAR, stderr);
7049   } else {
7050     char buf[2048];
7051     char *prefix = "", *sep = "";
7052     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7053     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7054     system(buf);
7055   }
7056 }
7057
7058 void
7059 RingBell()
7060 {
7061   PlaySound(appData.soundMove);
7062 }
7063
7064 void
7065 PlayIcsWinSound()
7066 {
7067   PlaySound(appData.soundIcsWin);
7068 }
7069
7070 void
7071 PlayIcsLossSound()
7072 {
7073   PlaySound(appData.soundIcsLoss);
7074 }
7075
7076 void
7077 PlayIcsDrawSound()
7078 {
7079   PlaySound(appData.soundIcsDraw);
7080 }
7081
7082 void
7083 PlayIcsUnfinishedSound()
7084 {
7085   PlaySound(appData.soundIcsUnfinished);
7086 }
7087
7088 void
7089 PlayAlarmSound()
7090 {
7091   PlaySound(appData.soundIcsAlarm);
7092 }
7093
7094 void
7095 EchoOn()
7096 {
7097     system("stty echo");
7098 }
7099
7100 void
7101 EchoOff()
7102 {
7103     system("stty -echo");
7104 }
7105
7106 void
7107 Colorize(cc, continuation)
7108      ColorClass cc;
7109      int continuation;
7110 {
7111     char buf[MSG_SIZ];
7112     int count, outCount, error;
7113
7114     if (textColors[(int)cc].bg > 0) {
7115         if (textColors[(int)cc].fg > 0) {
7116           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7117                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7118         } else {
7119           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7120                    textColors[(int)cc].bg);
7121         }
7122     } else {
7123         if (textColors[(int)cc].fg > 0) {
7124           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7125                     textColors[(int)cc].fg);
7126         } else {
7127           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7128         }
7129     }
7130     count = strlen(buf);
7131     outCount = OutputToProcess(NoProc, buf, count, &error);
7132     if (outCount < count) {
7133         DisplayFatalError(_("Error writing to display"), error, 1);
7134     }
7135
7136     if (continuation) return;
7137     switch (cc) {
7138     case ColorShout:
7139       PlaySound(appData.soundShout);
7140       break;
7141     case ColorSShout:
7142       PlaySound(appData.soundSShout);
7143       break;
7144     case ColorChannel1:
7145       PlaySound(appData.soundChannel1);
7146       break;
7147     case ColorChannel:
7148       PlaySound(appData.soundChannel);
7149       break;
7150     case ColorKibitz:
7151       PlaySound(appData.soundKibitz);
7152       break;
7153     case ColorTell:
7154       PlaySound(appData.soundTell);
7155       break;
7156     case ColorChallenge:
7157       PlaySound(appData.soundChallenge);
7158       break;
7159     case ColorRequest:
7160       PlaySound(appData.soundRequest);
7161       break;
7162     case ColorSeek:
7163       PlaySound(appData.soundSeek);
7164       break;
7165     case ColorNormal:
7166     case ColorNone:
7167     default:
7168       break;
7169     }
7170 }
7171
7172 char *UserName()
7173 {
7174     return getpwuid(getuid())->pw_name;
7175 }
7176
7177 static char *
7178 ExpandPathName(path)
7179      char *path;
7180 {
7181     static char static_buf[4*MSG_SIZ];
7182     char *d, *s, buf[4*MSG_SIZ];
7183     struct passwd *pwd;
7184
7185     s = path;
7186     d = static_buf;
7187
7188     while (*s && isspace(*s))
7189       ++s;
7190
7191     if (!*s) {
7192         *d = 0;
7193         return static_buf;
7194     }
7195
7196     if (*s == '~') {
7197         if (*(s+1) == '/') {
7198           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7199           strcat(d, s+1);
7200         }
7201         else {
7202           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7203           { char *p; if(p = strchr(buf, '/')) *p = 0; }
7204           pwd = getpwnam(buf);
7205           if (!pwd)
7206             {
7207               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7208                       buf, path);
7209               return NULL;
7210             }
7211           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7212           strcat(d, strchr(s+1, '/'));
7213         }
7214     }
7215     else
7216       safeStrCpy(d, s, 4*MSG_SIZ );
7217
7218     return static_buf;
7219 }
7220
7221 char *HostName()
7222 {
7223     static char host_name[MSG_SIZ];
7224
7225 #if HAVE_GETHOSTNAME
7226     gethostname(host_name, MSG_SIZ);
7227     return host_name;
7228 #else  /* not HAVE_GETHOSTNAME */
7229 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7230     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7231     return host_name;
7232 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7233     return "localhost";
7234 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7235 #endif /* not HAVE_GETHOSTNAME */
7236 }
7237
7238 XtIntervalId delayedEventTimerXID = 0;
7239 DelayedEventCallback delayedEventCallback = 0;
7240
7241 void
7242 FireDelayedEvent()
7243 {
7244     delayedEventTimerXID = 0;
7245     delayedEventCallback();
7246 }
7247
7248 void
7249 ScheduleDelayedEvent(cb, millisec)
7250      DelayedEventCallback cb; long millisec;
7251 {
7252     if(delayedEventTimerXID && delayedEventCallback == cb)
7253         // [HGM] alive: replace, rather than add or flush identical event
7254         XtRemoveTimeOut(delayedEventTimerXID);
7255     delayedEventCallback = cb;
7256     delayedEventTimerXID =
7257       XtAppAddTimeOut(appContext, millisec,
7258                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7259 }
7260
7261 DelayedEventCallback
7262 GetDelayedEvent()
7263 {
7264   if (delayedEventTimerXID) {
7265     return delayedEventCallback;
7266   } else {
7267     return NULL;
7268   }
7269 }
7270
7271 void
7272 CancelDelayedEvent()
7273 {
7274   if (delayedEventTimerXID) {
7275     XtRemoveTimeOut(delayedEventTimerXID);
7276     delayedEventTimerXID = 0;
7277   }
7278 }
7279
7280 XtIntervalId loadGameTimerXID = 0;
7281
7282 int LoadGameTimerRunning()
7283 {
7284     return loadGameTimerXID != 0;
7285 }
7286
7287 int StopLoadGameTimer()
7288 {
7289     if (loadGameTimerXID != 0) {
7290         XtRemoveTimeOut(loadGameTimerXID);
7291         loadGameTimerXID = 0;
7292         return TRUE;
7293     } else {
7294         return FALSE;
7295     }
7296 }
7297
7298 void
7299 LoadGameTimerCallback(arg, id)
7300      XtPointer arg;
7301      XtIntervalId *id;
7302 {
7303     loadGameTimerXID = 0;
7304     AutoPlayGameLoop();
7305 }
7306
7307 void
7308 StartLoadGameTimer(millisec)
7309      long millisec;
7310 {
7311     loadGameTimerXID =
7312       XtAppAddTimeOut(appContext, millisec,
7313                       (XtTimerCallbackProc) LoadGameTimerCallback,
7314                       (XtPointer) 0);
7315 }
7316
7317 XtIntervalId analysisClockXID = 0;
7318
7319 void
7320 AnalysisClockCallback(arg, id)
7321      XtPointer arg;
7322      XtIntervalId *id;
7323 {
7324     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7325          || appData.icsEngineAnalyze) { // [DM]
7326         AnalysisPeriodicEvent(0);
7327         StartAnalysisClock();
7328     }
7329 }
7330
7331 void
7332 StartAnalysisClock()
7333 {
7334     analysisClockXID =
7335       XtAppAddTimeOut(appContext, 2000,
7336                       (XtTimerCallbackProc) AnalysisClockCallback,
7337                       (XtPointer) 0);
7338 }
7339
7340 XtIntervalId clockTimerXID = 0;
7341
7342 int ClockTimerRunning()
7343 {
7344     return clockTimerXID != 0;
7345 }
7346
7347 int StopClockTimer()
7348 {
7349     if (clockTimerXID != 0) {
7350         XtRemoveTimeOut(clockTimerXID);
7351         clockTimerXID = 0;
7352         return TRUE;
7353     } else {
7354         return FALSE;
7355     }
7356 }
7357
7358 void
7359 ClockTimerCallback(arg, id)
7360      XtPointer arg;
7361      XtIntervalId *id;
7362 {
7363     clockTimerXID = 0;
7364     DecrementClocks();
7365 }
7366
7367 void
7368 StartClockTimer(millisec)
7369      long millisec;
7370 {
7371     clockTimerXID =
7372       XtAppAddTimeOut(appContext, millisec,
7373                       (XtTimerCallbackProc) ClockTimerCallback,
7374                       (XtPointer) 0);
7375 }
7376
7377 void
7378 DisplayTimerLabel(w, color, timer, highlight)
7379      Widget w;
7380      char *color;
7381      long timer;
7382      int highlight;
7383 {
7384     char buf[MSG_SIZ];
7385     Arg args[16];
7386
7387     /* check for low time warning */
7388     Pixel foregroundOrWarningColor = timerForegroundPixel;
7389
7390     if (timer > 0 &&
7391         appData.lowTimeWarning &&
7392         (timer / 1000) < appData.icsAlarmTime)
7393       foregroundOrWarningColor = lowTimeWarningColor;
7394
7395     if (appData.clockMode) {
7396       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7397       XtSetArg(args[0], XtNlabel, buf);
7398     } else {
7399       snprintf(buf, MSG_SIZ, "%s  ", color);
7400       XtSetArg(args[0], XtNlabel, buf);
7401     }
7402
7403     if (highlight) {
7404
7405         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7406         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7407     } else {
7408         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7409         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7410     }
7411
7412     XtSetValues(w, args, 3);
7413 }
7414
7415 void
7416 DisplayWhiteClock(timeRemaining, highlight)
7417      long timeRemaining;
7418      int highlight;
7419 {
7420     Arg args[16];
7421
7422     if(appData.noGUI) return;
7423     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7424     if (highlight && iconPixmap == bIconPixmap) {
7425         iconPixmap = wIconPixmap;
7426         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7427         XtSetValues(shellWidget, args, 1);
7428     }
7429 }
7430
7431 void
7432 DisplayBlackClock(timeRemaining, highlight)
7433      long timeRemaining;
7434      int highlight;
7435 {
7436     Arg args[16];
7437
7438     if(appData.noGUI) return;
7439     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7440     if (highlight && iconPixmap == wIconPixmap) {
7441         iconPixmap = bIconPixmap;
7442         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7443         XtSetValues(shellWidget, args, 1);
7444     }
7445 }
7446
7447 #define CPNone 0
7448 #define CPReal 1
7449 #define CPComm 2
7450 #define CPSock 3
7451 #define CPLoop 4
7452 typedef int CPKind;
7453
7454 typedef struct {
7455     CPKind kind;
7456     int pid;
7457     int fdTo, fdFrom;
7458 } ChildProc;
7459
7460
7461 int StartChildProcess(cmdLine, dir, pr)
7462      char *cmdLine;
7463      char *dir;
7464      ProcRef *pr;
7465 {
7466     char *argv[64], *p;
7467     int i, pid;
7468     int to_prog[2], from_prog[2];
7469     ChildProc *cp;
7470     char buf[MSG_SIZ];
7471
7472     if (appData.debugMode) {
7473         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7474     }
7475
7476     /* We do NOT feed the cmdLine to the shell; we just
7477        parse it into blank-separated arguments in the
7478        most simple-minded way possible.
7479        */
7480     i = 0;
7481     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7482     p = buf;
7483     for (;;) {
7484         while(*p == ' ') p++;
7485         argv[i++] = p;
7486         if(*p == '"' || *p == '\'')
7487              p = strchr(++argv[i-1], *p);
7488         else p = strchr(p, ' ');
7489         if (p == NULL) break;
7490         *p++ = NULLCHAR;
7491     }
7492     argv[i] = NULL;
7493
7494     SetUpChildIO(to_prog, from_prog);
7495
7496     if ((pid = fork()) == 0) {
7497         /* Child process */
7498         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7499         close(to_prog[1]);     // first close the unused pipe ends
7500         close(from_prog[0]);
7501         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7502         dup2(from_prog[1], 1);
7503         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7504         close(from_prog[1]);                   // and closing again loses one of the pipes!
7505         if(fileno(stderr) >= 2) // better safe than sorry...
7506                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7507
7508         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7509             perror(dir);
7510             exit(1);
7511         }
7512
7513         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7514
7515         execvp(argv[0], argv);
7516
7517         /* If we get here, exec failed */
7518         perror(argv[0]);
7519         exit(1);
7520     }
7521
7522     /* Parent process */
7523     close(to_prog[0]);
7524     close(from_prog[1]);
7525
7526     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7527     cp->kind = CPReal;
7528     cp->pid = pid;
7529     cp->fdFrom = from_prog[0];
7530     cp->fdTo = to_prog[1];
7531     *pr = (ProcRef) cp;
7532     return 0;
7533 }
7534
7535 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7536 static RETSIGTYPE AlarmCallBack(int n)
7537 {
7538     return;
7539 }
7540
7541 void
7542 DestroyChildProcess(pr, signalType)
7543      ProcRef pr;
7544      int signalType;
7545 {
7546     ChildProc *cp = (ChildProc *) pr;
7547
7548     if (cp->kind != CPReal) return;
7549     cp->kind = CPNone;
7550     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7551         signal(SIGALRM, AlarmCallBack);
7552         alarm(3);
7553         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7554             kill(cp->pid, SIGKILL); // kill it forcefully
7555             wait((int *) 0);        // and wait again
7556         }
7557     } else {
7558         if (signalType) {
7559             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7560         }
7561         /* Process is exiting either because of the kill or because of
7562            a quit command sent by the backend; either way, wait for it to die.
7563         */
7564         wait((int *) 0);
7565     }
7566     close(cp->fdFrom);
7567     close(cp->fdTo);
7568 }
7569
7570 void
7571 InterruptChildProcess(pr)
7572      ProcRef pr;
7573 {
7574     ChildProc *cp = (ChildProc *) pr;
7575
7576     if (cp->kind != CPReal) return;
7577     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7578 }
7579
7580 int OpenTelnet(host, port, pr)
7581      char *host;
7582      char *port;
7583      ProcRef *pr;
7584 {
7585     char cmdLine[MSG_SIZ];
7586
7587     if (port[0] == NULLCHAR) {
7588       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7589     } else {
7590       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7591     }
7592     return StartChildProcess(cmdLine, "", pr);
7593 }
7594
7595 int OpenTCP(host, port, pr)
7596      char *host;
7597      char *port;
7598      ProcRef *pr;
7599 {
7600 #if OMIT_SOCKETS
7601     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7602 #else  /* !OMIT_SOCKETS */
7603     int s;
7604     struct sockaddr_in sa;
7605     struct hostent     *hp;
7606     unsigned short uport;
7607     ChildProc *cp;
7608
7609     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7610         return errno;
7611     }
7612
7613     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7614     sa.sin_family = AF_INET;
7615     sa.sin_addr.s_addr = INADDR_ANY;
7616     uport = (unsigned short) 0;
7617     sa.sin_port = htons(uport);
7618     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7619         return errno;
7620     }
7621
7622     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7623     if (!(hp = gethostbyname(host))) {
7624         int b0, b1, b2, b3;
7625         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7626             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7627             hp->h_addrtype = AF_INET;
7628             hp->h_length = 4;
7629             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7630             hp->h_addr_list[0] = (char *) malloc(4);
7631             hp->h_addr_list[0][0] = b0;
7632             hp->h_addr_list[0][1] = b1;
7633             hp->h_addr_list[0][2] = b2;
7634             hp->h_addr_list[0][3] = b3;
7635         } else {
7636             return ENOENT;
7637         }
7638     }
7639     sa.sin_family = hp->h_addrtype;
7640     uport = (unsigned short) atoi(port);
7641     sa.sin_port = htons(uport);
7642     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7643
7644     if (connect(s, (struct sockaddr *) &sa,
7645                 sizeof(struct sockaddr_in)) < 0) {
7646         return errno;
7647     }
7648
7649     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7650     cp->kind = CPSock;
7651     cp->pid = 0;
7652     cp->fdFrom = s;
7653     cp->fdTo = s;
7654     *pr = (ProcRef) cp;
7655
7656 #endif /* !OMIT_SOCKETS */
7657
7658     return 0;
7659 }
7660
7661 int OpenCommPort(name, pr)
7662      char *name;
7663      ProcRef *pr;
7664 {
7665     int fd;
7666     ChildProc *cp;
7667
7668     fd = open(name, 2, 0);
7669     if (fd < 0) return errno;
7670
7671     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7672     cp->kind = CPComm;
7673     cp->pid = 0;
7674     cp->fdFrom = fd;
7675     cp->fdTo = fd;
7676     *pr = (ProcRef) cp;
7677
7678     return 0;
7679 }
7680
7681 int OpenLoopback(pr)
7682      ProcRef *pr;
7683 {
7684     ChildProc *cp;
7685     int to[2], from[2];
7686
7687     SetUpChildIO(to, from);
7688
7689     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7690     cp->kind = CPLoop;
7691     cp->pid = 0;
7692     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7693     cp->fdTo = to[1];
7694     *pr = (ProcRef) cp;
7695
7696     return 0;
7697 }
7698
7699 int OpenRcmd(host, user, cmd, pr)
7700      char *host, *user, *cmd;
7701      ProcRef *pr;
7702 {
7703     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7704     return -1;
7705 }
7706
7707 #define INPUT_SOURCE_BUF_SIZE 8192
7708
7709 typedef struct {
7710     CPKind kind;
7711     int fd;
7712     int lineByLine;
7713     char *unused;
7714     InputCallback func;
7715     XtInputId xid;
7716     char buf[INPUT_SOURCE_BUF_SIZE];
7717     VOIDSTAR closure;
7718 } InputSource;
7719
7720 void
7721 DoInputCallback(closure, source, xid)
7722      caddr_t closure;
7723      int *source;
7724      XtInputId *xid;
7725 {
7726     InputSource *is = (InputSource *) closure;
7727     int count;
7728     int error;
7729     char *p, *q;
7730
7731     if (is->lineByLine) {
7732         count = read(is->fd, is->unused,
7733                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7734         if (count <= 0) {
7735             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7736             return;
7737         }
7738         is->unused += count;
7739         p = is->buf;
7740         while (p < is->unused) {
7741             q = memchr(p, '\n', is->unused - p);
7742             if (q == NULL) break;
7743             q++;
7744             (is->func)(is, is->closure, p, q - p, 0);
7745             p = q;
7746         }
7747         q = is->buf;
7748         while (p < is->unused) {
7749             *q++ = *p++;
7750         }
7751         is->unused = q;
7752     } else {
7753         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7754         if (count == -1)
7755           error = errno;
7756         else
7757           error = 0;
7758         (is->func)(is, is->closure, is->buf, count, error);
7759     }
7760 }
7761
7762 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7763      ProcRef pr;
7764      int lineByLine;
7765      InputCallback func;
7766      VOIDSTAR closure;
7767 {
7768     InputSource *is;
7769     ChildProc *cp = (ChildProc *) pr;
7770
7771     is = (InputSource *) calloc(1, sizeof(InputSource));
7772     is->lineByLine = lineByLine;
7773     is->func = func;
7774     if (pr == NoProc) {
7775         is->kind = CPReal;
7776         is->fd = fileno(stdin);
7777     } else {
7778         is->kind = cp->kind;
7779         is->fd = cp->fdFrom;
7780     }
7781     if (lineByLine) {
7782         is->unused = is->buf;
7783     }
7784
7785     is->xid = XtAppAddInput(appContext, is->fd,
7786                             (XtPointer) (XtInputReadMask),
7787                             (XtInputCallbackProc) DoInputCallback,
7788                             (XtPointer) is);
7789     is->closure = closure;
7790     return (InputSourceRef) is;
7791 }
7792
7793 void
7794 RemoveInputSource(isr)
7795      InputSourceRef isr;
7796 {
7797     InputSource *is = (InputSource *) isr;
7798
7799     if (is->xid == 0) return;
7800     XtRemoveInput(is->xid);
7801     is->xid = 0;
7802 }
7803
7804 int OutputToProcess(pr, message, count, outError)
7805      ProcRef pr;
7806      char *message;
7807      int count;
7808      int *outError;
7809 {
7810     static int line = 0;
7811     ChildProc *cp = (ChildProc *) pr;
7812     int outCount;
7813
7814     if (pr == NoProc)
7815     {
7816         if (appData.noJoin || !appData.useInternalWrap)
7817             outCount = fwrite(message, 1, count, stdout);
7818         else
7819         {
7820             int width = get_term_width();
7821             int len = wrap(NULL, message, count, width, &line);
7822             char *msg = malloc(len);
7823             int dbgchk;
7824
7825             if (!msg)
7826                 outCount = fwrite(message, 1, count, stdout);
7827             else
7828             {
7829                 dbgchk = wrap(msg, message, count, width, &line);
7830                 if (dbgchk != len && appData.debugMode)
7831                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7832                 outCount = fwrite(msg, 1, dbgchk, stdout);
7833                 free(msg);
7834             }
7835         }
7836     }
7837     else
7838       outCount = write(cp->fdTo, message, count);
7839
7840     if (outCount == -1)
7841       *outError = errno;
7842     else
7843       *outError = 0;
7844
7845     return outCount;
7846 }
7847
7848 /* Output message to process, with "ms" milliseconds of delay
7849    between each character. This is needed when sending the logon
7850    script to ICC, which for some reason doesn't like the
7851    instantaneous send. */
7852 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7853      ProcRef pr;
7854      char *message;
7855      int count;
7856      int *outError;
7857      long msdelay;
7858 {
7859     ChildProc *cp = (ChildProc *) pr;
7860     int outCount = 0;
7861     int r;
7862
7863     while (count--) {
7864         r = write(cp->fdTo, message++, 1);
7865         if (r == -1) {
7866             *outError = errno;
7867             return outCount;
7868         }
7869         ++outCount;
7870         if (msdelay >= 0)
7871           TimeDelay(msdelay);
7872     }
7873
7874     return outCount;
7875 }
7876
7877 /****   Animation code by Hugh Fisher, DCS, ANU.
7878
7879         Known problem: if a window overlapping the board is
7880         moved away while a piece is being animated underneath,
7881         the newly exposed area won't be updated properly.
7882         I can live with this.
7883
7884         Known problem: if you look carefully at the animation
7885         of pieces in mono mode, they are being drawn as solid
7886         shapes without interior detail while moving. Fixing
7887         this would be a major complication for minimal return.
7888 ****/
7889
7890 /*      Masks for XPM pieces. Black and white pieces can have
7891         different shapes, but in the interest of retaining my
7892         sanity pieces must have the same outline on both light
7893         and dark squares, and all pieces must use the same
7894         background square colors/images.                */
7895
7896 static int xpmDone = 0;
7897
7898 static void
7899 CreateAnimMasks (pieceDepth)
7900      int pieceDepth;
7901 {
7902   ChessSquare   piece;
7903   Pixmap        buf;
7904   GC            bufGC, maskGC;
7905   int           kind, n;
7906   unsigned long plane;
7907   XGCValues     values;
7908
7909   /* Need a bitmap just to get a GC with right depth */
7910   buf = XCreatePixmap(xDisplay, xBoardWindow,
7911                         8, 8, 1);
7912   values.foreground = 1;
7913   values.background = 0;
7914   /* Don't use XtGetGC, not read only */
7915   maskGC = XCreateGC(xDisplay, buf,
7916                     GCForeground | GCBackground, &values);
7917   XFreePixmap(xDisplay, buf);
7918
7919   buf = XCreatePixmap(xDisplay, xBoardWindow,
7920                       squareSize, squareSize, pieceDepth);
7921   values.foreground = XBlackPixel(xDisplay, xScreen);
7922   values.background = XWhitePixel(xDisplay, xScreen);
7923   bufGC = XCreateGC(xDisplay, buf,
7924                     GCForeground | GCBackground, &values);
7925
7926   for (piece = WhitePawn; piece <= BlackKing; piece++) {
7927     /* Begin with empty mask */
7928     if(!xpmDone) // [HGM] pieces: keep using existing
7929     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7930                                  squareSize, squareSize, 1);
7931     XSetFunction(xDisplay, maskGC, GXclear);
7932     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7933                    0, 0, squareSize, squareSize);
7934
7935     /* Take a copy of the piece */
7936     if (White(piece))
7937       kind = 0;
7938     else
7939       kind = 2;
7940     XSetFunction(xDisplay, bufGC, GXcopy);
7941     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
7942               buf, bufGC,
7943               0, 0, squareSize, squareSize, 0, 0);
7944
7945     /* XOR the background (light) over the piece */
7946     XSetFunction(xDisplay, bufGC, GXxor);
7947     if (useImageSqs)
7948       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7949                 0, 0, squareSize, squareSize, 0, 0);
7950     else {
7951       XSetForeground(xDisplay, bufGC, lightSquareColor);
7952       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7953     }
7954
7955     /* We now have an inverted piece image with the background
7956        erased. Construct mask by just selecting all the non-zero
7957        pixels - no need to reconstruct the original image.      */
7958     XSetFunction(xDisplay, maskGC, GXor);
7959     plane = 1;
7960     /* Might be quicker to download an XImage and create bitmap
7961        data from it rather than this N copies per piece, but it
7962        only takes a fraction of a second and there is a much
7963        longer delay for loading the pieces.             */
7964     for (n = 0; n < pieceDepth; n ++) {
7965       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7966                  0, 0, squareSize, squareSize,
7967                  0, 0, plane);
7968       plane = plane << 1;
7969     }
7970   }
7971   /* Clean up */
7972   XFreePixmap(xDisplay, buf);
7973   XFreeGC(xDisplay, bufGC);
7974   XFreeGC(xDisplay, maskGC);
7975 }
7976
7977 static void
7978 InitAnimState (anim, info)
7979   AnimState * anim;
7980   XWindowAttributes * info;
7981 {
7982   XtGCMask  mask;
7983   XGCValues values;
7984
7985   /* Each buffer is square size, same depth as window */
7986   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7987                         squareSize, squareSize, info->depth);
7988   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7989                         squareSize, squareSize, info->depth);
7990
7991   /* Create a plain GC for blitting */
7992   mask = GCForeground | GCBackground | GCFunction |
7993          GCPlaneMask | GCGraphicsExposures;
7994   values.foreground = XBlackPixel(xDisplay, xScreen);
7995   values.background = XWhitePixel(xDisplay, xScreen);
7996   values.function   = GXcopy;
7997   values.plane_mask = AllPlanes;
7998   values.graphics_exposures = False;
7999   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8000
8001   /* Piece will be copied from an existing context at
8002      the start of each new animation/drag. */
8003   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8004
8005   /* Outline will be a read-only copy of an existing */
8006   anim->outlineGC = None;
8007 }
8008
8009 static void
8010 CreateAnimVars ()
8011 {
8012   XWindowAttributes info;
8013
8014   if (xpmDone && gameInfo.variant == oldVariant) return;
8015   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8016   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8017
8018   InitAnimState(&game, &info);
8019   InitAnimState(&player, &info);
8020
8021   /* For XPM pieces, we need bitmaps to use as masks. */
8022   if (useImages)
8023     CreateAnimMasks(info.depth), xpmDone = 1;
8024 }
8025
8026 #ifndef HAVE_USLEEP
8027
8028 static Boolean frameWaiting;
8029
8030 static RETSIGTYPE FrameAlarm (sig)
8031      int sig;
8032 {
8033   frameWaiting = False;
8034   /* In case System-V style signals.  Needed?? */
8035   signal(SIGALRM, FrameAlarm);
8036 }
8037
8038 static void
8039 FrameDelay (time)
8040      int time;
8041 {
8042   struct itimerval delay;
8043
8044   XSync(xDisplay, False);
8045
8046   if (time > 0) {
8047     frameWaiting = True;
8048     signal(SIGALRM, FrameAlarm);
8049     delay.it_interval.tv_sec =
8050       delay.it_value.tv_sec = time / 1000;
8051     delay.it_interval.tv_usec =
8052       delay.it_value.tv_usec = (time % 1000) * 1000;
8053     setitimer(ITIMER_REAL, &delay, NULL);
8054     while (frameWaiting) pause();
8055     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8056     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8057     setitimer(ITIMER_REAL, &delay, NULL);
8058   }
8059 }
8060
8061 #else
8062
8063 static void
8064 FrameDelay (time)
8065      int time;
8066 {
8067   XSync(xDisplay, False);
8068   if (time > 0)
8069     usleep(time * 1000);
8070 }
8071
8072 #endif
8073
8074 /*      Convert board position to corner of screen rect and color       */
8075
8076 static void
8077 ScreenSquare(column, row, pt, color)
8078      int column; int row; XPoint * pt; int * color;
8079 {
8080   if (flipView) {
8081     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8082     pt->y = lineGap + row * (squareSize + lineGap);
8083   } else {
8084     pt->x = lineGap + column * (squareSize + lineGap);
8085     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8086   }
8087   *color = SquareColor(row, column);
8088 }
8089
8090 /*      Convert window coords to square                 */
8091
8092 static void
8093 BoardSquare(x, y, column, row)
8094      int x; int y; int * column; int * row;
8095 {
8096   *column = EventToSquare(x, BOARD_WIDTH);
8097   if (flipView && *column >= 0)
8098     *column = BOARD_WIDTH - 1 - *column;
8099   *row = EventToSquare(y, BOARD_HEIGHT);
8100   if (!flipView && *row >= 0)
8101     *row = BOARD_HEIGHT - 1 - *row;
8102 }
8103
8104 /*   Utilities  */
8105
8106 #undef Max  /* just in case */
8107 #undef Min
8108 #define Max(a, b) ((a) > (b) ? (a) : (b))
8109 #define Min(a, b) ((a) < (b) ? (a) : (b))
8110
8111 static void
8112 SetRect(rect, x, y, width, height)
8113      XRectangle * rect; int x; int y; int width; int height;
8114 {
8115   rect->x = x;
8116   rect->y = y;
8117   rect->width  = width;
8118   rect->height = height;
8119 }
8120
8121 /*      Test if two frames overlap. If they do, return
8122         intersection rect within old and location of
8123         that rect within new. */
8124
8125 static Boolean
8126 Intersect(old, new, size, area, pt)
8127      XPoint * old; XPoint * new;
8128      int size; XRectangle * area; XPoint * pt;
8129 {
8130   if (old->x > new->x + size || new->x > old->x + size ||
8131       old->y > new->y + size || new->y > old->y + size) {
8132     return False;
8133   } else {
8134     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8135             size - abs(old->x - new->x), size - abs(old->y - new->y));
8136     pt->x = Max(old->x - new->x, 0);
8137     pt->y = Max(old->y - new->y, 0);
8138     return True;
8139   }
8140 }
8141
8142 /*      For two overlapping frames, return the rect(s)
8143         in the old that do not intersect with the new.   */
8144
8145 static void
8146 CalcUpdateRects(old, new, size, update, nUpdates)
8147      XPoint * old; XPoint * new; int size;
8148      XRectangle update[]; int * nUpdates;
8149 {
8150   int        count;
8151
8152   /* If old = new (shouldn't happen) then nothing to draw */
8153   if (old->x == new->x && old->y == new->y) {
8154     *nUpdates = 0;
8155     return;
8156   }
8157   /* Work out what bits overlap. Since we know the rects
8158      are the same size we don't need a full intersect calc. */
8159   count = 0;
8160   /* Top or bottom edge? */
8161   if (new->y > old->y) {
8162     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8163     count ++;
8164   } else if (old->y > new->y) {
8165     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8166                               size, old->y - new->y);
8167     count ++;
8168   }
8169   /* Left or right edge - don't overlap any update calculated above. */
8170   if (new->x > old->x) {
8171     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8172                               new->x - old->x, size - abs(new->y - old->y));
8173     count ++;
8174   } else if (old->x > new->x) {
8175     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8176                               old->x - new->x, size - abs(new->y - old->y));
8177     count ++;
8178   }
8179   /* Done */
8180   *nUpdates = count;
8181 }
8182
8183 /*      Generate a series of frame coords from start->mid->finish.
8184         The movement rate doubles until the half way point is
8185         reached, then halves back down to the final destination,
8186         which gives a nice slow in/out effect. The algorithmn
8187         may seem to generate too many intermediates for short
8188         moves, but remember that the purpose is to attract the
8189         viewers attention to the piece about to be moved and
8190         then to where it ends up. Too few frames would be less
8191         noticeable.                                             */
8192
8193 static void
8194 Tween(start, mid, finish, factor, frames, nFrames)
8195      XPoint * start; XPoint * mid;
8196      XPoint * finish; int factor;
8197      XPoint frames[]; int * nFrames;
8198 {
8199   int fraction, n, count;
8200
8201   count = 0;
8202
8203   /* Slow in, stepping 1/16th, then 1/8th, ... */
8204   fraction = 1;
8205   for (n = 0; n < factor; n++)
8206     fraction *= 2;
8207   for (n = 0; n < factor; n++) {
8208     frames[count].x = start->x + (mid->x - start->x) / fraction;
8209     frames[count].y = start->y + (mid->y - start->y) / fraction;
8210     count ++;
8211     fraction = fraction / 2;
8212   }
8213
8214   /* Midpoint */
8215   frames[count] = *mid;
8216   count ++;
8217
8218   /* Slow out, stepping 1/2, then 1/4, ... */
8219   fraction = 2;
8220   for (n = 0; n < factor; n++) {
8221     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8222     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8223     count ++;
8224     fraction = fraction * 2;
8225   }
8226   *nFrames = count;
8227 }
8228
8229 /*      Draw a piece on the screen without disturbing what's there      */
8230
8231 static void
8232 SelectGCMask(piece, clip, outline, mask)
8233      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8234 {
8235   GC source;
8236
8237   /* Bitmap for piece being moved. */
8238   if (appData.monoMode) {
8239       *mask = *pieceToSolid(piece);
8240   } else if (useImages) {
8241 #if HAVE_LIBXPM
8242       *mask = xpmMask[piece];
8243 #else
8244       *mask = ximMaskPm[piece];
8245 #endif
8246   } else {
8247       *mask = *pieceToSolid(piece);
8248   }
8249
8250   /* GC for piece being moved. Square color doesn't matter, but
8251      since it gets modified we make a copy of the original. */
8252   if (White(piece)) {
8253     if (appData.monoMode)
8254       source = bwPieceGC;
8255     else
8256       source = wlPieceGC;
8257   } else {
8258     if (appData.monoMode)
8259       source = wbPieceGC;
8260     else
8261       source = blPieceGC;
8262   }
8263   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8264
8265   /* Outline only used in mono mode and is not modified */
8266   if (White(piece))
8267     *outline = bwPieceGC;
8268   else
8269     *outline = wbPieceGC;
8270 }
8271
8272 static void
8273 OverlayPiece(piece, clip, outline,  dest)
8274      ChessSquare piece; GC clip; GC outline; Drawable dest;
8275 {
8276   int   kind;
8277
8278   if (!useImages) {
8279     /* Draw solid rectangle which will be clipped to shape of piece */
8280     XFillRectangle(xDisplay, dest, clip,
8281                    0, 0, squareSize, squareSize);
8282     if (appData.monoMode)
8283       /* Also draw outline in contrasting color for black
8284          on black / white on white cases                */
8285       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8286                  0, 0, squareSize, squareSize, 0, 0, 1);
8287   } else {
8288     /* Copy the piece */
8289     if (White(piece))
8290       kind = 0;
8291     else
8292       kind = 2;
8293     if(appData.upsideDown && flipView) kind ^= 2;
8294     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8295               dest, clip,
8296               0, 0, squareSize, squareSize,
8297               0, 0);
8298   }
8299 }
8300
8301 /* Animate the movement of a single piece */
8302
8303 static void
8304 BeginAnimation(anim, piece, startColor, start)
8305      AnimState *anim;
8306      ChessSquare piece;
8307      int startColor;
8308      XPoint * start;
8309 {
8310   Pixmap mask;
8311
8312   if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8313   /* The old buffer is initialised with the start square (empty) */
8314   BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8315   anim->prevFrame = *start;
8316
8317   /* The piece will be drawn using its own bitmap as a matte    */
8318   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8319   XSetClipMask(xDisplay, anim->pieceGC, mask);
8320 }
8321
8322 static void
8323 AnimationFrame(anim, frame, piece)
8324      AnimState *anim;
8325      XPoint *frame;
8326      ChessSquare piece;
8327 {
8328   XRectangle updates[4];
8329   XRectangle overlap;
8330   XPoint     pt;
8331   int        count, i;
8332
8333   /* Save what we are about to draw into the new buffer */
8334   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8335             frame->x, frame->y, squareSize, squareSize,
8336             0, 0);
8337
8338   /* Erase bits of the previous frame */
8339   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8340     /* Where the new frame overlapped the previous,
8341        the contents in newBuf are wrong. */
8342     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8343               overlap.x, overlap.y,
8344               overlap.width, overlap.height,
8345               pt.x, pt.y);
8346     /* Repaint the areas in the old that don't overlap new */
8347     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8348     for (i = 0; i < count; i++)
8349       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8350                 updates[i].x - anim->prevFrame.x,
8351                 updates[i].y - anim->prevFrame.y,
8352                 updates[i].width, updates[i].height,
8353                 updates[i].x, updates[i].y);
8354   } else {
8355     /* Easy when no overlap */
8356     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8357                   0, 0, squareSize, squareSize,
8358                   anim->prevFrame.x, anim->prevFrame.y);
8359   }
8360
8361   /* Save this frame for next time round */
8362   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8363                 0, 0, squareSize, squareSize,
8364                 0, 0);
8365   anim->prevFrame = *frame;
8366
8367   /* Draw piece over original screen contents, not current,
8368      and copy entire rect. Wipes out overlapping piece images. */
8369   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8370   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8371                 0, 0, squareSize, squareSize,
8372                 frame->x, frame->y);
8373 }
8374
8375 static void
8376 EndAnimation (anim, finish)
8377      AnimState *anim;
8378      XPoint *finish;
8379 {
8380   XRectangle updates[4];
8381   XRectangle overlap;
8382   XPoint     pt;
8383   int        count, i;
8384
8385   /* The main code will redraw the final square, so we
8386      only need to erase the bits that don't overlap.    */
8387   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8388     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8389     for (i = 0; i < count; i++)
8390       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8391                 updates[i].x - anim->prevFrame.x,
8392                 updates[i].y - anim->prevFrame.y,
8393                 updates[i].width, updates[i].height,
8394                 updates[i].x, updates[i].y);
8395   } else {
8396     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8397                 0, 0, squareSize, squareSize,
8398                 anim->prevFrame.x, anim->prevFrame.y);
8399   }
8400 }
8401
8402 static void
8403 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8404      AnimState *anim;
8405      ChessSquare piece; int startColor;
8406      XPoint * start; XPoint * finish;
8407      XPoint frames[]; int nFrames;
8408 {
8409   int n;
8410
8411   BeginAnimation(anim, piece, startColor, start);
8412   for (n = 0; n < nFrames; n++) {
8413     AnimationFrame(anim, &(frames[n]), piece);
8414     FrameDelay(appData.animSpeed);
8415   }
8416   EndAnimation(anim, finish);
8417 }
8418
8419 void
8420 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8421 {
8422     int i, x, y;
8423     ChessSquare piece = board[fromY][toY];
8424     board[fromY][toY] = EmptySquare;
8425     DrawPosition(FALSE, board);
8426     if (flipView) {
8427         x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8428         y = lineGap + toY * (squareSize + lineGap);
8429     } else {
8430         x = lineGap + toX * (squareSize + lineGap);
8431         y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8432     }
8433     for(i=1; i<4*kFactor; i++) {
8434         int r = squareSize * 9 * i/(20*kFactor - 5);
8435         XFillArc(xDisplay, xBoardWindow, highlineGC,
8436                 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8437         FrameDelay(appData.animSpeed);
8438     }
8439     board[fromY][toY] = piece;
8440 }
8441
8442 /* Main control logic for deciding what to animate and how */
8443
8444 void
8445 AnimateMove(board, fromX, fromY, toX, toY)
8446      Board board;
8447      int fromX;
8448      int fromY;
8449      int toX;
8450      int toY;
8451 {
8452   ChessSquare piece;
8453   int hop;
8454   XPoint      start, finish, mid;
8455   XPoint      frames[kFactor * 2 + 1];
8456   int         nFrames, startColor, endColor;
8457
8458   /* Are we animating? */
8459   if (!appData.animate || appData.blindfold)
8460     return;
8461
8462   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8463      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8464         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8465
8466   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8467   piece = board[fromY][fromX];
8468   if (piece >= EmptySquare) return;
8469
8470 #if DONT_HOP
8471   hop = FALSE;
8472 #else
8473   hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8474 #endif
8475
8476   if (appData.debugMode) {
8477       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8478                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8479              piece, fromX, fromY, toX, toY);  }
8480
8481   ScreenSquare(fromX, fromY, &start, &startColor);
8482   ScreenSquare(toX, toY, &finish, &endColor);
8483
8484   if (hop) {
8485     /* Knight: make straight movement then diagonal */
8486     if (abs(toY - fromY) < abs(toX - fromX)) {
8487        mid.x = start.x + (finish.x - start.x) / 2;
8488        mid.y = start.y;
8489      } else {
8490        mid.x = start.x;
8491        mid.y = start.y + (finish.y - start.y) / 2;
8492      }
8493   } else {
8494     mid.x = start.x + (finish.x - start.x) / 2;
8495     mid.y = start.y + (finish.y - start.y) / 2;
8496   }
8497
8498   /* Don't use as many frames for very short moves */
8499   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8500     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8501   else
8502     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8503   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8504   if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8505     int i,j;
8506     for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8507       if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8508   }
8509
8510   /* Be sure end square is redrawn */
8511   damage[0][toY][toX] = True;
8512 }
8513
8514 void
8515 DragPieceBegin(x, y)
8516      int x; int y;
8517 {
8518     int  boardX, boardY, color;
8519     XPoint corner;
8520
8521     /* Are we animating? */
8522     if (!appData.animateDragging || appData.blindfold)
8523       return;
8524
8525     /* Figure out which square we start in and the
8526        mouse position relative to top left corner. */
8527     BoardSquare(x, y, &boardX, &boardY);
8528     player.startBoardX = boardX;
8529     player.startBoardY = boardY;
8530     ScreenSquare(boardX, boardY, &corner, &color);
8531     player.startSquare  = corner;
8532     player.startColor   = color;
8533     /* As soon as we start dragging, the piece will jump slightly to
8534        be centered over the mouse pointer. */
8535     player.mouseDelta.x = squareSize/2;
8536     player.mouseDelta.y = squareSize/2;
8537     /* Initialise animation */
8538     player.dragPiece = PieceForSquare(boardX, boardY);
8539     /* Sanity check */
8540     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8541         player.dragActive = True;
8542         BeginAnimation(&player, player.dragPiece, color, &corner);
8543         /* Mark this square as needing to be redrawn. Note that
8544            we don't remove the piece though, since logically (ie
8545            as seen by opponent) the move hasn't been made yet. */
8546            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8547               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8548            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8549                      corner.x, corner.y, squareSize, squareSize,
8550                      0, 0); // [HGM] zh: unstack in stead of grab
8551            if(gatingPiece != EmptySquare) {
8552                /* Kludge alert: When gating we want the introduced
8553                   piece to appear on the from square. To generate an
8554                   image of it, we draw it on the board, copy the image,
8555                   and draw the original piece again. */
8556                ChessSquare piece = boards[currentMove][boardY][boardX];
8557                DrawSquare(boardY, boardX, gatingPiece, 0);
8558                XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8559                      corner.x, corner.y, squareSize, squareSize, 0, 0);
8560                DrawSquare(boardY, boardX, piece, 0);
8561            }
8562         damage[0][boardY][boardX] = True;
8563     } else {
8564         player.dragActive = False;
8565     }
8566 }
8567
8568 void
8569 ChangeDragPiece(ChessSquare piece)
8570 {
8571   Pixmap mask;
8572   player.dragPiece = piece;
8573   /* The piece will be drawn using its own bitmap as a matte    */
8574   SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8575   XSetClipMask(xDisplay, player.pieceGC, mask);
8576 }
8577
8578 static void
8579 DragPieceMove(x, y)
8580      int x; int y;
8581 {
8582     XPoint corner;
8583
8584     /* Are we animating? */
8585     if (!appData.animateDragging || appData.blindfold)
8586       return;
8587
8588     /* Sanity check */
8589     if (! player.dragActive)
8590       return;
8591     /* Move piece, maintaining same relative position
8592        of mouse within square    */
8593     corner.x = x - player.mouseDelta.x;
8594     corner.y = y - player.mouseDelta.y;
8595     AnimationFrame(&player, &corner, player.dragPiece);
8596 #if HIGHDRAG*0
8597     if (appData.highlightDragging) {
8598         int boardX, boardY;
8599         BoardSquare(x, y, &boardX, &boardY);
8600         SetHighlights(fromX, fromY, boardX, boardY);
8601     }
8602 #endif
8603 }
8604
8605 void
8606 DragPieceEnd(x, y)
8607      int x; int y;
8608 {
8609     int boardX, boardY, color;
8610     XPoint corner;
8611
8612     /* Are we animating? */
8613     if (!appData.animateDragging || appData.blindfold)
8614       return;
8615
8616     /* Sanity check */
8617     if (! player.dragActive)
8618       return;
8619     /* Last frame in sequence is square piece is
8620        placed on, which may not match mouse exactly. */
8621     BoardSquare(x, y, &boardX, &boardY);
8622     ScreenSquare(boardX, boardY, &corner, &color);
8623     EndAnimation(&player, &corner);
8624
8625     /* Be sure end square is redrawn */
8626     damage[0][boardY][boardX] = True;
8627
8628     /* This prevents weird things happening with fast successive
8629        clicks which on my Sun at least can cause motion events
8630        without corresponding press/release. */
8631     player.dragActive = False;
8632 }
8633
8634 /* Handle expose event while piece being dragged */
8635
8636 static void
8637 DrawDragPiece ()
8638 {
8639   if (!player.dragActive || appData.blindfold)
8640     return;
8641
8642   /* What we're doing: logically, the move hasn't been made yet,
8643      so the piece is still in it's original square. But visually
8644      it's being dragged around the board. So we erase the square
8645      that the piece is on and draw it at the last known drag point. */
8646   BlankSquare(player.startSquare.x, player.startSquare.y,
8647                 player.startColor, EmptySquare, xBoardWindow, 1);
8648   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8649   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8650 }
8651
8652 #include <sys/ioctl.h>
8653 int get_term_width()
8654 {
8655     int fd, default_width;
8656
8657     fd = STDIN_FILENO;
8658     default_width = 79; // this is FICS default anyway...
8659
8660 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8661     struct ttysize win;
8662     if (!ioctl(fd, TIOCGSIZE, &win))
8663         default_width = win.ts_cols;
8664 #elif defined(TIOCGWINSZ)
8665     struct winsize win;
8666     if (!ioctl(fd, TIOCGWINSZ, &win))
8667         default_width = win.ws_col;
8668 #endif
8669     return default_width;
8670 }
8671
8672 void
8673 update_ics_width()
8674 {
8675   static int old_width = 0;
8676   int new_width = get_term_width();
8677
8678   if (old_width != new_width)
8679     ics_printf("set width %d\n", new_width);
8680   old_width = new_width;
8681 }
8682
8683 void NotifyFrontendLogin()
8684 {
8685     update_ics_width();
8686 }
8687
8688 /* [AS] Arrow highlighting support */
8689
8690 static double A_WIDTH = 5; /* Width of arrow body */
8691
8692 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
8693 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
8694
8695 static double Sqr( double x )
8696 {
8697     return x*x;
8698 }
8699
8700 static int Round( double x )
8701 {
8702     return (int) (x + 0.5);
8703 }
8704
8705 void SquareToPos(int rank, int file, int *x, int *y)
8706 {
8707     if (flipView) {
8708         *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8709         *y = lineGap + rank * (squareSize + lineGap);
8710     } else {
8711         *x = lineGap + file * (squareSize + lineGap);
8712         *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8713     }
8714 }
8715
8716 /* Draw an arrow between two points using current settings */
8717 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8718 {
8719     XPoint arrow[7];
8720     double dx, dy, j, k, x, y;
8721
8722     if( d_x == s_x ) {
8723         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8724
8725         arrow[0].x = s_x + A_WIDTH + 0.5;
8726         arrow[0].y = s_y;
8727
8728         arrow[1].x = s_x + A_WIDTH + 0.5;
8729         arrow[1].y = d_y - h;
8730
8731         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8732         arrow[2].y = d_y - h;
8733
8734         arrow[3].x = d_x;
8735         arrow[3].y = d_y;
8736
8737         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8738         arrow[5].y = d_y - h;
8739
8740         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8741         arrow[4].y = d_y - h;
8742
8743         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8744         arrow[6].y = s_y;
8745     }
8746     else if( d_y == s_y ) {
8747         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8748
8749         arrow[0].x = s_x;
8750         arrow[0].y = s_y + A_WIDTH + 0.5;
8751
8752         arrow[1].x = d_x - w;
8753         arrow[1].y = s_y + A_WIDTH + 0.5;
8754
8755         arrow[2].x = d_x - w;
8756         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8757
8758         arrow[3].x = d_x;
8759         arrow[3].y = d_y;
8760
8761         arrow[5].x = d_x - w;
8762         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8763
8764         arrow[4].x = d_x - w;
8765         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8766
8767         arrow[6].x = s_x;
8768         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8769     }
8770     else {
8771         /* [AS] Needed a lot of paper for this! :-) */
8772         dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8773         dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8774
8775         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8776
8777         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8778
8779         x = s_x;
8780         y = s_y;
8781
8782         arrow[0].x = Round(x - j);
8783         arrow[0].y = Round(y + j*dx);
8784
8785         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
8786         arrow[1].y = Round(arrow[0].y - 2*j*dx);
8787
8788         if( d_x > s_x ) {
8789             x = (double) d_x - k;
8790             y = (double) d_y - k*dy;
8791         }
8792         else {
8793             x = (double) d_x + k;
8794             y = (double) d_y + k*dy;
8795         }
8796
8797         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8798
8799         arrow[6].x = Round(x - j);
8800         arrow[6].y = Round(y + j*dx);
8801
8802         arrow[2].x = Round(arrow[6].x + 2*j);
8803         arrow[2].y = Round(arrow[6].y - 2*j*dx);
8804
8805         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8806         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8807
8808         arrow[4].x = d_x;
8809         arrow[4].y = d_y;
8810
8811         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
8812         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
8813     }
8814
8815     XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
8816 //    Polygon( hdc, arrow, 7 );
8817 }
8818
8819 /* [AS] Draw an arrow between two squares */
8820 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
8821 {
8822     int s_x, s_y, d_x, d_y, hor, vert, i;
8823
8824     if( s_col == d_col && s_row == d_row ) {
8825         return;
8826     }
8827
8828     /* Get source and destination points */
8829     SquareToPos( s_row, s_col, &s_x, &s_y);
8830     SquareToPos( d_row, d_col, &d_x, &d_y);
8831
8832     if( d_y > s_y ) {
8833         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
8834     }
8835     else if( d_y < s_y ) {
8836         d_y += squareSize / 2 + squareSize / 4;
8837     }
8838     else {
8839         d_y += squareSize / 2;
8840     }
8841
8842     if( d_x > s_x ) {
8843         d_x += squareSize / 2 - squareSize / 4;
8844     }
8845     else if( d_x < s_x ) {
8846         d_x += squareSize / 2 + squareSize / 4;
8847     }
8848     else {
8849         d_x += squareSize / 2;
8850     }
8851
8852     s_x += squareSize / 2;
8853     s_y += squareSize / 2;
8854
8855     /* Adjust width */
8856     A_WIDTH = squareSize / 14.; //[HGM] make float
8857
8858     DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
8859
8860     hor = 64*s_col + 32; vert = 64*s_row + 32;
8861     for(i=0; i<= 64; i++) {
8862             damage[0][vert+6>>6][hor+6>>6] = True;
8863             damage[0][vert-6>>6][hor+6>>6] = True;
8864             damage[0][vert+6>>6][hor-6>>6] = True;
8865             damage[0][vert-6>>6][hor-6>>6] = True;
8866             hor += d_col - s_col; vert += d_row - s_row;
8867     }
8868 }
8869
8870 Boolean IsDrawArrowEnabled()
8871 {
8872     return appData.highlightMoveWithArrow && squareSize >= 32;
8873 }
8874
8875 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
8876 {
8877     if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
8878         DrawArrowBetweenSquares(fromX, fromY, toX, toY);
8879 }