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