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