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