Dynamic Seek Graph
[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>: PieceMenuPopup(menuB) \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|PointerMotionMask, 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; int menuNr;
3718     if (event->type == ButtonRelease)
3719         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY); 
3720     else if (event->type == ButtonPress)
3721         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3722     switch(menuNr) {
3723       case 0: whichMenu = params[0]; break;
3724       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3725       case 2:
3726       case -1: if (errorUp) ErrorPopDown();
3727       default: return;
3728     }
3729     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3730 }
3731
3732 static void PieceMenuSelect(w, piece, junk)
3733      Widget w;
3734      ChessSquare piece;
3735      caddr_t junk;
3736 {
3737     if (pmFromX < 0 || pmFromY < 0) return;
3738     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3739 }
3740
3741 static void DropMenuSelect(w, piece, junk)
3742      Widget w;
3743      ChessSquare piece;
3744      caddr_t junk;
3745 {
3746     if (pmFromX < 0 || pmFromY < 0) return;
3747     DropMenuEvent(piece, pmFromX, pmFromY);
3748 }
3749
3750 void WhiteClock(w, event, prms, nprms)
3751      Widget w;
3752      XEvent *event;
3753      String *prms;
3754      Cardinal *nprms;
3755 {
3756     if (gameMode == EditPosition || gameMode == IcsExamining) {
3757         SetWhiteToPlayEvent();
3758     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3759         CallFlagEvent();
3760     }
3761 }
3762
3763 void BlackClock(w, event, prms, nprms)
3764      Widget w;
3765      XEvent *event;
3766      String *prms;
3767      Cardinal *nprms;
3768 {
3769     if (gameMode == EditPosition || gameMode == IcsExamining) {
3770         SetBlackToPlayEvent();
3771     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3772         CallFlagEvent();
3773     }
3774 }
3775
3776
3777 /*
3778  * If the user selects on a border boundary, return -1; if off the board,
3779  *   return -2.  Otherwise map the event coordinate to the square.
3780  */
3781 int EventToSquare(x, limit)
3782      int x;
3783 {
3784     if (x <= 0)
3785       return -2;
3786     if (x < lineGap)
3787       return -1;
3788     x -= lineGap;
3789     if ((x % (squareSize + lineGap)) >= squareSize)
3790       return -1;
3791     x /= (squareSize + lineGap);
3792     if (x >= limit)
3793       return -2;
3794     return x;
3795 }
3796
3797 static void do_flash_delay(msec)
3798      unsigned long msec;
3799 {
3800     TimeDelay(msec);
3801 }
3802
3803 static void drawHighlight(file, rank, gc)
3804      int file, rank;
3805      GC gc;
3806 {
3807     int x, y;
3808
3809     if (lineGap == 0 || appData.blindfold) return;
3810
3811     if (flipView) {
3812         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3813           (squareSize + lineGap);
3814         y = lineGap/2 + rank * (squareSize + lineGap);
3815     } else {
3816         x = lineGap/2 + file * (squareSize + lineGap);
3817         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3818           (squareSize + lineGap);
3819     }
3820
3821     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3822                    squareSize+lineGap, squareSize+lineGap);
3823 }
3824
3825 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3826 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3827
3828 void
3829 SetHighlights(fromX, fromY, toX, toY)
3830      int fromX, fromY, toX, toY;
3831 {
3832     if (hi1X != fromX || hi1Y != fromY) {
3833         if (hi1X >= 0 && hi1Y >= 0) {
3834             drawHighlight(hi1X, hi1Y, lineGC);
3835         }
3836     } // [HGM] first erase both, then draw new!
3837     if (hi2X != toX || hi2Y != toY) {
3838         if (hi2X >= 0 && hi2Y >= 0) {
3839             drawHighlight(hi2X, hi2Y, lineGC);
3840         }
3841     }
3842     if (hi1X != fromX || hi1Y != fromY) {
3843         if (fromX >= 0 && fromY >= 0) {
3844             drawHighlight(fromX, fromY, highlineGC);
3845         }
3846     }
3847     if (hi2X != toX || hi2Y != toY) {
3848         if (toX >= 0 && toY >= 0) {
3849             drawHighlight(toX, toY, highlineGC);
3850         }
3851     }
3852     hi1X = fromX;
3853     hi1Y = fromY;
3854     hi2X = toX;
3855     hi2Y = toY;
3856 }
3857
3858 void
3859 ClearHighlights()
3860 {
3861     SetHighlights(-1, -1, -1, -1);
3862 }
3863
3864
3865 void
3866 SetPremoveHighlights(fromX, fromY, toX, toY)
3867      int fromX, fromY, toX, toY;
3868 {
3869     if (pm1X != fromX || pm1Y != fromY) {
3870         if (pm1X >= 0 && pm1Y >= 0) {
3871             drawHighlight(pm1X, pm1Y, lineGC);
3872         }
3873         if (fromX >= 0 && fromY >= 0) {
3874             drawHighlight(fromX, fromY, prelineGC);
3875         }
3876     }
3877     if (pm2X != toX || pm2Y != toY) {
3878         if (pm2X >= 0 && pm2Y >= 0) {
3879             drawHighlight(pm2X, pm2Y, lineGC);
3880         }
3881         if (toX >= 0 && toY >= 0) {
3882             drawHighlight(toX, toY, prelineGC);
3883         }
3884     }
3885     pm1X = fromX;
3886     pm1Y = fromY;
3887     pm2X = toX;
3888     pm2Y = toY;
3889 }
3890
3891 void
3892 ClearPremoveHighlights()
3893 {
3894   SetPremoveHighlights(-1, -1, -1, -1);
3895 }
3896
3897 static void BlankSquare(x, y, color, piece, dest)
3898      int x, y, color;
3899      ChessSquare piece;
3900      Drawable dest;
3901 {
3902     if (useImages && useImageSqs) {
3903         Pixmap pm;
3904         switch (color) {
3905           case 1: /* light */
3906             pm = xpmLightSquare;
3907             break;
3908           case 0: /* dark */
3909             pm = xpmDarkSquare;
3910             break;
3911           case 2: /* neutral */
3912           default:
3913             pm = xpmJailSquare;
3914             break;
3915         }
3916         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3917                   squareSize, squareSize, x, y);
3918     } else {
3919         GC gc;
3920         switch (color) {
3921           case 1: /* light */
3922             gc = lightSquareGC;
3923             break;
3924           case 0: /* dark */
3925             gc = darkSquareGC;
3926             break;
3927           case 2: /* neutral */
3928           default:
3929             gc = jailSquareGC;
3930             break;
3931         }
3932         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
3933     }
3934 }
3935
3936 /*
3937    I split out the routines to draw a piece so that I could
3938    make a generic flash routine.
3939 */
3940 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
3941      ChessSquare piece;
3942      int square_color, x, y;
3943      Drawable dest;
3944 {
3945     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3946     switch (square_color) {
3947       case 1: /* light */
3948       case 2: /* neutral */
3949       default:
3950         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3951                   ? *pieceToOutline(piece)
3952                   : *pieceToSolid(piece),
3953                   dest, bwPieceGC, 0, 0,
3954                   squareSize, squareSize, x, y);
3955         break;
3956       case 0: /* dark */
3957         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3958                   ? *pieceToSolid(piece)
3959                   : *pieceToOutline(piece),
3960                   dest, wbPieceGC, 0, 0,
3961                   squareSize, squareSize, x, y);
3962         break;
3963     }
3964 }
3965
3966 static void monoDrawPiece(piece, square_color, x, y, dest)
3967      ChessSquare piece;
3968      int square_color, x, y;
3969      Drawable dest;
3970 {
3971     switch (square_color) {
3972       case 1: /* light */
3973       case 2: /* neutral */
3974       default:
3975         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3976                    ? *pieceToOutline(piece)
3977                    : *pieceToSolid(piece),
3978                    dest, bwPieceGC, 0, 0,
3979                    squareSize, squareSize, x, y, 1);
3980         break;
3981       case 0: /* dark */
3982         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3983                    ? *pieceToSolid(piece)
3984                    : *pieceToOutline(piece),
3985                    dest, wbPieceGC, 0, 0,
3986                    squareSize, squareSize, x, y, 1);
3987         break;
3988     }
3989 }
3990
3991 static void colorDrawPiece(piece, square_color, x, y, dest)
3992      ChessSquare piece;
3993      int square_color, x, y;
3994      Drawable dest;
3995 {
3996     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3997     switch (square_color) {
3998       case 1: /* light */
3999         XCopyPlane(xDisplay, *pieceToSolid(piece),
4000                    dest, (int) piece < (int) BlackPawn
4001                    ? wlPieceGC : blPieceGC, 0, 0,
4002                    squareSize, squareSize, x, y, 1);
4003         break;
4004       case 0: /* dark */
4005         XCopyPlane(xDisplay, *pieceToSolid(piece),
4006                    dest, (int) piece < (int) BlackPawn
4007                    ? wdPieceGC : bdPieceGC, 0, 0,
4008                    squareSize, squareSize, x, y, 1);
4009         break;
4010       case 2: /* neutral */
4011       default:
4012         XCopyPlane(xDisplay, *pieceToSolid(piece),
4013                    dest, (int) piece < (int) BlackPawn
4014                    ? wjPieceGC : bjPieceGC, 0, 0,
4015                    squareSize, squareSize, x, y, 1);
4016         break;
4017     }
4018 }
4019
4020 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4021      ChessSquare piece;
4022      int square_color, x, y;
4023      Drawable dest;
4024 {
4025     int kind;
4026
4027     switch (square_color) {
4028       case 1: /* light */
4029       case 2: /* neutral */
4030       default:
4031         if ((int)piece < (int) BlackPawn) {
4032             kind = 0;
4033         } else {
4034             kind = 2;
4035             piece -= BlackPawn;
4036         }
4037         break;
4038       case 0: /* dark */
4039         if ((int)piece < (int) BlackPawn) {
4040             kind = 1;
4041         } else {
4042             kind = 3;
4043             piece -= BlackPawn;
4044         }
4045         break;
4046     }
4047     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4048               dest, wlPieceGC, 0, 0,
4049               squareSize, squareSize, x, y);
4050 }
4051
4052 typedef void (*DrawFunc)();
4053
4054 DrawFunc ChooseDrawFunc()
4055 {
4056     if (appData.monoMode) {
4057         if (DefaultDepth(xDisplay, xScreen) == 1) {
4058             return monoDrawPiece_1bit;
4059         } else {
4060             return monoDrawPiece;
4061         }
4062     } else {
4063         if (useImages)
4064           return colorDrawPieceImage;
4065         else
4066           return colorDrawPiece;
4067     }
4068 }
4069
4070 /* [HR] determine square color depending on chess variant. */
4071 static int SquareColor(row, column)
4072      int row, column;
4073 {
4074     int square_color;
4075
4076     if (gameInfo.variant == VariantXiangqi) {
4077         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4078             square_color = 1;
4079         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4080             square_color = 0;
4081         } else if (row <= 4) {
4082             square_color = 0;
4083         } else {
4084             square_color = 1;
4085         }
4086     } else {
4087         square_color = ((column + row) % 2) == 1;
4088     }
4089
4090     /* [hgm] holdings: next line makes all holdings squares light */
4091     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4092
4093     return square_color;
4094 }
4095
4096 void DrawSquare(row, column, piece, do_flash)
4097      int row, column, do_flash;
4098      ChessSquare piece;
4099 {
4100     int square_color, x, y, direction, font_ascent, font_descent;
4101     int i;
4102     char string[2];
4103     XCharStruct overall;
4104     DrawFunc drawfunc;
4105     int flash_delay;
4106
4107     /* Calculate delay in milliseconds (2-delays per complete flash) */
4108     flash_delay = 500 / appData.flashRate;
4109
4110     if (flipView) {
4111         x = lineGap + ((BOARD_WIDTH-1)-column) *
4112           (squareSize + lineGap);
4113         y = lineGap + row * (squareSize + lineGap);
4114     } else {
4115         x = lineGap + column * (squareSize + lineGap);
4116         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4117           (squareSize + lineGap);
4118     }
4119
4120     square_color = SquareColor(row, column);
4121
4122     if ( // [HGM] holdings: blank out area between board and holdings
4123                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4124               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4125                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4126                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4127
4128                         // [HGM] print piece counts next to holdings
4129                         string[1] = NULLCHAR;
4130                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4131                             string[0] = '0' + piece;
4132                             XTextExtents(countFontStruct, string, 1, &direction,
4133                                  &font_ascent, &font_descent, &overall);
4134                             if (appData.monoMode) {
4135                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4136                                                  x + squareSize - overall.width - 2,
4137                                                  y + font_ascent + 1, string, 1);
4138                             } else {
4139                                 XDrawString(xDisplay, xBoardWindow, countGC,
4140                                             x + squareSize - overall.width - 2,
4141                                             y + font_ascent + 1, string, 1);
4142                             }
4143                         }
4144                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4145                             string[0] = '0' + piece;
4146                             XTextExtents(countFontStruct, string, 1, &direction,
4147                                          &font_ascent, &font_descent, &overall);
4148                             if (appData.monoMode) {
4149                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4150                                                  x + 2, y + font_ascent + 1, string, 1);
4151                             } else {
4152                                 XDrawString(xDisplay, xBoardWindow, countGC,
4153                                             x + 2, y + font_ascent + 1, string, 1);
4154                             }
4155                         }
4156     } else {
4157             if (piece == EmptySquare || appData.blindfold) {
4158                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4159             } else {
4160                         drawfunc = ChooseDrawFunc();
4161                         if (do_flash && appData.flashCount > 0) {
4162                             for (i=0; i<appData.flashCount; ++i) {
4163
4164                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4165                                         XSync(xDisplay, False);
4166                                         do_flash_delay(flash_delay);
4167
4168                                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4169                                         XSync(xDisplay, False);
4170                                         do_flash_delay(flash_delay);
4171                             }
4172                         }
4173                         drawfunc(piece, square_color, x, y, xBoardWindow);
4174         }
4175         }
4176
4177     string[1] = NULLCHAR;
4178     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4179                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4180         string[0] = 'a' + column - BOARD_LEFT;
4181         XTextExtents(coordFontStruct, string, 1, &direction,
4182                      &font_ascent, &font_descent, &overall);
4183         if (appData.monoMode) {
4184             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4185                              x + squareSize - overall.width - 2,
4186                              y + squareSize - font_descent - 1, string, 1);
4187         } else {
4188             XDrawString(xDisplay, xBoardWindow, coordGC,
4189                         x + squareSize - overall.width - 2,
4190                         y + squareSize - font_descent - 1, string, 1);
4191         }
4192     }
4193     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4194         string[0] = ONE + row;
4195         XTextExtents(coordFontStruct, string, 1, &direction,
4196                      &font_ascent, &font_descent, &overall);
4197         if (appData.monoMode) {
4198             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4199                              x + 2, y + font_ascent + 1, string, 1);
4200         } else {
4201             XDrawString(xDisplay, xBoardWindow, coordGC,
4202                         x + 2, y + font_ascent + 1, string, 1);
4203         }
4204     }
4205     if(marker[row][column]) {
4206         XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC, 
4207                 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4208     }
4209 }
4210
4211
4212 /* Why is this needed on some versions of X? */
4213 void EventProc(widget, unused, event)
4214      Widget widget;
4215      caddr_t unused;
4216      XEvent *event;
4217 {
4218     if (!XtIsRealized(widget))
4219       return;
4220
4221     switch (event->type) {
4222       case Expose:
4223         if (event->xexpose.count > 0) return;  /* no clipping is done */
4224         XDrawPosition(widget, True, NULL);
4225         break;
4226       case MotionNotify:
4227         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, TRUE)) break;\r
4228       default:
4229         return;
4230     }
4231 }
4232 /* end why */
4233
4234 void DrawPosition(fullRedraw, board)
4235      /*Boolean*/int fullRedraw;
4236      Board board;
4237 {
4238     XDrawPosition(boardWidget, fullRedraw, board);
4239 }
4240
4241 /* Returns 1 if there are "too many" differences between b1 and b2
4242    (i.e. more than 1 move was made) */
4243 static int too_many_diffs(b1, b2)
4244      Board b1, b2;
4245 {
4246     int i, j;
4247     int c = 0;
4248
4249     for (i=0; i<BOARD_HEIGHT; ++i) {
4250         for (j=0; j<BOARD_WIDTH; ++j) {
4251             if (b1[i][j] != b2[i][j]) {
4252                 if (++c > 4)    /* Castling causes 4 diffs */
4253                   return 1;
4254             }
4255         }
4256     }
4257
4258     return 0;
4259 }
4260
4261 /* Matrix describing castling maneuvers */
4262 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4263 static int castling_matrix[4][5] = {
4264     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4265     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4266     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4267     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4268 };
4269
4270 /* Checks whether castling occurred. If it did, *rrow and *rcol
4271    are set to the destination (row,col) of the rook that moved.
4272
4273    Returns 1 if castling occurred, 0 if not.
4274
4275    Note: Only handles a max of 1 castling move, so be sure
4276    to call too_many_diffs() first.
4277    */
4278 static int check_castle_draw(newb, oldb, rrow, rcol)
4279      Board newb, oldb;
4280      int *rrow, *rcol;
4281 {
4282     int i, *r, j;
4283     int match;
4284
4285     /* For each type of castling... */
4286     for (i=0; i<4; ++i) {
4287         r = castling_matrix[i];
4288
4289         /* Check the 4 squares involved in the castling move */
4290         match = 0;
4291         for (j=1; j<=4; ++j) {
4292             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4293                 match = 1;
4294                 break;
4295             }
4296         }
4297
4298         if (!match) {
4299             /* All 4 changed, so it must be a castling move */
4300             *rrow = r[0];
4301             *rcol = r[3];
4302             return 1;
4303         }
4304     }
4305     return 0;
4306 }
4307
4308 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph 
4309 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4310 {
4311       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4312 }
4313
4314 void DrawSeekBackground( int left, int top, int right, int bottom )
4315 {
4316     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4317 }
4318
4319 void DrawSeekText(char *buf, int x, int y)
4320 {
4321     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4322 }
4323
4324 void DrawSeekDot(int x, int y, int color)
4325 {
4326         XFillArc(xDisplay, xBoardWindow, color == 0 ? prelineGC : color == 1 ? darkSquareGC : highlineGC, 
4327                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4328 }
4329
4330 static int damage[BOARD_RANKS][BOARD_FILES];
4331
4332 /*
4333  * event handler for redrawing the board
4334  */
4335 void XDrawPosition(w, repaint, board)
4336      Widget w;
4337      /*Boolean*/int repaint;
4338      Board board;
4339 {
4340     int i, j, do_flash;
4341     static int lastFlipView = 0;
4342     static int lastBoardValid = 0;
4343     static Board lastBoard;
4344     Arg args[16];
4345     int rrow, rcol;
4346
4347     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4348
4349     if (board == NULL) {
4350         if (!lastBoardValid) return;
4351         board = lastBoard;
4352     }
4353     if (!lastBoardValid || lastFlipView != flipView) {
4354         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4355         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
4356                     args, 1);
4357     }
4358
4359     /*
4360      * It would be simpler to clear the window with XClearWindow()
4361      * but this causes a very distracting flicker.
4362      */
4363
4364     if (!repaint && lastBoardValid && lastFlipView == flipView) {
4365
4366         /* If too much changes (begin observing new game, etc.), don't
4367            do flashing */
4368         do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
4369
4370         /* Special check for castling so we don't flash both the king
4371            and the rook (just flash the king). */
4372         if (do_flash) {
4373             if (check_castle_draw(board, lastBoard, &rrow, &rcol)) {
4374                 /* Draw rook with NO flashing. King will be drawn flashing later */
4375                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4376                 lastBoard[rrow][rcol] = board[rrow][rcol];
4377             }
4378         }
4379
4380         /* First pass -- Draw (newly) empty squares and repair damage.
4381            This prevents you from having a piece show up twice while it
4382            is flashing on its new square */
4383         for (i = 0; i < BOARD_HEIGHT; i++)
4384           for (j = 0; j < BOARD_WIDTH; j++)
4385             if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
4386                 || damage[i][j]) {
4387                 DrawSquare(i, j, board[i][j], 0);
4388                 damage[i][j] = False;
4389             }
4390
4391         /* Second pass -- Draw piece(s) in new position and flash them */
4392         for (i = 0; i < BOARD_HEIGHT; i++)
4393           for (j = 0; j < BOARD_WIDTH; j++)
4394             if (board[i][j] != lastBoard[i][j]) {
4395                 DrawSquare(i, j, board[i][j], do_flash);
4396             }
4397     } else {
4398         if (lineGap > 0)
4399           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4400                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4401
4402         for (i = 0; i < BOARD_HEIGHT; i++)
4403           for (j = 0; j < BOARD_WIDTH; j++) {
4404               DrawSquare(i, j, board[i][j], 0);
4405               damage[i][j] = False;
4406           }
4407     }
4408
4409     CopyBoard(lastBoard, board);
4410     lastBoardValid = 1;
4411     lastFlipView = flipView;
4412
4413     /* Draw highlights */
4414     if (pm1X >= 0 && pm1Y >= 0) {
4415       drawHighlight(pm1X, pm1Y, prelineGC);
4416     }
4417     if (pm2X >= 0 && pm2Y >= 0) {
4418       drawHighlight(pm2X, pm2Y, prelineGC);
4419     }
4420     if (hi1X >= 0 && hi1Y >= 0) {
4421       drawHighlight(hi1X, hi1Y, highlineGC);
4422     }
4423     if (hi2X >= 0 && hi2Y >= 0) {
4424       drawHighlight(hi2X, hi2Y, highlineGC);
4425     }
4426
4427     /* If piece being dragged around board, must redraw that too */
4428     DrawDragPiece();
4429
4430     XSync(xDisplay, False);
4431 }
4432
4433
4434 /*
4435  * event handler for redrawing the board
4436  */
4437 void DrawPositionProc(w, event, prms, nprms)
4438      Widget w;
4439      XEvent *event;
4440      String *prms;
4441      Cardinal *nprms;
4442 {
4443     XDrawPosition(w, True, NULL);
4444 }
4445
4446
4447 /*
4448  * event handler for parsing user moves
4449  */
4450 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4451 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4452 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4453 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
4454 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4455 //       and at the end FinishMove() to perform the move after optional promotion popups.
4456 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4457 void HandleUserMove(w, event, prms, nprms)
4458      Widget w;
4459      XEvent *event;
4460      String *prms;
4461      Cardinal *nprms;
4462 {
4463     if (w != boardWidget || errorExitStatus != -1) return;
4464
4465     if (promotionUp) {
4466         if (event->type == ButtonPress) {
4467             XtPopdown(promotionShell);
4468             XtDestroyWidget(promotionShell);
4469             promotionUp = False;
4470             ClearHighlights();
4471             fromX = fromY = -1;
4472         } else {
4473             return;
4474         }
4475     }
4476
4477     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4478     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
4479     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4480 }
4481
4482 void AnimateUserMove (Widget w, XEvent * event,
4483                       String * params, Cardinal * nParams)
4484 {
4485     DragPieceMove(event->xmotion.x, event->xmotion.y);
4486 }
4487
4488 void HandlePV (Widget w, XEvent * event,
4489                       String * params, Cardinal * nParams)
4490 {   // [HGM] pv: walk PV
4491     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4492 }
4493
4494 Widget CommentCreate(name, text, mutable, callback, lines)
4495      char *name, *text;
4496      int /*Boolean*/ mutable;
4497      XtCallbackProc callback;
4498      int lines;
4499 {
4500     Arg args[16];
4501     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4502     Dimension bw_width;
4503     int j;
4504
4505     j = 0;
4506     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4507     XtGetValues(boardWidget, args, j);
4508
4509     j = 0;
4510     XtSetArg(args[j], XtNresizable, True);  j++;
4511 #if TOPLEVEL
4512     shell =
4513       XtCreatePopupShell(name, topLevelShellWidgetClass,
4514                          shellWidget, args, j);
4515 #else
4516     shell =
4517       XtCreatePopupShell(name, transientShellWidgetClass,
4518                          shellWidget, args, j);
4519 #endif
4520     layout =
4521       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4522                             layoutArgs, XtNumber(layoutArgs));
4523     form =
4524       XtCreateManagedWidget("form", formWidgetClass, layout,
4525                             formArgs, XtNumber(formArgs));
4526
4527     j = 0;
4528     if (mutable) {
4529         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4530         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4531     }
4532     XtSetArg(args[j], XtNstring, text);  j++;
4533     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4534     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4535     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4536     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4537     XtSetArg(args[j], XtNresizable, True);  j++;
4538     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
4539     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4540     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4541     XtSetArg(args[j], XtNautoFill, True);  j++;
4542     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4543     edit =
4544       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4545
4546     if (mutable) {
4547         j = 0;
4548         XtSetArg(args[j], XtNfromVert, edit);  j++;
4549         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4550         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4551         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4552         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4553         b_ok =
4554           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4555         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4556
4557         j = 0;
4558         XtSetArg(args[j], XtNfromVert, edit);  j++;
4559         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4560         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4561         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4562         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4563         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4564         b_cancel =
4565           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4566         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4567
4568         j = 0;
4569         XtSetArg(args[j], XtNfromVert, edit);  j++;
4570         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4571         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4572         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4573         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4574         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4575         b_clear =
4576           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4577         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4578     } else {
4579         j = 0;
4580         XtSetArg(args[j], XtNfromVert, edit);  j++;
4581         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4582         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4583         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4584         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4585         b_close =
4586           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4587         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4588
4589         j = 0;
4590         XtSetArg(args[j], XtNfromVert, edit);  j++;
4591         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4592         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4593         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4594         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4595         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4596         b_edit =
4597           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4598         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4599     }
4600
4601     XtRealizeWidget(shell);
4602
4603     if (commentX == -1) {
4604         int xx, yy;
4605         Window junk;
4606         Dimension pw_height;
4607         Dimension ew_height;
4608
4609         j = 0;
4610         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4611         XtGetValues(edit, args, j);
4612
4613         j = 0;
4614         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4615         XtGetValues(shell, args, j);
4616         commentH = pw_height + (lines - 1) * ew_height;
4617         commentW = bw_width - 16;
4618
4619         XSync(xDisplay, False);
4620 #ifdef NOTDEF
4621         /* This code seems to tickle an X bug if it is executed too soon
4622            after xboard starts up.  The coordinates get transformed as if
4623            the main window was positioned at (0, 0).
4624            */
4625         XtTranslateCoords(shellWidget,
4626                           (bw_width - commentW) / 2, 0 - commentH / 2,
4627                           &commentX, &commentY);
4628 #else  /*!NOTDEF*/
4629         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4630                               RootWindowOfScreen(XtScreen(shellWidget)),
4631                               (bw_width - commentW) / 2, 0 - commentH / 2,
4632                               &xx, &yy, &junk);
4633         commentX = xx;
4634         commentY = yy;
4635 #endif /*!NOTDEF*/
4636         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4637     }
4638
4639     if(wpComment.width > 0) {
4640       commentX = wpComment.x;
4641       commentY = wpComment.y;
4642       commentW = wpComment.width;
4643       commentH = wpComment.height;
4644     }
4645
4646     j = 0;
4647     XtSetArg(args[j], XtNheight, commentH);  j++;
4648     XtSetArg(args[j], XtNwidth, commentW);  j++;
4649     XtSetArg(args[j], XtNx, commentX);  j++;
4650     XtSetArg(args[j], XtNy, commentY);  j++;
4651     XtSetValues(shell, args, j);
4652     XtSetKeyboardFocus(shell, edit);
4653
4654     return shell;
4655 }
4656
4657 /* Used for analysis window and ICS input window */
4658 Widget MiscCreate(name, text, mutable, callback, lines)
4659      char *name, *text;
4660      int /*Boolean*/ mutable;
4661      XtCallbackProc callback;
4662      int lines;
4663 {
4664     Arg args[16];
4665     Widget shell, layout, form, edit;
4666     Position x, y;
4667     Dimension bw_width, pw_height, ew_height, w, h;
4668     int j;
4669     int xx, yy;
4670     Window junk;
4671
4672     j = 0;
4673     XtSetArg(args[j], XtNresizable, True);  j++;
4674 #if TOPLEVEL
4675     shell =
4676       XtCreatePopupShell(name, topLevelShellWidgetClass,
4677                          shellWidget, args, j);
4678 #else
4679     shell =
4680       XtCreatePopupShell(name, transientShellWidgetClass,
4681                          shellWidget, args, j);
4682 #endif
4683     layout =
4684       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4685                             layoutArgs, XtNumber(layoutArgs));
4686     form =
4687       XtCreateManagedWidget("form", formWidgetClass, layout,
4688                             formArgs, XtNumber(formArgs));
4689
4690     j = 0;
4691     if (mutable) {
4692         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4693         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4694     }
4695     XtSetArg(args[j], XtNstring, text);  j++;
4696     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4697     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4698     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4699     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4700     XtSetArg(args[j], XtNresizable, True);  j++;
4701     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4702     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4703     XtSetArg(args[j], XtNautoFill, True);  j++;
4704     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4705     edit =
4706       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4707
4708     XtRealizeWidget(shell);
4709
4710     j = 0;
4711     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4712     XtGetValues(boardWidget, args, j);
4713
4714     j = 0;
4715     XtSetArg(args[j], XtNheight, &ew_height);  j++;
4716     XtGetValues(edit, args, j);
4717
4718     j = 0;
4719     XtSetArg(args[j], XtNheight, &pw_height);  j++;
4720     XtGetValues(shell, args, j);
4721     h = pw_height + (lines - 1) * ew_height;
4722     w = bw_width - 16;
4723
4724     XSync(xDisplay, False);
4725 #ifdef NOTDEF
4726     /* This code seems to tickle an X bug if it is executed too soon
4727        after xboard starts up.  The coordinates get transformed as if
4728        the main window was positioned at (0, 0).
4729     */
4730     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4731 #else  /*!NOTDEF*/
4732     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4733                           RootWindowOfScreen(XtScreen(shellWidget)),
4734                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4735 #endif /*!NOTDEF*/
4736     x = xx;
4737     y = yy;
4738     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4739
4740     j = 0;
4741     XtSetArg(args[j], XtNheight, h);  j++;
4742     XtSetArg(args[j], XtNwidth, w);  j++;
4743     XtSetArg(args[j], XtNx, x);  j++;
4744     XtSetArg(args[j], XtNy, y);  j++;
4745     XtSetValues(shell, args, j);
4746
4747     return shell;
4748 }
4749
4750
4751 static int savedIndex;  /* gross that this is global */
4752
4753 void EditCommentPopUp(index, title, text)
4754      int index;
4755      char *title, *text;
4756 {
4757     Widget edit;
4758     Arg args[16];
4759     int j;
4760
4761     savedIndex = index;
4762     if (text == NULL) text = "";
4763
4764     if (editShell == NULL) {
4765         editShell =
4766           CommentCreate(title, text, True, EditCommentCallback, 4);
4767         XtRealizeWidget(editShell);
4768         CatchDeleteWindow(editShell, "EditCommentPopDown");
4769     } else {
4770         edit = XtNameToWidget(editShell, "*form.text");
4771         j = 0;
4772         XtSetArg(args[j], XtNstring, text); j++;
4773         XtSetValues(edit, args, j);
4774         j = 0;
4775         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4776         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4777         XtSetValues(editShell, args, j);
4778     }
4779
4780     XtPopup(editShell, XtGrabNone);
4781
4782     editUp = True;
4783     j = 0;
4784     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4785     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4786                 args, j);
4787 }
4788
4789 void EditCommentCallback(w, client_data, call_data)
4790      Widget w;
4791      XtPointer client_data, call_data;
4792 {
4793     String name, val;
4794     Arg args[16];
4795     int j;
4796     Widget edit;
4797
4798     j = 0;
4799     XtSetArg(args[j], XtNlabel, &name);  j++;
4800     XtGetValues(w, args, j);
4801
4802     if (strcmp(name, _("ok")) == 0) {
4803         edit = XtNameToWidget(editShell, "*form.text");
4804         j = 0;
4805         XtSetArg(args[j], XtNstring, &val); j++;
4806         XtGetValues(edit, args, j);
4807         ReplaceComment(savedIndex, val);
4808         EditCommentPopDown();
4809     } else if (strcmp(name, _("cancel")) == 0) {
4810         EditCommentPopDown();
4811     } else if (strcmp(name, _("clear")) == 0) {
4812         edit = XtNameToWidget(editShell, "*form.text");
4813         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4814         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4815     }
4816 }
4817
4818 void EditCommentPopDown()
4819 {
4820     Arg args[16];
4821     int j;
4822
4823     if (!editUp) return;
4824     j = 0;
4825     XtSetArg(args[j], XtNx, &commentX); j++;
4826     XtSetArg(args[j], XtNy, &commentY); j++;
4827     XtSetArg(args[j], XtNheight, &commentH); j++;
4828     XtSetArg(args[j], XtNwidth, &commentW); j++;
4829     XtGetValues(editShell, args, j);
4830     XtPopdown(editShell);
4831     editUp = False;
4832     j = 0;
4833     XtSetArg(args[j], XtNleftBitmap, None); j++;
4834     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4835                 args, j);
4836 }
4837
4838 void ICSInputBoxPopUp()
4839 {
4840     Widget edit;
4841     Arg args[16];
4842     int j;
4843     char *title = _("ICS Input");
4844     XtTranslations tr;
4845
4846     if (ICSInputShell == NULL) {
4847         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
4848         tr = XtParseTranslationTable(ICSInputTranslations);
4849         edit = XtNameToWidget(ICSInputShell, "*form.text");
4850         XtOverrideTranslations(edit, tr);
4851         XtRealizeWidget(ICSInputShell);
4852         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
4853
4854     } else {
4855         edit = XtNameToWidget(ICSInputShell, "*form.text");
4856         j = 0;
4857         XtSetArg(args[j], XtNstring, ""); j++;
4858         XtSetValues(edit, args, j);
4859         j = 0;
4860         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4861         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4862         XtSetValues(ICSInputShell, args, j);
4863     }
4864
4865     XtPopup(ICSInputShell, XtGrabNone);
4866     XtSetKeyboardFocus(ICSInputShell, edit);
4867
4868     ICSInputBoxUp = True;
4869     j = 0;
4870     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4871     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4872                 args, j);
4873 }
4874
4875 void ICSInputSendText()
4876 {
4877     Widget edit;
4878     int j;
4879     Arg args[16];
4880     String val;
4881
4882     edit = XtNameToWidget(ICSInputShell, "*form.text");
4883     j = 0;
4884     XtSetArg(args[j], XtNstring, &val); j++;
4885     XtGetValues(edit, args, j);
4886     SendMultiLineToICS(val);
4887     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4888     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4889 }
4890
4891 void ICSInputBoxPopDown()
4892 {
4893     Arg args[16];
4894     int j;
4895
4896     if (!ICSInputBoxUp) return;
4897     j = 0;
4898     XtPopdown(ICSInputShell);
4899     ICSInputBoxUp = False;
4900     j = 0;
4901     XtSetArg(args[j], XtNleftBitmap, None); j++;
4902     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4903                 args, j);
4904 }
4905
4906 void CommentPopUp(title, text)
4907      char *title, *text;
4908 {
4909     Arg args[16];
4910     int j;
4911     Widget edit;
4912
4913     if (commentShell == NULL) {
4914         commentShell =
4915           CommentCreate(title, text, False, CommentCallback, 4);
4916         XtRealizeWidget(commentShell);
4917         CatchDeleteWindow(commentShell, "CommentPopDown");
4918     } else {
4919         edit = XtNameToWidget(commentShell, "*form.text");
4920         j = 0;
4921         XtSetArg(args[j], XtNstring, text); j++;
4922         XtSetValues(edit, args, j);
4923         j = 0;
4924         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4925         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4926         XtSetValues(commentShell, args, j);
4927     }
4928
4929     XtPopup(commentShell, XtGrabNone);
4930     XSync(xDisplay, False);
4931
4932     commentUp = True;
4933 }
4934
4935 void CommentCallback(w, client_data, call_data)
4936      Widget w;
4937      XtPointer client_data, call_data;
4938 {
4939     String name;
4940     Arg args[16];
4941     int j;
4942
4943     j = 0;
4944     XtSetArg(args[j], XtNlabel, &name);  j++;
4945     XtGetValues(w, args, j);
4946
4947     if (strcmp(name, _("close")) == 0) {
4948         CommentPopDown();
4949     } else if (strcmp(name, _("edit")) == 0) {
4950         CommentPopDown();
4951         EditCommentEvent();
4952     }
4953 }
4954
4955
4956 void CommentPopDown()
4957 {
4958     Arg args[16];
4959     int j;
4960
4961     if (!commentUp) return;
4962     j = 0;
4963     XtSetArg(args[j], XtNx, &commentX); j++;
4964     XtSetArg(args[j], XtNy, &commentY); j++;
4965     XtSetArg(args[j], XtNwidth, &commentW); j++;
4966     XtSetArg(args[j], XtNheight, &commentH); j++;
4967     XtGetValues(commentShell, args, j);
4968     XtPopdown(commentShell);
4969     XSync(xDisplay, False);
4970     commentUp = False;
4971 }
4972
4973 void FileNamePopUp(label, def, proc, openMode)
4974      char *label;
4975      char *def;
4976      FileProc proc;
4977      char *openMode;
4978 {
4979     Arg args[16];
4980     Widget popup, layout, dialog, edit;
4981     Window root, child;
4982     int x, y, i;
4983     int win_x, win_y;
4984     unsigned int mask;
4985
4986     fileProc = proc;            /* I can't see a way not */
4987     fileOpenMode = openMode;    /*   to use globals here */
4988
4989     i = 0;
4990     XtSetArg(args[i], XtNresizable, True); i++;
4991     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4992     XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
4993     fileNameShell = popup =
4994       XtCreatePopupShell("File name prompt", transientShellWidgetClass,
4995                          shellWidget, args, i);
4996
4997     layout =
4998       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4999                             layoutArgs, XtNumber(layoutArgs));
5000
5001     i = 0;
5002     XtSetArg(args[i], XtNlabel, label); i++;
5003     XtSetArg(args[i], XtNvalue, def); i++;
5004     XtSetArg(args[i], XtNborderWidth, 0); i++;
5005     dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
5006                                    layout, args, i);
5007
5008     XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
5009     XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
5010                        (XtPointer) dialog);
5011
5012     XtRealizeWidget(popup);
5013     CatchDeleteWindow(popup, "FileNamePopDown");
5014
5015     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
5016                   &x, &y, &win_x, &win_y, &mask);
5017
5018     XtSetArg(args[0], XtNx, x - 10);
5019     XtSetArg(args[1], XtNy, y - 30);
5020     XtSetValues(popup, args, 2);
5021
5022     XtPopup(popup, XtGrabExclusive);
5023     filenameUp = True;
5024
5025     edit = XtNameToWidget(dialog, "*value");
5026     XtSetKeyboardFocus(popup, edit);
5027 }
5028
5029 void FileNamePopDown()
5030 {
5031     if (!filenameUp) return;
5032     XtPopdown(fileNameShell);
5033     XtDestroyWidget(fileNameShell);
5034     filenameUp = False;
5035     ModeHighlight();
5036 }
5037
5038 void FileNameCallback(w, client_data, call_data)
5039      Widget w;
5040      XtPointer client_data, call_data;
5041 {
5042     String name;
5043     Arg args[16];
5044
5045     XtSetArg(args[0], XtNlabel, &name);
5046     XtGetValues(w, args, 1);
5047
5048     if (strcmp(name, _("cancel")) == 0) {
5049         FileNamePopDown();
5050         return;
5051     }
5052
5053     FileNameAction(w, NULL, NULL, NULL);
5054 }
5055
5056 void FileNameAction(w, event, prms, nprms)
5057      Widget w;
5058      XEvent *event;
5059      String *prms;
5060      Cardinal *nprms;
5061 {
5062     char buf[MSG_SIZ];
5063     String name;
5064     FILE *f;
5065     char *p, *fullname;
5066     int index;
5067
5068     name = XawDialogGetValueString(w = XtParent(w));
5069
5070     if ((name != NULL) && (*name != NULLCHAR)) {
5071         strcpy(buf, name);
5072         XtPopdown(w = XtParent(XtParent(w)));
5073         XtDestroyWidget(w);
5074         filenameUp = False;
5075
5076         p = strrchr(buf, ' ');
5077         if (p == NULL) {
5078             index = 0;
5079         } else {
5080             *p++ = NULLCHAR;
5081             index = atoi(p);
5082         }
5083         fullname = ExpandPathName(buf);
5084         if (!fullname) {
5085             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5086         }
5087         else {
5088             f = fopen(fullname, fileOpenMode);
5089             if (f == NULL) {
5090                 DisplayError(_("Failed to open file"), errno);
5091             } else {
5092                 (void) (*fileProc)(f, index, buf);
5093             }
5094         }
5095         ModeHighlight();
5096         return;
5097     }
5098
5099     XtPopdown(w = XtParent(XtParent(w)));
5100     XtDestroyWidget(w);
5101     filenameUp = False;
5102     ModeHighlight();
5103 }
5104
5105 void PromotionPopUp()
5106 {
5107     Arg args[16];
5108     Widget dialog, layout;
5109     Position x, y;
5110     Dimension bw_width, pw_width;
5111     int j;
5112
5113     j = 0;
5114     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5115     XtGetValues(boardWidget, args, j);
5116
5117     j = 0;
5118     XtSetArg(args[j], XtNresizable, True); j++;
5119     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5120     promotionShell =
5121       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5122                          shellWidget, args, j);
5123     layout =
5124       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5125                             layoutArgs, XtNumber(layoutArgs));
5126
5127     j = 0;
5128     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5129     XtSetArg(args[j], XtNborderWidth, 0); j++;
5130     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5131                                    layout, args, j);
5132
5133   if(gameInfo.variant != VariantShogi) {
5134     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5135                        (XtPointer) dialog);
5136     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5137                        (XtPointer) dialog);
5138     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5139                        (XtPointer) dialog);
5140     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5141                        (XtPointer) dialog);
5142     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5143         gameInfo.variant == VariantGiveaway) {
5144       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5145                          (XtPointer) dialog);
5146     }
5147     if(gameInfo.variant == VariantCapablanca || 
5148        gameInfo.variant == VariantGothic || 
5149        gameInfo.variant == VariantCapaRandom) {
5150       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5151                          (XtPointer) dialog);
5152       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5153                          (XtPointer) dialog);
5154     }
5155   } else // [HGM] shogi
5156   {
5157       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5158                          (XtPointer) dialog);
5159       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5160                          (XtPointer) dialog);
5161   }
5162     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5163                        (XtPointer) dialog);
5164
5165     XtRealizeWidget(promotionShell);
5166     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5167
5168     j = 0;
5169     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5170     XtGetValues(promotionShell, args, j);
5171
5172     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5173                       lineGap + squareSize/3 +
5174                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5175                        0 : 6*(squareSize + lineGap)), &x, &y);
5176
5177     j = 0;
5178     XtSetArg(args[j], XtNx, x); j++;
5179     XtSetArg(args[j], XtNy, y); j++;
5180     XtSetValues(promotionShell, args, j);
5181
5182     XtPopup(promotionShell, XtGrabNone);
5183
5184     promotionUp = True;
5185 }
5186
5187 void PromotionPopDown()
5188 {
5189     if (!promotionUp) return;
5190     XtPopdown(promotionShell);
5191     XtDestroyWidget(promotionShell);
5192     promotionUp = False;
5193 }
5194
5195 void PromotionCallback(w, client_data, call_data)
5196      Widget w;
5197      XtPointer client_data, call_data;
5198 {
5199     String name;
5200     Arg args[16];
5201     int promoChar;
5202
5203     XtSetArg(args[0], XtNlabel, &name);
5204     XtGetValues(w, args, 1);
5205
5206     PromotionPopDown();
5207
5208     if (fromX == -1) return;
5209
5210     if (strcmp(name, _("cancel")) == 0) {
5211         fromX = fromY = -1;
5212         ClearHighlights();
5213         return;
5214     } else if (strcmp(name, _("Knight")) == 0) {
5215         promoChar = 'n';
5216     } else if (strcmp(name, _("Promote")) == 0) {
5217         promoChar = '+';
5218     } else if (strcmp(name, _("Defer")) == 0) {
5219         promoChar = '=';
5220     } else {
5221         promoChar = ToLower(name[0]);
5222     }
5223
5224     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5225
5226     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5227     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5228     fromX = fromY = -1;
5229 }
5230
5231
5232 void ErrorCallback(w, client_data, call_data)
5233      Widget w;
5234      XtPointer client_data, call_data;
5235 {
5236     errorUp = False;
5237     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5238     XtDestroyWidget(w);
5239     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5240 }
5241
5242
5243 void ErrorPopDown()
5244 {
5245     if (!errorUp) return;
5246     errorUp = False;
5247     XtPopdown(errorShell);
5248     XtDestroyWidget(errorShell);
5249     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5250 }
5251
5252 void ErrorPopUp(title, label, modal)
5253      char *title, *label;
5254      int modal;
5255 {
5256     Arg args[16];
5257     Widget dialog, layout;
5258     Position x, y;
5259     int xx, yy;
5260     Window junk;
5261     Dimension bw_width, pw_width;
5262     Dimension pw_height;
5263     int i;
5264
5265     i = 0;
5266     XtSetArg(args[i], XtNresizable, True);  i++;
5267     XtSetArg(args[i], XtNtitle, title); i++;
5268     errorShell =
5269       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5270                          shellWidget, args, i);
5271     layout =
5272       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5273                             layoutArgs, XtNumber(layoutArgs));
5274
5275     i = 0;
5276     XtSetArg(args[i], XtNlabel, label); i++;
5277     XtSetArg(args[i], XtNborderWidth, 0); i++;
5278     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5279                                    layout, args, i);
5280
5281     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5282
5283     XtRealizeWidget(errorShell);
5284     CatchDeleteWindow(errorShell, "ErrorPopDown");
5285
5286     i = 0;
5287     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5288     XtGetValues(boardWidget, args, i);
5289     i = 0;
5290     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5291     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5292     XtGetValues(errorShell, args, i);
5293
5294 #ifdef NOTDEF
5295     /* This code seems to tickle an X bug if it is executed too soon
5296        after xboard starts up.  The coordinates get transformed as if
5297        the main window was positioned at (0, 0).
5298        */
5299     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5300                       0 - pw_height + squareSize / 3, &x, &y);
5301 #else
5302     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5303                           RootWindowOfScreen(XtScreen(boardWidget)),
5304                           (bw_width - pw_width) / 2,
5305                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5306     x = xx;
5307     y = yy;
5308 #endif
5309     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5310
5311     i = 0;
5312     XtSetArg(args[i], XtNx, x);  i++;
5313     XtSetArg(args[i], XtNy, y);  i++;
5314     XtSetValues(errorShell, args, i);
5315
5316     errorUp = True;
5317     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5318 }
5319
5320 /* Disable all user input other than deleting the window */
5321 static int frozen = 0;
5322 void FreezeUI()
5323 {
5324   if (frozen) return;
5325   /* Grab by a widget that doesn't accept input */
5326   XtAddGrab(messageWidget, TRUE, FALSE);
5327   frozen = 1;
5328 }
5329
5330 /* Undo a FreezeUI */
5331 void ThawUI()
5332 {
5333   if (!frozen) return;
5334   XtRemoveGrab(messageWidget);
5335   frozen = 0;
5336 }
5337
5338 char *ModeToWidgetName(mode)
5339      GameMode mode;
5340 {
5341     switch (mode) {
5342       case BeginningOfGame:
5343         if (appData.icsActive)
5344           return "menuMode.ICS Client";
5345         else if (appData.noChessProgram ||
5346                  *appData.cmailGameName != NULLCHAR)
5347           return "menuMode.Edit Game";
5348         else
5349           return "menuMode.Machine Black";
5350       case MachinePlaysBlack:
5351         return "menuMode.Machine Black";
5352       case MachinePlaysWhite:
5353         return "menuMode.Machine White";
5354       case AnalyzeMode:
5355         return "menuMode.Analysis Mode";
5356       case AnalyzeFile:
5357         return "menuMode.Analyze File";
5358       case TwoMachinesPlay:
5359         return "menuMode.Two Machines";
5360       case EditGame:
5361         return "menuMode.Edit Game";
5362       case PlayFromGameFile:
5363         return "menuFile.Load Game";
5364       case EditPosition:
5365         return "menuMode.Edit Position";
5366       case Training:
5367         return "menuMode.Training";
5368       case IcsPlayingWhite:
5369       case IcsPlayingBlack:
5370       case IcsObserving:
5371       case IcsIdle:
5372       case IcsExamining:
5373         return "menuMode.ICS Client";
5374       default:
5375       case EndOfGame:
5376         return NULL;
5377     }
5378 }
5379
5380 void ModeHighlight()
5381 {
5382     Arg args[16];
5383     static int oldPausing = FALSE;
5384     static GameMode oldmode = (GameMode) -1;
5385     char *wname;
5386
5387     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5388
5389     if (pausing != oldPausing) {
5390         oldPausing = pausing;
5391         if (pausing) {
5392             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5393         } else {
5394             XtSetArg(args[0], XtNleftBitmap, None);
5395         }
5396         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5397                     args, 1);
5398
5399         if (appData.showButtonBar) {
5400           /* Always toggle, don't set.  Previous code messes up when
5401              invoked while the button is pressed, as releasing it
5402              toggles the state again. */
5403           {
5404             Pixel oldbg, oldfg;
5405             XtSetArg(args[0], XtNbackground, &oldbg);
5406             XtSetArg(args[1], XtNforeground, &oldfg);
5407             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5408                         args, 2);
5409             XtSetArg(args[0], XtNbackground, oldfg);
5410             XtSetArg(args[1], XtNforeground, oldbg);
5411           }
5412           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5413         }
5414     }
5415
5416     wname = ModeToWidgetName(oldmode);
5417     if (wname != NULL) {
5418         XtSetArg(args[0], XtNleftBitmap, None);
5419         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5420     }
5421     wname = ModeToWidgetName(gameMode);
5422     if (wname != NULL) {
5423         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5424         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5425     }
5426     oldmode = gameMode;
5427
5428     /* Maybe all the enables should be handled here, not just this one */
5429     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5430                    gameMode == Training || gameMode == PlayFromGameFile);
5431 }
5432
5433
5434 /*
5435  * Button/menu procedures
5436  */
5437 void ResetProc(w, event, prms, nprms)
5438      Widget w;
5439      XEvent *event;
5440      String *prms;
5441      Cardinal *nprms;
5442 {
5443     ResetGameEvent();
5444 }
5445
5446 int LoadGamePopUp(f, gameNumber, title)
5447      FILE *f;
5448      int gameNumber;
5449      char *title;
5450 {
5451     cmailMsgLoaded = FALSE;
5452     if (gameNumber == 0) {
5453         int error = GameListBuild(f);
5454         if (error) {
5455             DisplayError(_("Cannot build game list"), error);
5456         } else if (!ListEmpty(&gameList) &&
5457                    ((ListGame *) gameList.tailPred)->number > 1) {
5458             GameListPopUp(f, title);
5459             return TRUE;
5460         }
5461         GameListDestroy();
5462         gameNumber = 1;
5463     }
5464     return LoadGame(f, gameNumber, title, FALSE);
5465 }
5466
5467 void LoadGameProc(w, event, prms, nprms)
5468      Widget w;
5469      XEvent *event;
5470      String *prms;
5471      Cardinal *nprms;
5472 {
5473     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5474         Reset(FALSE, TRUE);
5475     }
5476     FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5477 }
5478
5479 void LoadNextGameProc(w, event, prms, nprms)
5480      Widget w;
5481      XEvent *event;
5482      String *prms;
5483      Cardinal *nprms;
5484 {
5485     ReloadGame(1);
5486 }
5487
5488 void LoadPrevGameProc(w, event, prms, nprms)
5489      Widget w;
5490      XEvent *event;
5491      String *prms;
5492      Cardinal *nprms;
5493 {
5494     ReloadGame(-1);
5495 }
5496
5497 void ReloadGameProc(w, event, prms, nprms)
5498      Widget w;
5499      XEvent *event;
5500      String *prms;
5501      Cardinal *nprms;
5502 {
5503     ReloadGame(0);
5504 }
5505
5506 void LoadNextPositionProc(w, event, prms, nprms)
5507      Widget w;
5508      XEvent *event;
5509      String *prms;
5510      Cardinal *nprms;
5511 {
5512     ReloadPosition(1);
5513 }
5514
5515 void LoadPrevPositionProc(w, event, prms, nprms)
5516      Widget w;
5517      XEvent *event;
5518      String *prms;
5519      Cardinal *nprms;
5520 {
5521     ReloadPosition(-1);
5522 }
5523
5524 void ReloadPositionProc(w, event, prms, nprms)
5525      Widget w;
5526      XEvent *event;
5527      String *prms;
5528      Cardinal *nprms;
5529 {
5530     ReloadPosition(0);
5531 }
5532
5533 void LoadPositionProc(w, event, prms, nprms)
5534      Widget w;
5535      XEvent *event;
5536      String *prms;
5537      Cardinal *nprms;
5538 {
5539     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5540         Reset(FALSE, TRUE);
5541     }
5542     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5543 }
5544
5545 void SaveGameProc(w, event, prms, nprms)
5546      Widget w;
5547      XEvent *event;
5548      String *prms;
5549      Cardinal *nprms;
5550 {
5551     FileNamePopUp(_("Save game file name?"),
5552                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5553                   SaveGame, "a");
5554 }
5555
5556 void SavePositionProc(w, event, prms, nprms)
5557      Widget w;
5558      XEvent *event;
5559      String *prms;
5560      Cardinal *nprms;
5561 {
5562     FileNamePopUp(_("Save position file name?"),
5563                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5564                   SavePosition, "a");
5565 }
5566
5567 void ReloadCmailMsgProc(w, event, prms, nprms)
5568      Widget w;
5569      XEvent *event;
5570      String *prms;
5571      Cardinal *nprms;
5572 {
5573     ReloadCmailMsgEvent(FALSE);
5574 }
5575
5576 void MailMoveProc(w, event, prms, nprms)
5577      Widget w;
5578      XEvent *event;
5579      String *prms;
5580      Cardinal *nprms;
5581 {
5582     MailMoveEvent();
5583 }
5584
5585 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5586 char *selected_fen_position=NULL;
5587
5588 Boolean
5589 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5590                  Atom *type_return, XtPointer *value_return,
5591                  unsigned long *length_return, int *format_return)
5592 {
5593   char *selection_tmp;
5594
5595   if (!selected_fen_position) return False; /* should never happen */
5596   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5597     /* note: since no XtSelectionDoneProc was registered, Xt will
5598      * automatically call XtFree on the value returned.  So have to
5599      * make a copy of it allocated with XtMalloc */
5600     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5601     strcpy(selection_tmp, selected_fen_position);
5602
5603     *value_return=selection_tmp;
5604     *length_return=strlen(selection_tmp);
5605     *type_return=*target;
5606     *format_return = 8; /* bits per byte */
5607     return True;
5608   } else if (*target == XA_TARGETS(xDisplay)) {
5609     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5610     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5611     targets_tmp[1] = XA_STRING;
5612     *value_return = targets_tmp;
5613     *type_return = XA_ATOM;
5614     *length_return = 2;
5615     *format_return = 8 * sizeof(Atom);
5616     if (*format_return > 32) {
5617       *length_return *= *format_return / 32;
5618       *format_return = 32;
5619     }
5620     return True;
5621   } else {
5622     return False;
5623   }
5624 }
5625
5626 /* note: when called from menu all parameters are NULL, so no clue what the
5627  * Widget which was clicked on was, or what the click event was
5628  */
5629 void CopyPositionProc(w, event, prms, nprms)
5630   Widget w;
5631   XEvent *event;
5632   String *prms;
5633   Cardinal *nprms;
5634   {
5635     /*
5636      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5637      * have a notion of a position that is selected but not copied.
5638      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5639      */
5640     if(gameMode == EditPosition) EditPositionDone(TRUE);
5641     if (selected_fen_position) free(selected_fen_position);
5642     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5643     if (!selected_fen_position) return;
5644     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5645                    CurrentTime,
5646                    SendPositionSelection,
5647                    NULL/* lose_ownership_proc */ ,
5648                    NULL/* transfer_done_proc */);
5649     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5650                    CurrentTime,
5651                    SendPositionSelection,
5652                    NULL/* lose_ownership_proc */ ,
5653                    NULL/* transfer_done_proc */);
5654   }
5655
5656 /* function called when the data to Paste is ready */
5657 static void
5658 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5659            Atom *type, XtPointer value, unsigned long *len, int *format)
5660 {
5661   char *fenstr=value;
5662   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5663   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5664   EditPositionPasteFEN(fenstr);
5665   XtFree(value);
5666 }
5667
5668 /* called when Paste Position button is pressed,
5669  * all parameters will be NULL */
5670 void PastePositionProc(w, event, prms, nprms)
5671   Widget w;
5672   XEvent *event;
5673   String *prms;
5674   Cardinal *nprms;
5675 {
5676     XtGetSelectionValue(menuBarWidget, 
5677       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5678       /* (XtSelectionCallbackProc) */ PastePositionCB,
5679       NULL, /* client_data passed to PastePositionCB */
5680
5681       /* better to use the time field from the event that triggered the
5682        * call to this function, but that isn't trivial to get
5683        */
5684       CurrentTime
5685     );
5686     return;
5687 }
5688
5689 static Boolean
5690 SendGameSelection(Widget w, Atom *selection, Atom *target,
5691                   Atom *type_return, XtPointer *value_return,
5692                   unsigned long *length_return, int *format_return)
5693 {
5694   char *selection_tmp;
5695
5696   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5697     FILE* f = fopen(gameCopyFilename, "r");
5698     long len;
5699     size_t count;
5700     if (f == NULL) return False;
5701     fseek(f, 0, 2);
5702     len = ftell(f);
5703     rewind(f);
5704     selection_tmp = XtMalloc(len + 1);
5705     count = fread(selection_tmp, 1, len, f);
5706     if (len != count) {
5707       XtFree(selection_tmp);
5708       return False;
5709     }
5710     selection_tmp[len] = NULLCHAR;
5711     *value_return = selection_tmp;
5712     *length_return = len;
5713     *type_return = *target;
5714     *format_return = 8; /* bits per byte */
5715     return True;
5716   } else if (*target == XA_TARGETS(xDisplay)) {
5717     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5718     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5719     targets_tmp[1] = XA_STRING;
5720     *value_return = targets_tmp;
5721     *type_return = XA_ATOM;
5722     *length_return = 2;
5723     *format_return = 8 * sizeof(Atom);
5724     if (*format_return > 32) {
5725       *length_return *= *format_return / 32;
5726       *format_return = 32;
5727     }
5728     return True;
5729   } else {
5730     return False;
5731   }
5732 }
5733
5734 /* note: when called from menu all parameters are NULL, so no clue what the
5735  * Widget which was clicked on was, or what the click event was
5736  */
5737 void CopyGameProc(w, event, prms, nprms)
5738   Widget w;
5739   XEvent *event;
5740   String *prms;
5741   Cardinal *nprms;
5742 {
5743   int ret;
5744
5745   ret = SaveGameToFile(gameCopyFilename, FALSE);
5746   if (!ret) return;
5747
5748   /*
5749    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5750    * have a notion of a game that is selected but not copied.
5751    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5752    */
5753   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5754                  CurrentTime,
5755                  SendGameSelection,
5756                  NULL/* lose_ownership_proc */ ,
5757                  NULL/* transfer_done_proc */);
5758   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5759                  CurrentTime,
5760                  SendGameSelection,
5761                  NULL/* lose_ownership_proc */ ,
5762                  NULL/* transfer_done_proc */);
5763 }
5764
5765 /* function called when the data to Paste is ready */
5766 static void
5767 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5768             Atom *type, XtPointer value, unsigned long *len, int *format)
5769 {
5770   FILE* f;
5771   if (value == NULL || *len == 0) {
5772     return; /* nothing had been selected to copy */
5773   }
5774   f = fopen(gamePasteFilename, "w");
5775   if (f == NULL) {
5776     DisplayError(_("Can't open temp file"), errno);
5777     return;
5778   }
5779   fwrite(value, 1, *len, f);
5780   fclose(f);
5781   XtFree(value);
5782   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5783 }
5784
5785 /* called when Paste Game button is pressed,
5786  * all parameters will be NULL */
5787 void PasteGameProc(w, event, prms, nprms)
5788   Widget w;
5789   XEvent *event;
5790   String *prms;
5791   Cardinal *nprms;
5792 {
5793     XtGetSelectionValue(menuBarWidget,
5794       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5795       /* (XtSelectionCallbackProc) */ PasteGameCB,
5796       NULL, /* client_data passed to PasteGameCB */
5797
5798       /* better to use the time field from the event that triggered the
5799        * call to this function, but that isn't trivial to get
5800        */
5801       CurrentTime
5802     );
5803     return;
5804 }
5805
5806
5807 void AutoSaveGame()
5808 {
5809     SaveGameProc(NULL, NULL, NULL, NULL);
5810 }
5811
5812
5813 void QuitProc(w, event, prms, nprms)
5814      Widget w;
5815      XEvent *event;
5816      String *prms;
5817      Cardinal *nprms;
5818 {
5819     ExitEvent(0);
5820 }
5821
5822 void PauseProc(w, event, prms, nprms)
5823      Widget w;
5824      XEvent *event;
5825      String *prms;
5826      Cardinal *nprms;
5827 {
5828     PauseEvent();
5829 }
5830
5831
5832 void MachineBlackProc(w, event, prms, nprms)
5833      Widget w;
5834      XEvent *event;
5835      String *prms;
5836      Cardinal *nprms;
5837 {
5838     MachineBlackEvent();
5839 }
5840
5841 void MachineWhiteProc(w, event, prms, nprms)
5842      Widget w;
5843      XEvent *event;
5844      String *prms;
5845      Cardinal *nprms;
5846 {
5847     MachineWhiteEvent();
5848 }
5849
5850 void AnalyzeModeProc(w, event, prms, nprms)
5851      Widget w;
5852      XEvent *event;
5853      String *prms;
5854      Cardinal *nprms;
5855 {
5856     char buf[MSG_SIZ];
5857
5858     if (!first.analysisSupport) {
5859       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5860       DisplayError(buf, 0);
5861       return;
5862     }
5863     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5864     if (appData.icsActive) {
5865         if (gameMode != IcsObserving) {
5866             sprintf(buf,_("You are not observing a game"));
5867             DisplayError(buf, 0);
5868             /* secure check */
5869             if (appData.icsEngineAnalyze) {
5870                 if (appData.debugMode)
5871                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5872                 ExitAnalyzeMode();
5873                 ModeHighlight();
5874             }
5875             return;
5876         }
5877         /* if enable, use want disable icsEngineAnalyze */
5878         if (appData.icsEngineAnalyze) {
5879                 ExitAnalyzeMode();
5880                 ModeHighlight();
5881                 return;
5882         }
5883         appData.icsEngineAnalyze = TRUE;
5884         if (appData.debugMode)
5885             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5886     }
5887     if (!appData.showThinking)
5888       ShowThinkingProc(w,event,prms,nprms);
5889
5890     AnalyzeModeEvent();
5891 }
5892
5893 void AnalyzeFileProc(w, event, prms, nprms)
5894      Widget w;
5895      XEvent *event;
5896      String *prms;
5897      Cardinal *nprms;
5898 {
5899     if (!first.analysisSupport) {
5900       char buf[MSG_SIZ];
5901       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5902       DisplayError(buf, 0);
5903       return;
5904     }
5905     Reset(FALSE, TRUE);
5906
5907     if (!appData.showThinking)
5908       ShowThinkingProc(w,event,prms,nprms);
5909
5910     AnalyzeFileEvent();
5911     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
5912     AnalysisPeriodicEvent(1);
5913 }
5914
5915 void TwoMachinesProc(w, event, prms, nprms)
5916      Widget w;
5917      XEvent *event;
5918      String *prms;
5919      Cardinal *nprms;
5920 {
5921     TwoMachinesEvent();
5922 }
5923
5924 void IcsClientProc(w, event, prms, nprms)
5925      Widget w;
5926      XEvent *event;
5927      String *prms;
5928      Cardinal *nprms;
5929 {
5930     IcsClientEvent();
5931 }
5932
5933 void EditGameProc(w, event, prms, nprms)
5934      Widget w;
5935      XEvent *event;
5936      String *prms;
5937      Cardinal *nprms;
5938 {
5939     EditGameEvent();
5940 }
5941
5942 void EditPositionProc(w, event, prms, nprms)
5943      Widget w;
5944      XEvent *event;
5945      String *prms;
5946      Cardinal *nprms;
5947 {
5948     EditPositionEvent();
5949 }
5950
5951 void TrainingProc(w, event, prms, nprms)
5952      Widget w;
5953      XEvent *event;
5954      String *prms;
5955      Cardinal *nprms;
5956 {
5957     TrainingEvent();
5958 }
5959
5960 void EditCommentProc(w, event, prms, nprms)
5961      Widget w;
5962      XEvent *event;
5963      String *prms;
5964      Cardinal *nprms;
5965 {
5966     if (editUp) {
5967         EditCommentPopDown();
5968     } else {
5969         EditCommentEvent();
5970     }
5971 }
5972
5973 void IcsInputBoxProc(w, event, prms, nprms)
5974      Widget w;
5975      XEvent *event;
5976      String *prms;
5977      Cardinal *nprms;
5978 {
5979     if (ICSInputBoxUp) {
5980         ICSInputBoxPopDown();
5981     } else {
5982         ICSInputBoxPopUp();
5983     }
5984 }
5985
5986 void AcceptProc(w, event, prms, nprms)
5987      Widget w;
5988      XEvent *event;
5989      String *prms;
5990      Cardinal *nprms;
5991 {
5992     AcceptEvent();
5993 }
5994
5995 void DeclineProc(w, event, prms, nprms)
5996      Widget w;
5997      XEvent *event;
5998      String *prms;
5999      Cardinal *nprms;
6000 {
6001     DeclineEvent();
6002 }
6003
6004 void RematchProc(w, event, prms, nprms)
6005      Widget w;
6006      XEvent *event;
6007      String *prms;
6008      Cardinal *nprms;
6009 {
6010     RematchEvent();
6011 }
6012
6013 void CallFlagProc(w, event, prms, nprms)
6014      Widget w;
6015      XEvent *event;
6016      String *prms;
6017      Cardinal *nprms;
6018 {
6019     CallFlagEvent();
6020 }
6021
6022 void DrawProc(w, event, prms, nprms)
6023      Widget w;
6024      XEvent *event;
6025      String *prms;
6026      Cardinal *nprms;
6027 {
6028     DrawEvent();
6029 }
6030
6031 void AbortProc(w, event, prms, nprms)
6032      Widget w;
6033      XEvent *event;
6034      String *prms;
6035      Cardinal *nprms;
6036 {
6037     AbortEvent();
6038 }
6039
6040 void AdjournProc(w, event, prms, nprms)
6041      Widget w;
6042      XEvent *event;
6043      String *prms;
6044      Cardinal *nprms;
6045 {
6046     AdjournEvent();
6047 }
6048
6049 void ResignProc(w, event, prms, nprms)
6050      Widget w;
6051      XEvent *event;
6052      String *prms;
6053      Cardinal *nprms;
6054 {
6055     ResignEvent();
6056 }
6057
6058 void AdjuWhiteProc(w, event, prms, nprms)
6059      Widget w;
6060      XEvent *event;
6061      String *prms;
6062      Cardinal *nprms;
6063 {
6064     UserAdjudicationEvent(+1);
6065 }
6066
6067 void AdjuBlackProc(w, event, prms, nprms)
6068      Widget w;
6069      XEvent *event;
6070      String *prms;
6071      Cardinal *nprms;
6072 {
6073     UserAdjudicationEvent(-1);
6074 }
6075
6076 void AdjuDrawProc(w, event, prms, nprms)
6077      Widget w;
6078      XEvent *event;
6079      String *prms;
6080      Cardinal *nprms;
6081 {
6082     UserAdjudicationEvent(0);
6083 }
6084
6085 void EnterKeyProc(w, event, prms, nprms)
6086      Widget w;
6087      XEvent *event;
6088      String *prms;
6089      Cardinal *nprms;
6090 {
6091     if (ICSInputBoxUp == True)
6092       ICSInputSendText();
6093 }
6094
6095 void StopObservingProc(w, event, prms, nprms)
6096      Widget w;
6097      XEvent *event;
6098      String *prms;
6099      Cardinal *nprms;
6100 {
6101     StopObservingEvent();
6102 }
6103
6104 void StopExaminingProc(w, event, prms, nprms)
6105      Widget w;
6106      XEvent *event;
6107      String *prms;
6108      Cardinal *nprms;
6109 {
6110     StopExaminingEvent();
6111 }
6112
6113
6114 void ForwardProc(w, event, prms, nprms)
6115      Widget w;
6116      XEvent *event;
6117      String *prms;
6118      Cardinal *nprms;
6119 {
6120     ForwardEvent();
6121 }
6122
6123
6124 void BackwardProc(w, event, prms, nprms)
6125      Widget w;
6126      XEvent *event;
6127      String *prms;
6128      Cardinal *nprms;
6129 {
6130     BackwardEvent();
6131 }
6132
6133 void ToStartProc(w, event, prms, nprms)
6134      Widget w;
6135      XEvent *event;
6136      String *prms;
6137      Cardinal *nprms;
6138 {
6139     ToStartEvent();
6140 }
6141
6142 void ToEndProc(w, event, prms, nprms)
6143      Widget w;
6144      XEvent *event;
6145      String *prms;
6146      Cardinal *nprms;
6147 {
6148     ToEndEvent();
6149 }
6150
6151 void RevertProc(w, event, prms, nprms)
6152      Widget w;
6153      XEvent *event;
6154      String *prms;
6155      Cardinal *nprms;
6156 {
6157     RevertEvent();
6158 }
6159
6160 void TruncateGameProc(w, event, prms, nprms)
6161      Widget w;
6162      XEvent *event;
6163      String *prms;
6164      Cardinal *nprms;
6165 {
6166     TruncateGameEvent();
6167 }
6168 void RetractMoveProc(w, event, prms, nprms)
6169      Widget w;
6170      XEvent *event;
6171      String *prms;
6172      Cardinal *nprms;
6173 {
6174     RetractMoveEvent();
6175 }
6176
6177 void MoveNowProc(w, event, prms, nprms)
6178      Widget w;
6179      XEvent *event;
6180      String *prms;
6181      Cardinal *nprms;
6182 {
6183     MoveNowEvent();
6184 }
6185
6186
6187 void AlwaysQueenProc(w, event, prms, nprms)
6188      Widget w;
6189      XEvent *event;
6190      String *prms;
6191      Cardinal *nprms;
6192 {
6193     Arg args[16];
6194
6195     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6196
6197     if (appData.alwaysPromoteToQueen) {
6198         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6199     } else {
6200         XtSetArg(args[0], XtNleftBitmap, None);
6201     }
6202     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6203                 args, 1);
6204 }
6205
6206 void AnimateDraggingProc(w, event, prms, nprms)
6207      Widget w;
6208      XEvent *event;
6209      String *prms;
6210      Cardinal *nprms;
6211 {
6212     Arg args[16];
6213
6214     appData.animateDragging = !appData.animateDragging;
6215
6216     if (appData.animateDragging) {
6217         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6218         CreateAnimVars();
6219     } else {
6220         XtSetArg(args[0], XtNleftBitmap, None);
6221     }
6222     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6223                 args, 1);
6224 }
6225
6226 void AnimateMovingProc(w, event, prms, nprms)
6227      Widget w;
6228      XEvent *event;
6229      String *prms;
6230      Cardinal *nprms;
6231 {
6232     Arg args[16];
6233
6234     appData.animate = !appData.animate;
6235
6236     if (appData.animate) {
6237         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6238         CreateAnimVars();
6239     } else {
6240         XtSetArg(args[0], XtNleftBitmap, None);
6241     }
6242     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6243                 args, 1);
6244 }
6245
6246 void AutocommProc(w, event, prms, nprms)
6247      Widget w;
6248      XEvent *event;
6249      String *prms;
6250      Cardinal *nprms;
6251 {
6252     Arg args[16];
6253
6254     appData.autoComment = !appData.autoComment;
6255
6256     if (appData.autoComment) {
6257         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6258     } else {
6259         XtSetArg(args[0], XtNleftBitmap, None);
6260     }
6261     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6262                 args, 1);
6263 }
6264
6265
6266 void AutoflagProc(w, event, prms, nprms)
6267      Widget w;
6268      XEvent *event;
6269      String *prms;
6270      Cardinal *nprms;
6271 {
6272     Arg args[16];
6273
6274     appData.autoCallFlag = !appData.autoCallFlag;
6275
6276     if (appData.autoCallFlag) {
6277         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6278     } else {
6279         XtSetArg(args[0], XtNleftBitmap, None);
6280     }
6281     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6282                 args, 1);
6283 }
6284
6285 void AutoflipProc(w, event, prms, nprms)
6286      Widget w;
6287      XEvent *event;
6288      String *prms;
6289      Cardinal *nprms;
6290 {
6291     Arg args[16];
6292
6293     appData.autoFlipView = !appData.autoFlipView;
6294
6295     if (appData.autoFlipView) {
6296         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6297     } else {
6298         XtSetArg(args[0], XtNleftBitmap, None);
6299     }
6300     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6301                 args, 1);
6302 }
6303
6304 void AutobsProc(w, event, prms, nprms)
6305      Widget w;
6306      XEvent *event;
6307      String *prms;
6308      Cardinal *nprms;
6309 {
6310     Arg args[16];
6311
6312     appData.autoObserve = !appData.autoObserve;
6313
6314     if (appData.autoObserve) {
6315         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6316     } else {
6317         XtSetArg(args[0], XtNleftBitmap, None);
6318     }
6319     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6320                 args, 1);
6321 }
6322
6323 void AutoraiseProc(w, event, prms, nprms)
6324      Widget w;
6325      XEvent *event;
6326      String *prms;
6327      Cardinal *nprms;
6328 {
6329     Arg args[16];
6330
6331     appData.autoRaiseBoard = !appData.autoRaiseBoard;
6332
6333     if (appData.autoRaiseBoard) {
6334         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6335     } else {
6336         XtSetArg(args[0], XtNleftBitmap, None);
6337     }
6338     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
6339                 args, 1);
6340 }
6341
6342 void AutosaveProc(w, event, prms, nprms)
6343      Widget w;
6344      XEvent *event;
6345      String *prms;
6346      Cardinal *nprms;
6347 {
6348     Arg args[16];
6349
6350     appData.autoSaveGames = !appData.autoSaveGames;
6351
6352     if (appData.autoSaveGames) {
6353         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6354     } else {
6355         XtSetArg(args[0], XtNleftBitmap, None);
6356     }
6357     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
6358                 args, 1);
6359 }
6360
6361 void BlindfoldProc(w, event, prms, nprms)
6362      Widget w;
6363      XEvent *event;
6364      String *prms;
6365      Cardinal *nprms;
6366 {
6367     Arg args[16];
6368
6369     appData.blindfold = !appData.blindfold;
6370
6371     if (appData.blindfold) {
6372         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6373     } else {
6374         XtSetArg(args[0], XtNleftBitmap, None);
6375     }
6376     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6377                 args, 1);
6378
6379     DrawPosition(True, NULL);
6380 }
6381
6382 void TestLegalityProc(w, event, prms, nprms)
6383      Widget w;
6384      XEvent *event;
6385      String *prms;
6386      Cardinal *nprms;
6387 {
6388     Arg args[16];
6389
6390     appData.testLegality = !appData.testLegality;
6391
6392     if (appData.testLegality) {
6393         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6394     } else {
6395         XtSetArg(args[0], XtNleftBitmap, None);
6396     }
6397     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6398                 args, 1);
6399 }
6400
6401
6402 void FlashMovesProc(w, event, prms, nprms)
6403      Widget w;
6404      XEvent *event;
6405      String *prms;
6406      Cardinal *nprms;
6407 {
6408     Arg args[16];
6409
6410     if (appData.flashCount == 0) {
6411         appData.flashCount = 3;
6412     } else {
6413         appData.flashCount = -appData.flashCount;
6414     }
6415
6416     if (appData.flashCount > 0) {
6417         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6418     } else {
6419         XtSetArg(args[0], XtNleftBitmap, None);
6420     }
6421     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6422                 args, 1);
6423 }
6424
6425 void FlipViewProc(w, event, prms, nprms)
6426      Widget w;
6427      XEvent *event;
6428      String *prms;
6429      Cardinal *nprms;
6430 {
6431     flipView = !flipView;
6432     DrawPosition(True, NULL);
6433 }
6434
6435 void GetMoveListProc(w, event, prms, nprms)
6436      Widget w;
6437      XEvent *event;
6438      String *prms;
6439      Cardinal *nprms;
6440 {
6441     Arg args[16];
6442
6443     appData.getMoveList = !appData.getMoveList;
6444
6445     if (appData.getMoveList) {
6446         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6447         GetMoveListEvent();
6448     } else {
6449         XtSetArg(args[0], XtNleftBitmap, None);
6450     }
6451     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
6452                 args, 1);
6453 }
6454
6455 #if HIGHDRAG
6456 void HighlightDraggingProc(w, event, prms, nprms)
6457      Widget w;
6458      XEvent *event;
6459      String *prms;
6460      Cardinal *nprms;
6461 {
6462     Arg args[16];
6463
6464     appData.highlightDragging = !appData.highlightDragging;
6465
6466     if (appData.highlightDragging) {
6467         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6468     } else {
6469         XtSetArg(args[0], XtNleftBitmap, None);
6470     }
6471     XtSetValues(XtNameToWidget(menuBarWidget,
6472                                "menuOptions.Highlight Dragging"), args, 1);
6473 }
6474 #endif
6475
6476 void HighlightLastMoveProc(w, event, prms, nprms)
6477      Widget w;
6478      XEvent *event;
6479      String *prms;
6480      Cardinal *nprms;
6481 {
6482     Arg args[16];
6483
6484     appData.highlightLastMove = !appData.highlightLastMove;
6485
6486     if (appData.highlightLastMove) {
6487         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6488     } else {
6489         XtSetArg(args[0], XtNleftBitmap, None);
6490     }
6491     XtSetValues(XtNameToWidget(menuBarWidget,
6492                                "menuOptions.Highlight Last Move"), args, 1);
6493 }
6494
6495 void IcsAlarmProc(w, event, prms, nprms)
6496      Widget w;
6497      XEvent *event;
6498      String *prms;
6499      Cardinal *nprms;
6500 {
6501     Arg args[16];
6502
6503     appData.icsAlarm = !appData.icsAlarm;
6504
6505     if (appData.icsAlarm) {
6506         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6507     } else {
6508         XtSetArg(args[0], XtNleftBitmap, None);
6509     }
6510     XtSetValues(XtNameToWidget(menuBarWidget,
6511                                "menuOptions.ICS Alarm"), args, 1);
6512 }
6513
6514 void MoveSoundProc(w, event, prms, nprms)
6515      Widget w;
6516      XEvent *event;
6517      String *prms;
6518      Cardinal *nprms;
6519 {
6520     Arg args[16];
6521
6522     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6523
6524     if (appData.ringBellAfterMoves) {
6525         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6526     } else {
6527         XtSetArg(args[0], XtNleftBitmap, None);
6528     }
6529     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6530                 args, 1);
6531 }
6532
6533
6534 void OldSaveStyleProc(w, event, prms, nprms)
6535      Widget w;
6536      XEvent *event;
6537      String *prms;
6538      Cardinal *nprms;
6539 {
6540     Arg args[16];
6541
6542     appData.oldSaveStyle = !appData.oldSaveStyle;
6543
6544     if (appData.oldSaveStyle) {
6545         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6546     } else {
6547         XtSetArg(args[0], XtNleftBitmap, None);
6548     }
6549     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
6550                 args, 1);
6551 }
6552
6553 void PeriodicUpdatesProc(w, event, prms, nprms)
6554      Widget w;
6555      XEvent *event;
6556      String *prms;
6557      Cardinal *nprms;
6558 {
6559     Arg args[16];
6560
6561     PeriodicUpdatesEvent(!appData.periodicUpdates);
6562
6563     if (appData.periodicUpdates) {
6564         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6565     } else {
6566         XtSetArg(args[0], XtNleftBitmap, None);
6567     }
6568     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6569                 args, 1);
6570 }
6571
6572 void PonderNextMoveProc(w, event, prms, nprms)
6573      Widget w;
6574      XEvent *event;
6575      String *prms;
6576      Cardinal *nprms;
6577 {
6578     Arg args[16];
6579
6580     PonderNextMoveEvent(!appData.ponderNextMove);
6581
6582     if (appData.ponderNextMove) {
6583         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6584     } else {
6585         XtSetArg(args[0], XtNleftBitmap, None);
6586     }
6587     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6588                 args, 1);
6589 }
6590
6591 void PopupExitMessageProc(w, event, prms, nprms)
6592      Widget w;
6593      XEvent *event;
6594      String *prms;
6595      Cardinal *nprms;
6596 {
6597     Arg args[16];
6598
6599     appData.popupExitMessage = !appData.popupExitMessage;
6600
6601     if (appData.popupExitMessage) {
6602         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6603     } else {
6604         XtSetArg(args[0], XtNleftBitmap, None);
6605     }
6606     XtSetValues(XtNameToWidget(menuBarWidget,
6607                                "menuOptions.Popup Exit Message"), args, 1);
6608 }
6609
6610 void PopupMoveErrorsProc(w, event, prms, nprms)
6611      Widget w;
6612      XEvent *event;
6613      String *prms;
6614      Cardinal *nprms;
6615 {
6616     Arg args[16];
6617
6618     appData.popupMoveErrors = !appData.popupMoveErrors;
6619
6620     if (appData.popupMoveErrors) {
6621         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6622     } else {
6623         XtSetArg(args[0], XtNleftBitmap, None);
6624     }
6625     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6626                 args, 1);
6627 }
6628
6629 void PremoveProc(w, event, prms, nprms)
6630      Widget w;
6631      XEvent *event;
6632      String *prms;
6633      Cardinal *nprms;
6634 {
6635     Arg args[16];
6636
6637     appData.premove = !appData.premove;
6638
6639     if (appData.premove) {
6640         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6641     } else {
6642         XtSetArg(args[0], XtNleftBitmap, None);
6643     }
6644     XtSetValues(XtNameToWidget(menuBarWidget,
6645                                "menuOptions.Premove"), args, 1);
6646 }
6647
6648 void QuietPlayProc(w, event, prms, nprms)
6649      Widget w;
6650      XEvent *event;
6651      String *prms;
6652      Cardinal *nprms;
6653 {
6654     Arg args[16];
6655
6656     appData.quietPlay = !appData.quietPlay;
6657
6658     if (appData.quietPlay) {
6659         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6660     } else {
6661         XtSetArg(args[0], XtNleftBitmap, None);
6662     }
6663     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
6664                 args, 1);
6665 }
6666
6667 void ShowCoordsProc(w, event, prms, nprms)
6668      Widget w;
6669      XEvent *event;
6670      String *prms;
6671      Cardinal *nprms;
6672 {
6673     Arg args[16];
6674
6675     appData.showCoords = !appData.showCoords;
6676
6677     if (appData.showCoords) {
6678         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6679     } else {
6680         XtSetArg(args[0], XtNleftBitmap, None);
6681     }
6682     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6683                 args, 1);
6684
6685     DrawPosition(True, NULL);
6686 }
6687
6688 void ShowThinkingProc(w, event, prms, nprms)
6689      Widget w;
6690      XEvent *event;
6691      String *prms;
6692      Cardinal *nprms;
6693 {
6694     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6695     ShowThinkingEvent();
6696 }
6697
6698 void HideThinkingProc(w, event, prms, nprms)
6699      Widget w;
6700      XEvent *event;
6701      String *prms;
6702      Cardinal *nprms;
6703 {
6704     Arg args[16];
6705
6706     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6707     ShowThinkingEvent();
6708
6709     if (appData.hideThinkingFromHuman) {
6710         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6711     } else {
6712         XtSetArg(args[0], XtNleftBitmap, None);
6713     }
6714     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6715                 args, 1);
6716 }
6717
6718 void SaveOnExitProc(w, event, prms, nprms)
6719      Widget w;
6720      XEvent *event;
6721      String *prms;
6722      Cardinal *nprms;
6723 {
6724     Arg args[16];
6725
6726     saveSettingsOnExit = !saveSettingsOnExit;
6727
6728     if (saveSettingsOnExit) {
6729         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6730     } else {
6731         XtSetArg(args[0], XtNleftBitmap, None);
6732     }
6733     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6734                 args, 1);
6735 }
6736
6737 void SaveSettingsProc(w, event, prms, nprms)
6738      Widget w;
6739      XEvent *event;
6740      String *prms;
6741      Cardinal *nprms;
6742 {
6743      SaveSettings(settingsFileName);
6744 }
6745
6746 void InfoProc(w, event, prms, nprms)
6747      Widget w;
6748      XEvent *event;
6749      String *prms;
6750      Cardinal *nprms;
6751 {
6752     char buf[MSG_SIZ];
6753     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6754             INFODIR, INFOFILE);
6755     system(buf);
6756 }
6757
6758 void ManProc(w, event, prms, nprms)
6759      Widget w;
6760      XEvent *event;
6761      String *prms;
6762      Cardinal *nprms;
6763 {
6764     char buf[MSG_SIZ];
6765     String name;
6766     if (nprms && *nprms > 0)
6767       name = prms[0];
6768     else
6769       name = "xboard";
6770     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6771     system(buf);
6772 }
6773
6774 void HintProc(w, event, prms, nprms)
6775      Widget w;
6776      XEvent *event;
6777      String *prms;
6778      Cardinal *nprms;
6779 {
6780     HintEvent();
6781 }
6782
6783 void BookProc(w, event, prms, nprms)
6784      Widget w;
6785      XEvent *event;
6786      String *prms;
6787      Cardinal *nprms;
6788 {
6789     BookEvent();
6790 }
6791
6792 void AboutProc(w, event, prms, nprms)
6793      Widget w;
6794      XEvent *event;
6795      String *prms;
6796      Cardinal *nprms;
6797 {
6798     char buf[MSG_SIZ];
6799 #if ZIPPY
6800     char *zippy = " (with Zippy code)";
6801 #else
6802     char *zippy = "";
6803 #endif
6804     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6805             programVersion, zippy,
6806             "Copyright 1991 Digital Equipment Corporation",
6807             "Enhancements Copyright 1992-2009 Free Software Foundation",
6808             "Enhancements Copyright 2005 Alessandro Scotti",
6809             PACKAGE, " is free software and carries NO WARRANTY;",
6810             "see the file COPYING for more information.");
6811     ErrorPopUp(_("About XBoard"), buf, FALSE);
6812 }
6813
6814 void DebugProc(w, event, prms, nprms)
6815      Widget w;
6816      XEvent *event;
6817      String *prms;
6818      Cardinal *nprms;
6819 {
6820     appData.debugMode = !appData.debugMode;
6821 }
6822
6823 void AboutGameProc(w, event, prms, nprms)
6824      Widget w;
6825      XEvent *event;
6826      String *prms;
6827      Cardinal *nprms;
6828 {
6829     AboutGameEvent();
6830 }
6831
6832 void NothingProc(w, event, prms, nprms)
6833      Widget w;
6834      XEvent *event;
6835      String *prms;
6836      Cardinal *nprms;
6837 {
6838     return;
6839 }
6840
6841 void Iconify(w, event, prms, nprms)
6842      Widget w;
6843      XEvent *event;
6844      String *prms;
6845      Cardinal *nprms;
6846 {
6847     Arg args[16];
6848
6849     fromX = fromY = -1;
6850     XtSetArg(args[0], XtNiconic, True);
6851     XtSetValues(shellWidget, args, 1);
6852 }
6853
6854 void DisplayMessage(message, extMessage)
6855      char *message, *extMessage;
6856 {
6857   /* display a message in the message widget */
6858   
6859   char buf[MSG_SIZ];
6860   Arg arg;
6861   
6862   if (extMessage) 
6863     {
6864       if (*message) 
6865         {
6866           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
6867           message = buf;
6868         } 
6869       else 
6870         {
6871           message = extMessage;
6872         };
6873     };
6874   
6875   /* need to test if messageWidget already exists, since this function
6876      can also be called during the startup, if for example a Xresource
6877      is not set up correctly */
6878   if(messageWidget)
6879     {
6880       XtSetArg(arg, XtNlabel, message);
6881       XtSetValues(messageWidget, &arg, 1);
6882     };
6883   
6884   return;
6885 }
6886
6887 void DisplayTitle(text)
6888      char *text;
6889 {
6890     Arg args[16];
6891     int i;
6892     char title[MSG_SIZ];
6893     char icon[MSG_SIZ];
6894
6895     if (text == NULL) text = "";
6896
6897     if (appData.titleInWindow) {
6898         i = 0;
6899         XtSetArg(args[i], XtNlabel, text);   i++;
6900         XtSetValues(titleWidget, args, i);
6901     }
6902
6903     if (*text != NULLCHAR) {
6904         strcpy(icon, text);
6905         strcpy(title, text);
6906     } else if (appData.icsActive) {
6907         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6908         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6909     } else if (appData.cmailGameName[0] != NULLCHAR) {
6910         snprintf(icon, sizeof(icon), "%s", "CMail");
6911         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6912 #ifdef GOTHIC
6913     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6914     } else if (gameInfo.variant == VariantGothic) {
6915         strcpy(icon, programName);
6916         strcpy(title, GOTHIC);
6917 #endif
6918 #ifdef FALCON
6919     } else if (gameInfo.variant == VariantFalcon) {
6920         strcpy(icon, programName);
6921         strcpy(title, FALCON);
6922 #endif
6923     } else if (appData.noChessProgram) {
6924         strcpy(icon, programName);
6925         strcpy(title, programName);
6926     } else {
6927         strcpy(icon, first.tidy);
6928         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6929     }
6930     i = 0;
6931     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6932     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6933     XtSetValues(shellWidget, args, i);
6934 }
6935
6936
6937 void DisplayError(message, error)
6938      String message;
6939      int error;
6940 {
6941     char buf[MSG_SIZ];
6942
6943     if (error == 0) {
6944         if (appData.debugMode || appData.matchMode) {
6945             fprintf(stderr, "%s: %s\n", programName, message);
6946         }
6947     } else {
6948         if (appData.debugMode || appData.matchMode) {
6949             fprintf(stderr, "%s: %s: %s\n",
6950                     programName, message, strerror(error));
6951         }
6952         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6953         message = buf;
6954     }
6955     ErrorPopUp(_("Error"), message, FALSE);
6956 }
6957
6958
6959 void DisplayMoveError(message)
6960      String message;
6961 {
6962     fromX = fromY = -1;
6963     ClearHighlights();
6964     DrawPosition(FALSE, NULL);
6965     if (appData.debugMode || appData.matchMode) {
6966         fprintf(stderr, "%s: %s\n", programName, message);
6967     }
6968     if (appData.popupMoveErrors) {
6969         ErrorPopUp(_("Error"), message, FALSE);
6970     } else {
6971         DisplayMessage(message, "");
6972     }
6973 }
6974
6975
6976 void DisplayFatalError(message, error, status)
6977      String message;
6978      int error, status;
6979 {
6980     char buf[MSG_SIZ];
6981
6982     errorExitStatus = status;
6983     if (error == 0) {
6984         fprintf(stderr, "%s: %s\n", programName, message);
6985     } else {
6986         fprintf(stderr, "%s: %s: %s\n",
6987                 programName, message, strerror(error));
6988         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
6989         message = buf;
6990     }
6991     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6992       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
6993     } else {
6994       ExitEvent(status);
6995     }
6996 }
6997
6998 void DisplayInformation(message)
6999      String message;
7000 {
7001     ErrorPopDown();
7002     ErrorPopUp(_("Information"), message, TRUE);
7003 }
7004
7005 void DisplayNote(message)
7006      String message;
7007 {
7008     ErrorPopDown();
7009     ErrorPopUp(_("Note"), message, FALSE);
7010 }
7011
7012 static int
7013 NullXErrorCheck(dpy, error_event)
7014      Display *dpy;
7015      XErrorEvent *error_event;
7016 {
7017     return 0;
7018 }
7019
7020 void DisplayIcsInteractionTitle(message)
7021      String message;
7022 {
7023   if (oldICSInteractionTitle == NULL) {
7024     /* Magic to find the old window title, adapted from vim */
7025     char *wina = getenv("WINDOWID");
7026     if (wina != NULL) {
7027       Window win = (Window) atoi(wina);
7028       Window root, parent, *children;
7029       unsigned int nchildren;
7030       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7031       for (;;) {
7032         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7033         if (!XQueryTree(xDisplay, win, &root, &parent,
7034                         &children, &nchildren)) break;
7035         if (children) XFree((void *)children);
7036         if (parent == root || parent == 0) break;
7037         win = parent;
7038       }
7039       XSetErrorHandler(oldHandler);
7040     }
7041     if (oldICSInteractionTitle == NULL) {
7042       oldICSInteractionTitle = "xterm";
7043     }
7044   }
7045   printf("\033]0;%s\007", message);
7046   fflush(stdout);
7047 }
7048
7049 char pendingReplyPrefix[MSG_SIZ];
7050 ProcRef pendingReplyPR;
7051
7052 void AskQuestionProc(w, event, prms, nprms)
7053      Widget w;
7054      XEvent *event;
7055      String *prms;
7056      Cardinal *nprms;
7057 {
7058     if (*nprms != 4) {
7059         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7060                 *nprms);
7061         return;
7062     }
7063     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7064 }
7065
7066 void AskQuestionPopDown()
7067 {
7068     if (!askQuestionUp) return;
7069     XtPopdown(askQuestionShell);
7070     XtDestroyWidget(askQuestionShell);
7071     askQuestionUp = False;
7072 }
7073
7074 void AskQuestionReplyAction(w, event, prms, nprms)
7075      Widget w;
7076      XEvent *event;
7077      String *prms;
7078      Cardinal *nprms;
7079 {
7080     char buf[MSG_SIZ];
7081     int err;
7082     String reply;
7083
7084     reply = XawDialogGetValueString(w = XtParent(w));
7085     strcpy(buf, pendingReplyPrefix);
7086     if (*buf) strcat(buf, " ");
7087     strcat(buf, reply);
7088     strcat(buf, "\n");
7089     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7090     AskQuestionPopDown();
7091
7092     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7093 }
7094
7095 void AskQuestionCallback(w, client_data, call_data)
7096      Widget w;
7097      XtPointer client_data, call_data;
7098 {
7099     String name;
7100     Arg args[16];
7101
7102     XtSetArg(args[0], XtNlabel, &name);
7103     XtGetValues(w, args, 1);
7104
7105     if (strcmp(name, _("cancel")) == 0) {
7106         AskQuestionPopDown();
7107     } else {
7108         AskQuestionReplyAction(w, NULL, NULL, NULL);
7109     }
7110 }
7111
7112 void AskQuestion(title, question, replyPrefix, pr)
7113      char *title, *question, *replyPrefix;
7114      ProcRef pr;
7115 {
7116     Arg args[16];
7117     Widget popup, layout, dialog, edit;
7118     Window root, child;
7119     int x, y, i;
7120     int win_x, win_y;
7121     unsigned int mask;
7122
7123     strcpy(pendingReplyPrefix, replyPrefix);
7124     pendingReplyPR = pr;
7125
7126     i = 0;
7127     XtSetArg(args[i], XtNresizable, True); i++;
7128     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7129     askQuestionShell = popup =
7130       XtCreatePopupShell(title, transientShellWidgetClass,
7131                          shellWidget, args, i);
7132
7133     layout =
7134       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7135                             layoutArgs, XtNumber(layoutArgs));
7136
7137     i = 0;
7138     XtSetArg(args[i], XtNlabel, question); i++;
7139     XtSetArg(args[i], XtNvalue, ""); i++;
7140     XtSetArg(args[i], XtNborderWidth, 0); i++;
7141     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7142                                    layout, args, i);
7143
7144     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7145                        (XtPointer) dialog);
7146     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7147                        (XtPointer) dialog);
7148
7149     XtRealizeWidget(popup);
7150     CatchDeleteWindow(popup, "AskQuestionPopDown");
7151
7152     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7153                   &x, &y, &win_x, &win_y, &mask);
7154
7155     XtSetArg(args[0], XtNx, x - 10);
7156     XtSetArg(args[1], XtNy, y - 30);
7157     XtSetValues(popup, args, 2);
7158
7159     XtPopup(popup, XtGrabExclusive);
7160     askQuestionUp = True;
7161
7162     edit = XtNameToWidget(dialog, "*value");
7163     XtSetKeyboardFocus(popup, edit);
7164 }
7165
7166
7167 void
7168 PlaySound(name)
7169      char *name;
7170 {
7171   if (*name == NULLCHAR) {
7172     return;
7173   } else if (strcmp(name, "$") == 0) {
7174     putc(BELLCHAR, stderr);
7175   } else {
7176     char buf[2048];
7177     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7178     system(buf);
7179   }
7180 }
7181
7182 void
7183 RingBell()
7184 {
7185   PlaySound(appData.soundMove);
7186 }
7187
7188 void
7189 PlayIcsWinSound()
7190 {
7191   PlaySound(appData.soundIcsWin);
7192 }
7193
7194 void
7195 PlayIcsLossSound()
7196 {
7197   PlaySound(appData.soundIcsLoss);
7198 }
7199
7200 void
7201 PlayIcsDrawSound()
7202 {
7203   PlaySound(appData.soundIcsDraw);
7204 }
7205
7206 void
7207 PlayIcsUnfinishedSound()
7208 {
7209   PlaySound(appData.soundIcsUnfinished);
7210 }
7211
7212 void
7213 PlayAlarmSound()
7214 {
7215   PlaySound(appData.soundIcsAlarm);
7216 }
7217
7218 void
7219 EchoOn()
7220 {
7221     system("stty echo");
7222 }
7223
7224 void
7225 EchoOff()
7226 {
7227     system("stty -echo");
7228 }
7229
7230 void
7231 Colorize(cc, continuation)
7232      ColorClass cc;
7233      int continuation;
7234 {
7235     char buf[MSG_SIZ];
7236     int count, outCount, error;
7237
7238     if (textColors[(int)cc].bg > 0) {
7239         if (textColors[(int)cc].fg > 0) {
7240             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7241                     textColors[(int)cc].fg, textColors[(int)cc].bg);
7242         } else {
7243             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7244                     textColors[(int)cc].bg);
7245         }
7246     } else {
7247         if (textColors[(int)cc].fg > 0) {
7248             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7249                     textColors[(int)cc].fg);
7250         } else {
7251             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
7252         }
7253     }
7254     count = strlen(buf);
7255     outCount = OutputToProcess(NoProc, buf, count, &error);
7256     if (outCount < count) {
7257         DisplayFatalError(_("Error writing to display"), error, 1);
7258     }
7259
7260     if (continuation) return;
7261     switch (cc) {
7262     case ColorShout:
7263       PlaySound(appData.soundShout);
7264       break;
7265     case ColorSShout:
7266       PlaySound(appData.soundSShout);
7267       break;
7268     case ColorChannel1:
7269       PlaySound(appData.soundChannel1);
7270       break;
7271     case ColorChannel:
7272       PlaySound(appData.soundChannel);
7273       break;
7274     case ColorKibitz:
7275       PlaySound(appData.soundKibitz);
7276       break;
7277     case ColorTell:
7278       PlaySound(appData.soundTell);
7279       break;
7280     case ColorChallenge:
7281       PlaySound(appData.soundChallenge);
7282       break;
7283     case ColorRequest:
7284       PlaySound(appData.soundRequest);
7285       break;
7286     case ColorSeek:
7287       PlaySound(appData.soundSeek);
7288       break;
7289     case ColorNormal:
7290     case ColorNone:
7291     default:
7292       break;
7293     }
7294 }
7295
7296 char *UserName()
7297 {
7298     return getpwuid(getuid())->pw_name;
7299 }
7300
7301 static char *ExpandPathName(path)
7302      char *path;
7303 {
7304     static char static_buf[2000];
7305     char *d, *s, buf[2000];
7306     struct passwd *pwd;
7307
7308     s = path;
7309     d = static_buf;
7310
7311     while (*s && isspace(*s))
7312       ++s;
7313
7314     if (!*s) {
7315         *d = 0;
7316         return static_buf;
7317     }
7318
7319     if (*s == '~') {
7320         if (*(s+1) == '/') {
7321             strcpy(d, getpwuid(getuid())->pw_dir);
7322             strcat(d, s+1);
7323         }
7324         else {
7325             strcpy(buf, s+1);
7326             *strchr(buf, '/') = 0;
7327             pwd = getpwnam(buf);
7328             if (!pwd)
7329               {
7330                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7331                           buf, path);
7332                   return NULL;
7333               }
7334             strcpy(d, pwd->pw_dir);
7335             strcat(d, strchr(s+1, '/'));
7336         }
7337     }
7338     else
7339       strcpy(d, s);
7340
7341     return static_buf;
7342 }
7343
7344 char *HostName()
7345 {
7346     static char host_name[MSG_SIZ];
7347
7348 #if HAVE_GETHOSTNAME
7349     gethostname(host_name, MSG_SIZ);
7350     return host_name;
7351 #else  /* not HAVE_GETHOSTNAME */
7352 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7353     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7354     return host_name;
7355 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7356     return "localhost";
7357 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7358 #endif /* not HAVE_GETHOSTNAME */
7359 }
7360
7361 XtIntervalId delayedEventTimerXID = 0;
7362 DelayedEventCallback delayedEventCallback = 0;
7363
7364 void
7365 FireDelayedEvent()
7366 {
7367     delayedEventTimerXID = 0;
7368     delayedEventCallback();
7369 }
7370
7371 void
7372 ScheduleDelayedEvent(cb, millisec)
7373      DelayedEventCallback cb; long millisec;
7374 {
7375     if(delayedEventTimerXID && delayedEventCallback == cb)
7376         // [HGM] alive: replace, rather than add or flush identical event
7377         XtRemoveTimeOut(delayedEventTimerXID);
7378     delayedEventCallback = cb;
7379     delayedEventTimerXID =
7380       XtAppAddTimeOut(appContext, millisec,
7381                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7382 }
7383
7384 DelayedEventCallback
7385 GetDelayedEvent()
7386 {
7387   if (delayedEventTimerXID) {
7388     return delayedEventCallback;
7389   } else {
7390     return NULL;
7391   }
7392 }
7393
7394 void
7395 CancelDelayedEvent()
7396 {
7397   if (delayedEventTimerXID) {
7398     XtRemoveTimeOut(delayedEventTimerXID);
7399     delayedEventTimerXID = 0;
7400   }
7401 }
7402
7403 XtIntervalId loadGameTimerXID = 0;
7404
7405 int LoadGameTimerRunning()
7406 {
7407     return loadGameTimerXID != 0;
7408 }
7409
7410 int StopLoadGameTimer()
7411 {
7412     if (loadGameTimerXID != 0) {
7413         XtRemoveTimeOut(loadGameTimerXID);
7414         loadGameTimerXID = 0;
7415         return TRUE;
7416     } else {
7417         return FALSE;
7418     }
7419 }
7420
7421 void
7422 LoadGameTimerCallback(arg, id)
7423      XtPointer arg;
7424      XtIntervalId *id;
7425 {
7426     loadGameTimerXID = 0;
7427     AutoPlayGameLoop();
7428 }
7429
7430 void
7431 StartLoadGameTimer(millisec)
7432      long millisec;
7433 {
7434     loadGameTimerXID =
7435       XtAppAddTimeOut(appContext, millisec,
7436                       (XtTimerCallbackProc) LoadGameTimerCallback,
7437                       (XtPointer) 0);
7438 }
7439
7440 XtIntervalId analysisClockXID = 0;
7441
7442 void
7443 AnalysisClockCallback(arg, id)
7444      XtPointer arg;
7445      XtIntervalId *id;
7446 {
7447     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7448          || appData.icsEngineAnalyze) { // [DM]
7449         AnalysisPeriodicEvent(0);
7450         StartAnalysisClock();
7451     }
7452 }
7453
7454 void
7455 StartAnalysisClock()
7456 {
7457     analysisClockXID =
7458       XtAppAddTimeOut(appContext, 2000,
7459                       (XtTimerCallbackProc) AnalysisClockCallback,
7460                       (XtPointer) 0);
7461 }
7462
7463 XtIntervalId clockTimerXID = 0;
7464
7465 int ClockTimerRunning()
7466 {
7467     return clockTimerXID != 0;
7468 }
7469
7470 int StopClockTimer()
7471 {
7472     if (clockTimerXID != 0) {
7473         XtRemoveTimeOut(clockTimerXID);
7474         clockTimerXID = 0;
7475         return TRUE;
7476     } else {
7477         return FALSE;
7478     }
7479 }
7480
7481 void
7482 ClockTimerCallback(arg, id)
7483      XtPointer arg;
7484      XtIntervalId *id;
7485 {
7486     clockTimerXID = 0;
7487     DecrementClocks();
7488 }
7489
7490 void
7491 StartClockTimer(millisec)
7492      long millisec;
7493 {
7494     clockTimerXID =
7495       XtAppAddTimeOut(appContext, millisec,
7496                       (XtTimerCallbackProc) ClockTimerCallback,
7497                       (XtPointer) 0);
7498 }
7499
7500 void
7501 DisplayTimerLabel(w, color, timer, highlight)
7502      Widget w;
7503      char *color;
7504      long timer;
7505      int highlight;
7506 {
7507     char buf[MSG_SIZ];
7508     Arg args[16];
7509
7510     /* check for low time warning */
7511     Pixel foregroundOrWarningColor = timerForegroundPixel;
7512
7513     if (timer > 0 &&
7514         appData.lowTimeWarning && 
7515         (timer / 1000) < appData.icsAlarmTime)
7516       foregroundOrWarningColor = lowTimeWarningColor;
7517
7518     if (appData.clockMode) {
7519         sprintf(buf, "%s: %s", color, TimeString(timer));
7520         XtSetArg(args[0], XtNlabel, buf);
7521     } else {
7522         sprintf(buf, "%s  ", color);
7523         XtSetArg(args[0], XtNlabel, buf);
7524     }
7525
7526     if (highlight) {
7527
7528         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7529         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7530     } else {
7531         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7532         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7533     }
7534
7535     XtSetValues(w, args, 3);
7536 }
7537
7538 void
7539 DisplayWhiteClock(timeRemaining, highlight)
7540      long timeRemaining;
7541      int highlight;
7542 {
7543     Arg args[16];
7544
7545     if(appData.noGUI) return;
7546     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7547     if (highlight && iconPixmap == bIconPixmap) {
7548         iconPixmap = wIconPixmap;
7549         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7550         XtSetValues(shellWidget, args, 1);
7551     }
7552 }
7553
7554 void
7555 DisplayBlackClock(timeRemaining, highlight)
7556      long timeRemaining;
7557      int highlight;
7558 {
7559     Arg args[16];
7560
7561     if(appData.noGUI) return;
7562     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7563     if (highlight && iconPixmap == wIconPixmap) {
7564         iconPixmap = bIconPixmap;
7565         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7566         XtSetValues(shellWidget, args, 1);
7567     }
7568 }
7569
7570 #define CPNone 0
7571 #define CPReal 1
7572 #define CPComm 2
7573 #define CPSock 3
7574 #define CPLoop 4
7575 typedef int CPKind;
7576
7577 typedef struct {
7578     CPKind kind;
7579     int pid;
7580     int fdTo, fdFrom;
7581 } ChildProc;
7582
7583
7584 int StartChildProcess(cmdLine, dir, pr)
7585      char *cmdLine;
7586      char *dir;
7587      ProcRef *pr;
7588 {
7589     char *argv[64], *p;
7590     int i, pid;
7591     int to_prog[2], from_prog[2];
7592     ChildProc *cp;
7593     char buf[MSG_SIZ];
7594
7595     if (appData.debugMode) {
7596         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7597     }
7598
7599     /* We do NOT feed the cmdLine to the shell; we just
7600        parse it into blank-separated arguments in the
7601        most simple-minded way possible.
7602        */
7603     i = 0;
7604     strcpy(buf, cmdLine);
7605     p = buf;
7606     for (;;) {
7607         while(*p == ' ') p++;
7608         argv[i++] = p;
7609         if(*p == '"' || *p == '\'')
7610              p = strchr(++argv[i-1], *p);
7611         else p = strchr(p, ' ');
7612         if (p == NULL) break;
7613         *p++ = NULLCHAR;
7614     }
7615     argv[i] = NULL;
7616
7617     SetUpChildIO(to_prog, from_prog);
7618
7619     if ((pid = fork()) == 0) {
7620         /* Child process */
7621         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7622         close(to_prog[1]);     // first close the unused pipe ends
7623         close(from_prog[0]);
7624         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7625         dup2(from_prog[1], 1);
7626         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7627         close(from_prog[1]);                   // and closing again loses one of the pipes!
7628         if(fileno(stderr) >= 2) // better safe than sorry...
7629                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7630
7631         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7632             perror(dir);
7633             exit(1);
7634         }
7635
7636         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7637
7638         execvp(argv[0], argv);
7639
7640         /* If we get here, exec failed */
7641         perror(argv[0]);
7642         exit(1);
7643     }
7644
7645     /* Parent process */
7646     close(to_prog[0]);
7647     close(from_prog[1]);
7648
7649     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7650     cp->kind = CPReal;
7651     cp->pid = pid;
7652     cp->fdFrom = from_prog[0];
7653     cp->fdTo = to_prog[1];
7654     *pr = (ProcRef) cp;
7655     return 0;
7656 }
7657
7658 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7659 static RETSIGTYPE AlarmCallBack(int n)
7660 {
7661     return;
7662 }
7663
7664 void
7665 DestroyChildProcess(pr, signalType)
7666      ProcRef pr;
7667      int signalType;
7668 {
7669     ChildProc *cp = (ChildProc *) pr;
7670
7671     if (cp->kind != CPReal) return;
7672     cp->kind = CPNone;
7673     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7674         signal(SIGALRM, AlarmCallBack);
7675         alarm(3);
7676         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7677             kill(cp->pid, SIGKILL); // kill it forcefully
7678             wait((int *) 0);        // and wait again
7679         }
7680     } else {
7681         if (signalType) {
7682             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7683         }
7684         /* Process is exiting either because of the kill or because of
7685            a quit command sent by the backend; either way, wait for it to die.
7686         */
7687         wait((int *) 0);
7688     }
7689     close(cp->fdFrom);
7690     close(cp->fdTo);
7691 }
7692
7693 void
7694 InterruptChildProcess(pr)
7695      ProcRef pr;
7696 {
7697     ChildProc *cp = (ChildProc *) pr;
7698
7699     if (cp->kind != CPReal) return;
7700     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7701 }
7702
7703 int OpenTelnet(host, port, pr)
7704      char *host;
7705      char *port;
7706      ProcRef *pr;
7707 {
7708     char cmdLine[MSG_SIZ];
7709
7710     if (port[0] == NULLCHAR) {
7711       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7712     } else {
7713       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7714     }
7715     return StartChildProcess(cmdLine, "", pr);
7716 }
7717
7718 int OpenTCP(host, port, pr)
7719      char *host;
7720      char *port;
7721      ProcRef *pr;
7722 {
7723 #if OMIT_SOCKETS
7724     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7725 #else  /* !OMIT_SOCKETS */
7726     int s;
7727     struct sockaddr_in sa;
7728     struct hostent     *hp;
7729     unsigned short uport;
7730     ChildProc *cp;
7731
7732     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7733         return errno;
7734     }
7735
7736     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7737     sa.sin_family = AF_INET;
7738     sa.sin_addr.s_addr = INADDR_ANY;
7739     uport = (unsigned short) 0;
7740     sa.sin_port = htons(uport);
7741     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7742         return errno;
7743     }
7744
7745     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7746     if (!(hp = gethostbyname(host))) {
7747         int b0, b1, b2, b3;
7748         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7749             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7750             hp->h_addrtype = AF_INET;
7751             hp->h_length = 4;
7752             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7753             hp->h_addr_list[0] = (char *) malloc(4);
7754             hp->h_addr_list[0][0] = b0;
7755             hp->h_addr_list[0][1] = b1;
7756             hp->h_addr_list[0][2] = b2;
7757             hp->h_addr_list[0][3] = b3;
7758         } else {
7759             return ENOENT;
7760         }
7761     }
7762     sa.sin_family = hp->h_addrtype;
7763     uport = (unsigned short) atoi(port);
7764     sa.sin_port = htons(uport);
7765     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7766
7767     if (connect(s, (struct sockaddr *) &sa,
7768                 sizeof(struct sockaddr_in)) < 0) {
7769         return errno;
7770     }
7771
7772     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7773     cp->kind = CPSock;
7774     cp->pid = 0;
7775     cp->fdFrom = s;
7776     cp->fdTo = s;
7777     *pr = (ProcRef) cp;
7778
7779 #endif /* !OMIT_SOCKETS */
7780
7781     return 0;
7782 }
7783
7784 int OpenCommPort(name, pr)
7785      char *name;
7786      ProcRef *pr;
7787 {
7788     int fd;
7789     ChildProc *cp;
7790
7791     fd = open(name, 2, 0);
7792     if (fd < 0) return errno;
7793
7794     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7795     cp->kind = CPComm;
7796     cp->pid = 0;
7797     cp->fdFrom = fd;
7798     cp->fdTo = fd;
7799     *pr = (ProcRef) cp;
7800
7801     return 0;
7802 }
7803
7804 int OpenLoopback(pr)
7805      ProcRef *pr;
7806 {
7807     ChildProc *cp;
7808     int to[2], from[2];
7809
7810     SetUpChildIO(to, from);
7811
7812     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7813     cp->kind = CPLoop;
7814     cp->pid = 0;
7815     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7816     cp->fdTo = to[1];
7817     *pr = (ProcRef) cp;
7818
7819     return 0;
7820 }
7821
7822 int OpenRcmd(host, user, cmd, pr)
7823      char *host, *user, *cmd;
7824      ProcRef *pr;
7825 {
7826     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7827     return -1;
7828 }
7829
7830 #define INPUT_SOURCE_BUF_SIZE 8192
7831
7832 typedef struct {
7833     CPKind kind;
7834     int fd;
7835     int lineByLine;
7836     char *unused;
7837     InputCallback func;
7838     XtInputId xid;
7839     char buf[INPUT_SOURCE_BUF_SIZE];
7840     VOIDSTAR closure;
7841 } InputSource;
7842
7843 void
7844 DoInputCallback(closure, source, xid)
7845      caddr_t closure;
7846      int *source;
7847      XtInputId *xid;
7848 {
7849     InputSource *is = (InputSource *) closure;
7850     int count;
7851     int error;
7852     char *p, *q;
7853
7854     if (is->lineByLine) {
7855         count = read(is->fd, is->unused,
7856                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7857         if (count <= 0) {
7858             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7859             return;
7860         }
7861         is->unused += count;
7862         p = is->buf;
7863         while (p < is->unused) {
7864             q = memchr(p, '\n', is->unused - p);
7865             if (q == NULL) break;
7866             q++;
7867             (is->func)(is, is->closure, p, q - p, 0);
7868             p = q;
7869         }
7870         q = is->buf;
7871         while (p < is->unused) {
7872             *q++ = *p++;
7873         }
7874         is->unused = q;
7875     } else {
7876         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7877         if (count == -1)
7878           error = errno;
7879         else
7880           error = 0;
7881         (is->func)(is, is->closure, is->buf, count, error);
7882     }
7883 }
7884
7885 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7886      ProcRef pr;
7887      int lineByLine;
7888      InputCallback func;
7889      VOIDSTAR closure;
7890 {
7891     InputSource *is;
7892     ChildProc *cp = (ChildProc *) pr;
7893
7894     is = (InputSource *) calloc(1, sizeof(InputSource));
7895     is->lineByLine = lineByLine;
7896     is->func = func;
7897     if (pr == NoProc) {
7898         is->kind = CPReal;
7899         is->fd = fileno(stdin);
7900     } else {
7901         is->kind = cp->kind;
7902         is->fd = cp->fdFrom;
7903     }
7904     if (lineByLine) {
7905         is->unused = is->buf;
7906     }
7907
7908     is->xid = XtAppAddInput(appContext, is->fd,
7909                             (XtPointer) (XtInputReadMask),
7910                             (XtInputCallbackProc) DoInputCallback,
7911                             (XtPointer) is);
7912     is->closure = closure;
7913     return (InputSourceRef) is;
7914 }
7915
7916 void
7917 RemoveInputSource(isr)
7918      InputSourceRef isr;
7919 {
7920     InputSource *is = (InputSource *) isr;
7921
7922     if (is->xid == 0) return;
7923     XtRemoveInput(is->xid);
7924     is->xid = 0;
7925 }
7926
7927 int OutputToProcess(pr, message, count, outError)
7928      ProcRef pr;
7929      char *message;
7930      int count;
7931      int *outError;
7932 {
7933     static int line = 0;
7934     ChildProc *cp = (ChildProc *) pr;
7935     int outCount;
7936
7937     if (pr == NoProc)
7938     {
7939         if (appData.noJoin || !appData.useInternalWrap)
7940             outCount = fwrite(message, 1, count, stdout);
7941         else
7942         {
7943             int width = get_term_width();
7944             int len = wrap(NULL, message, count, width, &line);
7945             char *msg = malloc(len);
7946             int dbgchk;
7947
7948             if (!msg)
7949                 outCount = fwrite(message, 1, count, stdout);
7950             else
7951             {
7952                 dbgchk = wrap(msg, message, count, width, &line);
7953                 if (dbgchk != len && appData.debugMode)
7954                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
7955                 outCount = fwrite(msg, 1, dbgchk, stdout);
7956                 free(msg);
7957             }
7958         }
7959     }
7960     else
7961       outCount = write(cp->fdTo, message, count);
7962
7963     if (outCount == -1)
7964       *outError = errno;
7965     else
7966       *outError = 0;
7967
7968     return outCount;
7969 }
7970
7971 /* Output message to process, with "ms" milliseconds of delay
7972    between each character. This is needed when sending the logon
7973    script to ICC, which for some reason doesn't like the
7974    instantaneous send. */
7975 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7976      ProcRef pr;
7977      char *message;
7978      int count;
7979      int *outError;
7980      long msdelay;
7981 {
7982     ChildProc *cp = (ChildProc *) pr;
7983     int outCount = 0;
7984     int r;
7985
7986     while (count--) {
7987         r = write(cp->fdTo, message++, 1);
7988         if (r == -1) {
7989             *outError = errno;
7990             return outCount;
7991         }
7992         ++outCount;
7993         if (msdelay >= 0)
7994           TimeDelay(msdelay);
7995     }
7996
7997     return outCount;
7998 }
7999
8000 /****   Animation code by Hugh Fisher, DCS, ANU.
8001
8002         Known problem: if a window overlapping the board is
8003         moved away while a piece is being animated underneath,
8004         the newly exposed area won't be updated properly.
8005         I can live with this.
8006
8007         Known problem: if you look carefully at the animation
8008         of pieces in mono mode, they are being drawn as solid
8009         shapes without interior detail while moving. Fixing
8010         this would be a major complication for minimal return.
8011 ****/
8012
8013 /*      Masks for XPM pieces. Black and white pieces can have
8014         different shapes, but in the interest of retaining my
8015         sanity pieces must have the same outline on both light
8016         and dark squares, and all pieces must use the same
8017         background square colors/images.                */
8018
8019 static int xpmDone = 0;
8020
8021 static void
8022 CreateAnimMasks (pieceDepth)
8023      int pieceDepth;
8024 {
8025   ChessSquare   piece;
8026   Pixmap        buf;
8027   GC            bufGC, maskGC;
8028   int           kind, n;
8029   unsigned long plane;
8030   XGCValues     values;
8031
8032   /* Need a bitmap just to get a GC with right depth */
8033   buf = XCreatePixmap(xDisplay, xBoardWindow,
8034                         8, 8, 1);
8035   values.foreground = 1;
8036   values.background = 0;
8037   /* Don't use XtGetGC, not read only */
8038   maskGC = XCreateGC(xDisplay, buf,
8039                     GCForeground | GCBackground, &values);
8040   XFreePixmap(xDisplay, buf);
8041
8042   buf = XCreatePixmap(xDisplay, xBoardWindow,
8043                       squareSize, squareSize, pieceDepth);
8044   values.foreground = XBlackPixel(xDisplay, xScreen);
8045   values.background = XWhitePixel(xDisplay, xScreen);
8046   bufGC = XCreateGC(xDisplay, buf,
8047                     GCForeground | GCBackground, &values);
8048
8049   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8050     /* Begin with empty mask */
8051     if(!xpmDone) // [HGM] pieces: keep using existing
8052     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8053                                  squareSize, squareSize, 1);
8054     XSetFunction(xDisplay, maskGC, GXclear);
8055     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8056                    0, 0, squareSize, squareSize);
8057
8058     /* Take a copy of the piece */
8059     if (White(piece))
8060       kind = 0;
8061     else
8062       kind = 2;
8063     XSetFunction(xDisplay, bufGC, GXcopy);
8064     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8065               buf, bufGC,
8066               0, 0, squareSize, squareSize, 0, 0);
8067
8068     /* XOR the background (light) over the piece */
8069     XSetFunction(xDisplay, bufGC, GXxor);
8070     if (useImageSqs)
8071       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8072                 0, 0, squareSize, squareSize, 0, 0);
8073     else {
8074       XSetForeground(xDisplay, bufGC, lightSquareColor);
8075       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8076     }
8077
8078     /* We now have an inverted piece image with the background
8079        erased. Construct mask by just selecting all the non-zero
8080        pixels - no need to reconstruct the original image.      */
8081     XSetFunction(xDisplay, maskGC, GXor);
8082     plane = 1;
8083     /* Might be quicker to download an XImage and create bitmap
8084        data from it rather than this N copies per piece, but it
8085        only takes a fraction of a second and there is a much
8086        longer delay for loading the pieces.             */
8087     for (n = 0; n < pieceDepth; n ++) {
8088       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8089                  0, 0, squareSize, squareSize,
8090                  0, 0, plane);
8091       plane = plane << 1;
8092     }
8093   }
8094   /* Clean up */
8095   XFreePixmap(xDisplay, buf);
8096   XFreeGC(xDisplay, bufGC);
8097   XFreeGC(xDisplay, maskGC);
8098 }
8099
8100 static void
8101 InitAnimState (anim, info)
8102   AnimState * anim;
8103   XWindowAttributes * info;
8104 {
8105   XtGCMask  mask;
8106   XGCValues values;
8107
8108   /* Each buffer is square size, same depth as window */
8109   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8110                         squareSize, squareSize, info->depth);
8111   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8112                         squareSize, squareSize, info->depth);
8113
8114   /* Create a plain GC for blitting */
8115   mask = GCForeground | GCBackground | GCFunction |
8116          GCPlaneMask | GCGraphicsExposures;
8117   values.foreground = XBlackPixel(xDisplay, xScreen);
8118   values.background = XWhitePixel(xDisplay, xScreen);
8119   values.function   = GXcopy;
8120   values.plane_mask = AllPlanes;
8121   values.graphics_exposures = False;
8122   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8123
8124   /* Piece will be copied from an existing context at
8125      the start of each new animation/drag. */
8126   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8127
8128   /* Outline will be a read-only copy of an existing */
8129   anim->outlineGC = None;
8130 }
8131
8132 static void
8133 CreateAnimVars ()
8134 {
8135   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8136   XWindowAttributes info;
8137
8138   if (xpmDone && gameInfo.variant == old) return;
8139   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8140   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8141
8142   InitAnimState(&game, &info);
8143   InitAnimState(&player, &info);
8144
8145   /* For XPM pieces, we need bitmaps to use as masks. */
8146   if (useImages)
8147     CreateAnimMasks(info.depth);
8148    xpmDone = 1;
8149 }
8150
8151 #ifndef HAVE_USLEEP
8152
8153 static Boolean frameWaiting;
8154
8155 static RETSIGTYPE FrameAlarm (sig)
8156      int sig;
8157 {
8158   frameWaiting = False;
8159   /* In case System-V style signals.  Needed?? */
8160   signal(SIGALRM, FrameAlarm);
8161 }
8162
8163 static void
8164 FrameDelay (time)
8165      int time;
8166 {
8167   struct itimerval delay;
8168
8169   XSync(xDisplay, False);
8170
8171   if (time > 0) {
8172     frameWaiting = True;
8173     signal(SIGALRM, FrameAlarm);
8174     delay.it_interval.tv_sec =
8175       delay.it_value.tv_sec = time / 1000;
8176     delay.it_interval.tv_usec =
8177       delay.it_value.tv_usec = (time % 1000) * 1000;
8178     setitimer(ITIMER_REAL, &delay, NULL);
8179     while (frameWaiting) pause();
8180     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8181     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8182     setitimer(ITIMER_REAL, &delay, NULL);
8183   }
8184 }
8185
8186 #else
8187
8188 static void
8189 FrameDelay (time)
8190      int time;
8191 {
8192   XSync(xDisplay, False);
8193   if (time > 0)
8194     usleep(time * 1000);
8195 }
8196
8197 #endif
8198
8199 /*      Convert board position to corner of screen rect and color       */
8200
8201 static void
8202 ScreenSquare(column, row, pt, color)
8203      int column; int row; XPoint * pt; int * color;
8204 {
8205   if (flipView) {
8206     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8207     pt->y = lineGap + row * (squareSize + lineGap);
8208   } else {
8209     pt->x = lineGap + column * (squareSize + lineGap);
8210     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8211   }
8212   *color = SquareColor(row, column);
8213 }
8214
8215 /*      Convert window coords to square                 */
8216
8217 static void
8218 BoardSquare(x, y, column, row)
8219      int x; int y; int * column; int * row;
8220 {
8221   *column = EventToSquare(x, BOARD_WIDTH);
8222   if (flipView && *column >= 0)
8223     *column = BOARD_WIDTH - 1 - *column;
8224   *row = EventToSquare(y, BOARD_HEIGHT);
8225   if (!flipView && *row >= 0)
8226     *row = BOARD_HEIGHT - 1 - *row;
8227 }
8228
8229 /*   Utilities  */
8230
8231 #undef Max  /* just in case */
8232 #undef Min
8233 #define Max(a, b) ((a) > (b) ? (a) : (b))
8234 #define Min(a, b) ((a) < (b) ? (a) : (b))
8235
8236 static void
8237 SetRect(rect, x, y, width, height)
8238      XRectangle * rect; int x; int y; int width; int height;
8239 {
8240   rect->x = x;
8241   rect->y = y;
8242   rect->width  = width;
8243   rect->height = height;
8244 }
8245
8246 /*      Test if two frames overlap. If they do, return
8247         intersection rect within old and location of
8248         that rect within new. */
8249
8250 static Boolean
8251 Intersect(old, new, size, area, pt)
8252      XPoint * old; XPoint * new;
8253      int size; XRectangle * area; XPoint * pt;
8254 {
8255   if (old->x > new->x + size || new->x > old->x + size ||
8256       old->y > new->y + size || new->y > old->y + size) {
8257     return False;
8258   } else {
8259     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8260             size - abs(old->x - new->x), size - abs(old->y - new->y));
8261     pt->x = Max(old->x - new->x, 0);
8262     pt->y = Max(old->y - new->y, 0);
8263     return True;
8264   }
8265 }
8266
8267 /*      For two overlapping frames, return the rect(s)
8268         in the old that do not intersect with the new.   */
8269
8270 static void
8271 CalcUpdateRects(old, new, size, update, nUpdates)
8272      XPoint * old; XPoint * new; int size;
8273      XRectangle update[]; int * nUpdates;
8274 {
8275   int        count;
8276
8277   /* If old = new (shouldn't happen) then nothing to draw */
8278   if (old->x == new->x && old->y == new->y) {
8279     *nUpdates = 0;
8280     return;
8281   }
8282   /* Work out what bits overlap. Since we know the rects
8283      are the same size we don't need a full intersect calc. */
8284   count = 0;
8285   /* Top or bottom edge? */
8286   if (new->y > old->y) {
8287     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8288     count ++;
8289   } else if (old->y > new->y) {
8290     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8291                               size, old->y - new->y);
8292     count ++;
8293   }
8294   /* Left or right edge - don't overlap any update calculated above. */
8295   if (new->x > old->x) {
8296     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8297                               new->x - old->x, size - abs(new->y - old->y));
8298     count ++;
8299   } else if (old->x > new->x) {
8300     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8301                               old->x - new->x, size - abs(new->y - old->y));
8302     count ++;
8303   }
8304   /* Done */
8305   *nUpdates = count;
8306 }
8307
8308 /*      Generate a series of frame coords from start->mid->finish.
8309         The movement rate doubles until the half way point is
8310         reached, then halves back down to the final destination,
8311         which gives a nice slow in/out effect. The algorithmn
8312         may seem to generate too many intermediates for short
8313         moves, but remember that the purpose is to attract the
8314         viewers attention to the piece about to be moved and
8315         then to where it ends up. Too few frames would be less
8316         noticeable.                                             */
8317
8318 static void
8319 Tween(start, mid, finish, factor, frames, nFrames)
8320      XPoint * start; XPoint * mid;
8321      XPoint * finish; int factor;
8322      XPoint frames[]; int * nFrames;
8323 {
8324   int fraction, n, count;
8325
8326   count = 0;
8327
8328   /* Slow in, stepping 1/16th, then 1/8th, ... */
8329   fraction = 1;
8330   for (n = 0; n < factor; n++)
8331     fraction *= 2;
8332   for (n = 0; n < factor; n++) {
8333     frames[count].x = start->x + (mid->x - start->x) / fraction;
8334     frames[count].y = start->y + (mid->y - start->y) / fraction;
8335     count ++;
8336     fraction = fraction / 2;
8337   }
8338
8339   /* Midpoint */
8340   frames[count] = *mid;
8341   count ++;
8342
8343   /* Slow out, stepping 1/2, then 1/4, ... */
8344   fraction = 2;
8345   for (n = 0; n < factor; n++) {
8346     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8347     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8348     count ++;
8349     fraction = fraction * 2;
8350   }
8351   *nFrames = count;
8352 }
8353
8354 /*      Draw a piece on the screen without disturbing what's there      */
8355
8356 static void
8357 SelectGCMask(piece, clip, outline, mask)
8358      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8359 {
8360   GC source;
8361
8362   /* Bitmap for piece being moved. */
8363   if (appData.monoMode) {
8364       *mask = *pieceToSolid(piece);
8365   } else if (useImages) {
8366 #if HAVE_LIBXPM
8367       *mask = xpmMask[piece];
8368 #else
8369       *mask = ximMaskPm[piece];
8370 #endif
8371   } else {
8372       *mask = *pieceToSolid(piece);
8373   }
8374
8375   /* GC for piece being moved. Square color doesn't matter, but
8376      since it gets modified we make a copy of the original. */
8377   if (White(piece)) {
8378     if (appData.monoMode)
8379       source = bwPieceGC;
8380     else
8381       source = wlPieceGC;
8382   } else {
8383     if (appData.monoMode)
8384       source = wbPieceGC;
8385     else
8386       source = blPieceGC;
8387   }
8388   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8389
8390   /* Outline only used in mono mode and is not modified */
8391   if (White(piece))
8392     *outline = bwPieceGC;
8393   else
8394     *outline = wbPieceGC;
8395 }
8396
8397 static void
8398 OverlayPiece(piece, clip, outline,  dest)
8399      ChessSquare piece; GC clip; GC outline; Drawable dest;
8400 {
8401   int   kind;
8402
8403   if (!useImages) {
8404     /* Draw solid rectangle which will be clipped to shape of piece */
8405     XFillRectangle(xDisplay, dest, clip,
8406                    0, 0, squareSize, squareSize);
8407     if (appData.monoMode)
8408       /* Also draw outline in contrasting color for black
8409          on black / white on white cases                */
8410       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8411                  0, 0, squareSize, squareSize, 0, 0, 1);
8412   } else {
8413     /* Copy the piece */
8414     if (White(piece))
8415       kind = 0;
8416     else
8417       kind = 2;
8418     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8419               dest, clip,
8420               0, 0, squareSize, squareSize,
8421               0, 0);
8422   }
8423 }
8424
8425 /* Animate the movement of a single piece */
8426
8427 static void
8428 BeginAnimation(anim, piece, startColor, start)
8429      AnimState *anim;
8430      ChessSquare piece;
8431      int startColor;
8432      XPoint * start;
8433 {
8434   Pixmap mask;
8435
8436   /* The old buffer is initialised with the start square (empty) */
8437   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
8438   anim->prevFrame = *start;
8439
8440   /* The piece will be drawn using its own bitmap as a matte    */
8441   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8442   XSetClipMask(xDisplay, anim->pieceGC, mask);
8443 }
8444
8445 static void
8446 AnimationFrame(anim, frame, piece)
8447      AnimState *anim;
8448      XPoint *frame;
8449      ChessSquare piece;
8450 {
8451   XRectangle updates[4];
8452   XRectangle overlap;
8453   XPoint     pt;
8454   int        count, i;
8455
8456   /* Save what we are about to draw into the new buffer */
8457   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8458             frame->x, frame->y, squareSize, squareSize,
8459             0, 0);
8460
8461   /* Erase bits of the previous frame */
8462   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8463     /* Where the new frame overlapped the previous,
8464        the contents in newBuf are wrong. */
8465     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8466               overlap.x, overlap.y,
8467               overlap.width, overlap.height,
8468               pt.x, pt.y);
8469     /* Repaint the areas in the old that don't overlap new */
8470     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8471     for (i = 0; i < count; i++)
8472       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8473                 updates[i].x - anim->prevFrame.x,
8474                 updates[i].y - anim->prevFrame.y,
8475                 updates[i].width, updates[i].height,
8476                 updates[i].x, updates[i].y);
8477   } else {
8478     /* Easy when no overlap */
8479     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8480                   0, 0, squareSize, squareSize,
8481                   anim->prevFrame.x, anim->prevFrame.y);
8482   }
8483
8484   /* Save this frame for next time round */
8485   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8486                 0, 0, squareSize, squareSize,
8487                 0, 0);
8488   anim->prevFrame = *frame;
8489
8490   /* Draw piece over original screen contents, not current,
8491      and copy entire rect. Wipes out overlapping piece images. */
8492   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8493   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8494                 0, 0, squareSize, squareSize,
8495                 frame->x, frame->y);
8496 }
8497
8498 static void
8499 EndAnimation (anim, finish)
8500      AnimState *anim;
8501      XPoint *finish;
8502 {
8503   XRectangle updates[4];
8504   XRectangle overlap;
8505   XPoint     pt;
8506   int        count, i;
8507
8508   /* The main code will redraw the final square, so we
8509      only need to erase the bits that don't overlap.    */
8510   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8511     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8512     for (i = 0; i < count; i++)
8513       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8514                 updates[i].x - anim->prevFrame.x,
8515                 updates[i].y - anim->prevFrame.y,
8516                 updates[i].width, updates[i].height,
8517                 updates[i].x, updates[i].y);
8518   } else {
8519     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8520                 0, 0, squareSize, squareSize,
8521                 anim->prevFrame.x, anim->prevFrame.y);
8522   }
8523 }
8524
8525 static void
8526 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8527      AnimState *anim;
8528      ChessSquare piece; int startColor;
8529      XPoint * start; XPoint * finish;
8530      XPoint frames[]; int nFrames;
8531 {
8532   int n;
8533
8534   BeginAnimation(anim, piece, startColor, start);
8535   for (n = 0; n < nFrames; n++) {
8536     AnimationFrame(anim, &(frames[n]), piece);
8537     FrameDelay(appData.animSpeed);
8538   }
8539   EndAnimation(anim, finish);
8540 }
8541
8542 /* Main control logic for deciding what to animate and how */
8543
8544 void
8545 AnimateMove(board, fromX, fromY, toX, toY)
8546      Board board;
8547      int fromX;
8548      int fromY;
8549      int toX;
8550      int toY;
8551 {
8552   ChessSquare piece;
8553   int hop;
8554   XPoint      start, finish, mid;
8555   XPoint      frames[kFactor * 2 + 1];
8556   int         nFrames, startColor, endColor;
8557
8558   /* Are we animating? */
8559   if (!appData.animate || appData.blindfold)
8560     return;
8561
8562   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
8563      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
8564         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8565
8566   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8567   piece = board[fromY][fromX];
8568   if (piece >= EmptySquare) return;
8569
8570 #if DONT_HOP
8571   hop = FALSE;
8572 #else
8573   hop = (piece == WhiteKnight || piece == BlackKnight);
8574 #endif
8575
8576   if (appData.debugMode) {
8577       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8578                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8579              piece, fromX, fromY, toX, toY);  }
8580
8581   ScreenSquare(fromX, fromY, &start, &startColor);
8582   ScreenSquare(toX, toY, &finish, &endColor);
8583
8584   if (hop) {
8585     /* Knight: make diagonal movement then straight */
8586     if (abs(toY - fromY) < abs(toX - fromX)) {
8587        mid.x = start.x + (finish.x - start.x) / 2;
8588        mid.y = finish.y;
8589      } else {
8590        mid.x = finish.x;
8591        mid.y = start.y + (finish.y - start.y) / 2;
8592      }
8593   } else {
8594     mid.x = start.x + (finish.x - start.x) / 2;
8595     mid.y = start.y + (finish.y - start.y) / 2;
8596   }
8597
8598   /* Don't use as many frames for very short moves */
8599   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8600     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8601   else
8602     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8603   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8604
8605   /* Be sure end square is redrawn */
8606   damage[toY][toX] = True;
8607 }
8608
8609 void
8610 DragPieceBegin(x, y)
8611      int x; int y;
8612 {
8613     int  boardX, boardY, color;
8614     XPoint corner;
8615
8616     /* Are we animating? */
8617     if (!appData.animateDragging || appData.blindfold)
8618       return;
8619
8620     /* Figure out which square we start in and the
8621        mouse position relative to top left corner. */
8622     BoardSquare(x, y, &boardX, &boardY);
8623     player.startBoardX = boardX;
8624     player.startBoardY = boardY;
8625     ScreenSquare(boardX, boardY, &corner, &color);
8626     player.startSquare  = corner;
8627     player.startColor   = color;
8628     /* As soon as we start dragging, the piece will jump slightly to
8629        be centered over the mouse pointer. */
8630     player.mouseDelta.x = squareSize/2;
8631     player.mouseDelta.y = squareSize/2;
8632     /* Initialise animation */
8633     player.dragPiece = PieceForSquare(boardX, boardY);
8634     /* Sanity check */
8635     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8636         player.dragActive = True;
8637         BeginAnimation(&player, player.dragPiece, color, &corner);
8638         /* Mark this square as needing to be redrawn. Note that
8639            we don't remove the piece though, since logically (ie
8640            as seen by opponent) the move hasn't been made yet. */
8641            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8642               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8643            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8644                      corner.x, corner.y, squareSize, squareSize,
8645                      0, 0); // [HGM] zh: unstack in stead of grab
8646         damage[boardY][boardX] = True;
8647     } else {
8648         player.dragActive = False;
8649     }
8650 }
8651
8652 static void
8653 DragPieceMove(x, y)
8654      int x; int y;
8655 {
8656     XPoint corner;
8657
8658     /* Are we animating? */
8659     if (!appData.animateDragging || appData.blindfold)
8660       return;
8661
8662     /* Sanity check */
8663     if (! player.dragActive)
8664       return;
8665     /* Move piece, maintaining same relative position
8666        of mouse within square    */
8667     corner.x = x - player.mouseDelta.x;
8668     corner.y = y - player.mouseDelta.y;
8669     AnimationFrame(&player, &corner, player.dragPiece);
8670 #if HIGHDRAG*0
8671     if (appData.highlightDragging) {
8672         int boardX, boardY;
8673         BoardSquare(x, y, &boardX, &boardY);
8674         SetHighlights(fromX, fromY, boardX, boardY);
8675     }
8676 #endif
8677 }
8678
8679 void
8680 DragPieceEnd(x, y)
8681      int x; int y;
8682 {
8683     int boardX, boardY, color;
8684     XPoint corner;
8685
8686     /* Are we animating? */
8687     if (!appData.animateDragging || appData.blindfold)
8688       return;
8689
8690     /* Sanity check */
8691     if (! player.dragActive)
8692       return;
8693     /* Last frame in sequence is square piece is
8694        placed on, which may not match mouse exactly. */
8695     BoardSquare(x, y, &boardX, &boardY);
8696     ScreenSquare(boardX, boardY, &corner, &color);
8697     EndAnimation(&player, &corner);
8698
8699     /* Be sure end square is redrawn */
8700     damage[boardY][boardX] = True;
8701
8702     /* This prevents weird things happening with fast successive
8703        clicks which on my Sun at least can cause motion events
8704        without corresponding press/release. */
8705     player.dragActive = False;
8706 }
8707
8708 /* Handle expose event while piece being dragged */
8709
8710 static void
8711 DrawDragPiece ()
8712 {
8713   if (!player.dragActive || appData.blindfold)
8714     return;
8715
8716   /* What we're doing: logically, the move hasn't been made yet,
8717      so the piece is still in it's original square. But visually
8718      it's being dragged around the board. So we erase the square
8719      that the piece is on and draw it at the last known drag point. */
8720   BlankSquare(player.startSquare.x, player.startSquare.y,
8721                 player.startColor, EmptySquare, xBoardWindow);
8722   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8723   damage[player.startBoardY][player.startBoardX] = TRUE;
8724 }
8725
8726 #include <sys/ioctl.h>
8727 int get_term_width()
8728 {
8729     int fd, default_width;
8730
8731     fd = STDIN_FILENO;
8732     default_width = 79; // this is FICS default anyway...
8733
8734 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8735     struct ttysize win;
8736     if (!ioctl(fd, TIOCGSIZE, &win))
8737         default_width = win.ts_cols;
8738 #elif defined(TIOCGWINSZ)
8739     struct winsize win;
8740     if (!ioctl(fd, TIOCGWINSZ, &win))
8741         default_width = win.ws_col;
8742 #endif
8743     return default_width;
8744 }
8745
8746 void update_ics_width()
8747 {
8748     static int old_width = 0;
8749     int new_width = get_term_width();
8750
8751     if (old_width != new_width)
8752        ics_printf("set width %d\n", new_width);
8753     old_width = new_width;
8754 }
8755
8756 void NotifyFrontendLogin()
8757 {
8758     update_ics_width();
8759 }