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