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