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