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