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