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