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