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