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