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