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