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