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