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