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