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