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