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