Allow escape sequences in telluser(error) messages
[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 SelectPV P((Widget w, XEvent * event,
266                      String * params, Cardinal * nParams));
267 void StopPV P((Widget w, XEvent * event,
268                      String * params, Cardinal * nParams));
269 void WhiteClock P((Widget w, XEvent *event,
270                    String *prms, Cardinal *nprms));
271 void BlackClock P((Widget w, XEvent *event,
272                    String *prms, Cardinal *nprms));
273 void DrawPositionProc P((Widget w, XEvent *event,
274                      String *prms, Cardinal *nprms));
275 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
276                      Board board));
277 void CommentClick P((Widget w, XEvent * event,
278                    String * params, Cardinal * nParams));
279 void CommentPopUp P((char *title, char *label));
280 void CommentPopDown P((void));
281 void CommentCallback P((Widget w, XtPointer client_data,
282                         XtPointer call_data));
283 void ICSInputBoxPopUp P((void));
284 void ICSInputBoxPopDown P((void));
285 void FileNamePopUp P((char *label, char *def,
286                       FileProc proc, char *openMode));
287 void FileNamePopDown P((void));
288 void FileNameCallback P((Widget w, XtPointer client_data,
289                          XtPointer call_data));
290 void FileNameAction P((Widget w, XEvent *event,
291                        String *prms, Cardinal *nprms));
292 void AskQuestionReplyAction P((Widget w, XEvent *event,
293                           String *prms, Cardinal *nprms));
294 void AskQuestionProc P((Widget w, XEvent *event,
295                           String *prms, Cardinal *nprms));
296 void AskQuestionPopDown P((void));
297 void PromotionPopDown P((void));
298 void PromotionCallback P((Widget w, XtPointer client_data,
299                           XtPointer call_data));
300 void EditCommentPopDown P((void));
301 void EditCommentCallback P((Widget w, XtPointer client_data,
302                             XtPointer call_data));
303 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
304 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
307                          Cardinal *nprms));
308 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
309                          Cardinal *nprms));
310 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
311                        Cardinal *nprms));
312 void LoadPositionProc P((Widget w, XEvent *event,
313                          String *prms, Cardinal *nprms));
314 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
315                          Cardinal *nprms));
316 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
317                          Cardinal *nprms));
318 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
319                        Cardinal *nprms));
320 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
321                          Cardinal *nprms));
322 void PastePositionProc P((Widget w, XEvent *event, String *prms,
323                           Cardinal *nprms));
324 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
325 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
326 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
327 void SavePositionProc P((Widget w, XEvent *event,
328                          String *prms, Cardinal *nprms));
329 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
330 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
331                             Cardinal *nprms));
332 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
333 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
334 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
335                          Cardinal *nprms));
336 void MachineWhiteProc P((Widget w, XEvent *event,
337                          String *prms, Cardinal *nprms));
338 void AnalyzeModeProc P((Widget w, XEvent *event,
339                          String *prms, Cardinal *nprms));
340 void AnalyzeFileProc P((Widget w, XEvent *event,
341                          String *prms, Cardinal *nprms));
342 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
343                         Cardinal *nprms));
344 void IcsClientProc P((Widget w, XEvent *event, String *prms,
345                       Cardinal *nprms));
346 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
347 void EditPositionProc P((Widget w, XEvent *event,
348                          String *prms, Cardinal *nprms));
349 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
350 void EditCommentProc P((Widget w, XEvent *event,
351                         String *prms, Cardinal *nprms));
352 void IcsInputBoxProc P((Widget w, XEvent *event,
353                         String *prms, Cardinal *nprms));
354 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
355 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
356 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void StopObservingProc P((Widget w, XEvent *event, String *prms,
369                           Cardinal *nprms));
370 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
371                           Cardinal *nprms));
372 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
380                          Cardinal *nprms));
381 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
382                         Cardinal *nprms));
383 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
385                         Cardinal *nprms));
386 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
387                          Cardinal *nprms));
388 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
389                          Cardinal *nprms));
390 void AutocommProc P((Widget w, XEvent *event, String *prms,
391                      Cardinal *nprms));
392 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void AutobsProc P((Widget w, XEvent *event, String *prms,
395                         Cardinal *nprms));
396 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
400                        Cardinal *nprms));
401 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
403                         Cardinal *nprms));
404 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
405                               Cardinal *nprms));
406 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
407                               Cardinal *nprms));
408 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
409 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
411                          Cardinal *nprms));
412 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
413                          Cardinal *nprms));
414 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
415                            Cardinal *nprms));
416 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
417                         Cardinal *nprms));
418 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
419                              Cardinal *nprms));
420 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
421 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
423                        Cardinal *nprms));
424 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
425                          Cardinal *nprms));
426 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
427                          Cardinal *nprms));
428 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
429                           Cardinal *nprms));
430 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
431 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
433 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void DisplayMove P((int moveNumber));
442 void DisplayTitle P((char *title));
443 void ICSInitScript P((void));
444 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
445 void ErrorPopUp P((char *title, char *text, int modal));
446 void ErrorPopDown P((void));
447 static char *ExpandPathName P((char *path));
448 static void CreateAnimVars P((void));
449 static void DragPieceMove P((int x, int y));
450 static void DrawDragPiece P((void));
451 char *ModeToWidgetName P((GameMode mode));
452 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
453 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void GameListOptionsPopDown P(());
461 void ShufflePopDown P(());
462 void EnginePopDown P(());
463 void UciPopDown P(());
464 void TimeControlPopDown P(());
465 void NewVariantPopDown P(());
466 void SettingsPopDown P(());
467 void update_ics_width P(());
468 int get_term_width P(());
469 int CopyMemoProc P(());
470 /*
471 * XBoard depends on Xt R4 or higher
472 */
473 int xtVersion = XtSpecificationRelease;
474
475 int xScreen;
476 Display *xDisplay;
477 Window xBoardWindow;
478 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
479   jailSquareColor, highlightSquareColor, premoveHighlightColor;
480 Pixel lowTimeWarningColor;
481 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
482   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
483   wjPieceGC, bjPieceGC, prelineGC, countGC;
484 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
485 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
486   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
487   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
488   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
489   ICSInputShell, fileNameShell, askQuestionShell;
490 Widget historyShell, evalGraphShell, gameListShell;
491 int hOffset; // [HGM] dual
492 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
493 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
494 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
495 Font clockFontID, coordFontID, countFontID;
496 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
497 XtAppContext appContext;
498 char *layoutName;
499 char *oldICSInteractionTitle;
500
501 FileProc fileProc;
502 char *fileOpenMode;
503 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
504
505 Position commentX = -1, commentY = -1;
506 Dimension commentW, commentH;
507 typedef unsigned int BoardSize;
508 BoardSize boardSize;
509 Boolean chessProgram;
510
511 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
512 int squareSize, smallLayout = 0, tinyLayout = 0,
513   marginW, marginH, // [HGM] for run-time resizing
514   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
515   ICSInputBoxUp = False, askQuestionUp = False,
516   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
517   editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
518 Pixel timerForegroundPixel, timerBackgroundPixel;
519 Pixel buttonForegroundPixel, buttonBackgroundPixel;
520 char *chessDir, *programName, *programVersion,
521   *gameCopyFilename, *gamePasteFilename;
522 Boolean alwaysOnTop = False;
523 Boolean saveSettingsOnExit;
524 char *settingsFileName;
525 char *icsTextMenuString;
526 char *icsNames;
527 char *firstChessProgramNames;
528 char *secondChessProgramNames;
529
530 WindowPlacement wpMain;
531 WindowPlacement wpConsole;
532 WindowPlacement wpComment;
533 WindowPlacement wpMoveHistory;
534 WindowPlacement wpEvalGraph;
535 WindowPlacement wpEngineOutput;
536 WindowPlacement wpGameList;
537 WindowPlacement wpTags;
538
539 #define SOLID 0
540 #define OUTLINE 1
541 Pixmap pieceBitmap[2][(int)BlackPawn];
542 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
543 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
544 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
545 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
546 int useImages, useImageSqs;
547 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
548 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
549 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
550 XImage *ximLightSquare, *ximDarkSquare;
551 XImage *xim_Cross;
552
553 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
554 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
555
556 #define White(piece) ((int)(piece) < (int)BlackPawn)
557
558 /* Variables for doing smooth animation. This whole thing
559    would be much easier if the board was double-buffered,
560    but that would require a fairly major rewrite.       */
561
562 typedef struct {
563         Pixmap  saveBuf;
564         Pixmap  newBuf;
565         GC      blitGC, pieceGC, outlineGC;
566         XPoint  startSquare, prevFrame, mouseDelta;
567         int     startColor;
568         int     dragPiece;
569         Boolean dragActive;
570         int     startBoardX, startBoardY;
571     } AnimState;
572
573 /* There can be two pieces being animated at once: a player
574    can begin dragging a piece before the remote opponent has moved. */
575
576 static AnimState game, player;
577
578 /* Bitmaps for use as masks when drawing XPM pieces.
579    Need one for each black and white piece.             */
580 static Pixmap xpmMask[BlackKing + 1];
581
582 /* This magic number is the number of intermediate frames used
583    in each half of the animation. For short moves it's reduced
584    by 1. The total number of frames will be factor * 2 + 1.  */
585 #define kFactor    4
586
587 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
588
589 MenuItem fileMenu[] = {
590     {N_("New Game"), ResetProc},
591     {N_("New Shuffle Game ..."), ShuffleMenuProc},
592     {N_("New Variant ..."), NewVariantProc},      // [HGM] variant: not functional yet
593     {"----", NothingProc},
594     {N_("Load Game"), LoadGameProc},
595     {N_("Load Next Game"), LoadNextGameProc},
596     {N_("Load Previous Game"), LoadPrevGameProc},
597     {N_("Reload Same Game"), ReloadGameProc},
598     {N_("Save Game"), SaveGameProc},
599     {"----", NothingProc},
600     {N_("Copy Game"), CopyGameProc},
601     {N_("Paste Game"), PasteGameProc},
602     {"----", NothingProc},
603     {N_("Load Position"), LoadPositionProc},
604     {N_("Load Next Position"), LoadNextPositionProc},
605     {N_("Load Previous Position"), LoadPrevPositionProc},
606     {N_("Reload Same Position"), ReloadPositionProc},
607     {N_("Save Position"), SavePositionProc},
608     {"----", NothingProc},
609     {N_("Copy Position"), CopyPositionProc},
610     {N_("Paste Position"), PastePositionProc},
611     {"----", NothingProc},
612     {N_("Mail Move"), MailMoveProc},
613     {N_("Reload CMail Message"), ReloadCmailMsgProc},
614     {"----", NothingProc},
615     {N_("Exit"), QuitProc},
616     {NULL, NULL}
617 };
618
619 MenuItem modeMenu[] = {
620     {N_("Machine White"), MachineWhiteProc},
621     {N_("Machine Black"), MachineBlackProc},
622     {N_("Two Machines"), TwoMachinesProc},
623     {N_("Analysis Mode"), AnalyzeModeProc},
624     {N_("Analyze File"), AnalyzeFileProc },
625     {N_("ICS Client"), IcsClientProc},
626     {N_("Edit Game"), EditGameProc},
627     {N_("Edit Position"), EditPositionProc},
628     {N_("Training"), TrainingProc},
629     {"----", NothingProc},
630     {N_("Show Engine Output"), EngineOutputProc},
631     {N_("Show Evaluation Graph"), EvalGraphProc},
632     {N_("Show Game List"), ShowGameListProc},
633     {N_("Show Move History"), HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
634     {"----", NothingProc},
635     {N_("Edit Tags"), EditTagsProc},
636     {N_("Edit Comment"), EditCommentProc},
637     {N_("ICS Input Box"), IcsInputBoxProc},
638     {N_("Pause"), PauseProc},
639     {NULL, NULL}
640 };
641
642 MenuItem actionMenu[] = {
643     {N_("Accept"), AcceptProc},
644     {N_("Decline"), DeclineProc},
645     {N_("Rematch"), RematchProc},
646     {"----", NothingProc},
647     {N_("Call Flag"), CallFlagProc},
648     {N_("Draw"), DrawProc},
649     {N_("Adjourn"), AdjournProc},
650     {N_("Abort"), AbortProc},
651     {N_("Resign"), ResignProc},
652     {"----", NothingProc},
653     {N_("Stop Observing"), StopObservingProc},
654     {N_("Stop Examining"), StopExaminingProc},
655     {N_("Upload to Examine"), UploadProc},
656     {"----", NothingProc},
657     {N_("Adjudicate to White"), AdjuWhiteProc},
658     {N_("Adjudicate to Black"), AdjuBlackProc},
659     {N_("Adjudicate Draw"), AdjuDrawProc},
660     {NULL, NULL}
661 };
662
663 MenuItem stepMenu[] = {
664     {N_("Backward"), BackwardProc},
665     {N_("Forward"), ForwardProc},
666     {N_("Back to Start"), ToStartProc},
667     {N_("Forward to End"), ToEndProc},
668     {N_("Revert"), RevertProc},
669     {N_("Annotate"), AnnotateProc},
670     {N_("Truncate Game"), TruncateGameProc},
671     {"----", NothingProc},
672     {N_("Move Now"), MoveNowProc},
673     {N_("Retract Move"), RetractMoveProc},
674     {NULL, NULL}
675 };
676
677 MenuItem optionsMenu[] = {
678     {N_("Flip View"), FlipViewProc},
679     {"----", NothingProc},
680     {N_("Adjudications ..."), EngineMenuProc},
681     {N_("General Settings ..."), UciMenuProc},
682     {N_("Engine #1 Settings ..."), FirstSettingsProc},
683     {N_("Engine #2 Settings ..."), SecondSettingsProc},
684     {N_("Time Control ..."), TimeControlProc},
685     {N_("Game List ..."), GameListOptionsPopUp},
686     {"----", NothingProc},
687     {N_("Always Queen"), AlwaysQueenProc},
688     {N_("Animate Dragging"), AnimateDraggingProc},
689     {N_("Animate Moving"), AnimateMovingProc},
690     {N_("Auto Comment"), AutocommProc},
691     {N_("Auto Flag"), AutoflagProc},
692     {N_("Auto Flip View"), AutoflipProc},
693     {N_("Auto Observe"), AutobsProc},
694     {N_("Auto Raise Board"), AutoraiseProc},
695     {N_("Auto Save"), AutosaveProc},
696     {N_("Blindfold"), BlindfoldProc},
697     {N_("Flash Moves"), FlashMovesProc},
698     {N_("Get Move List"), GetMoveListProc},
699 #if HIGHDRAG
700     {N_("Highlight Dragging"), HighlightDraggingProc},
701 #endif
702     {N_("Highlight Last Move"), HighlightLastMoveProc},
703     {N_("Move Sound"), MoveSoundProc},
704     {N_("ICS Alarm"), IcsAlarmProc},
705     {N_("Old Save Style"), OldSaveStyleProc},
706     {N_("Periodic Updates"), PeriodicUpdatesProc},
707     {N_("Ponder Next Move"), PonderNextMoveProc},
708     {N_("Popup Exit Message"), PopupExitMessageProc},
709     {N_("Popup Move Errors"), PopupMoveErrorsProc},
710     {N_("Premove"), PremoveProc},
711     {N_("Quiet Play"), QuietPlayProc},
712     {N_("Show Coords"), ShowCoordsProc},
713     {N_("Hide Thinking"), HideThinkingProc},
714     {N_("Test Legality"), TestLegalityProc},
715     {"----", NothingProc},
716     {N_("Save Settings Now"), SaveSettingsProc},
717     {N_("Save Settings on Exit"), SaveOnExitProc},
718     {NULL, NULL}
719 };
720
721 MenuItem helpMenu[] = {
722     {N_("Info XBoard"), InfoProc},
723     {N_("Man XBoard"), ManProc},
724     {"----", NothingProc},
725     {N_("Hint"), HintProc},
726     {N_("Book"), BookProc},
727     {"----", NothingProc},
728     {N_("About XBoard"), AboutProc},
729     {NULL, NULL}
730 };
731
732 Menu menuBar[] = {
733     {N_("File"), fileMenu},
734     {N_("Mode"), modeMenu},
735     {N_("Action"), actionMenu},
736     {N_("Step"), stepMenu},
737     {N_("Options"), optionsMenu},
738     {N_("Help"), helpMenu},
739     {NULL, NULL}
740 };
741
742 #define PAUSE_BUTTON N_("P")
743 MenuItem buttonBar[] = {
744     {"<<", ToStartProc},
745     {"<", BackwardProc},
746     {PAUSE_BUTTON, PauseProc},
747     {">", ForwardProc},
748     {">>", ToEndProc},
749     {NULL, NULL}
750 };
751
752 #define PIECE_MENU_SIZE 18
753 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
754     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
755       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
756       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
757       N_("Empty square"), N_("Clear board") },
758     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
759       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
760       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
761       N_("Empty square"), N_("Clear board") }
762 };
763 /* must be in same order as PieceMenuStrings! */
764 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
765     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
766         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
767         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0, 
768         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
769     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
770         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
771         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0, 
772         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
773 };
774
775 #define DROP_MENU_SIZE 6
776 String dropMenuStrings[DROP_MENU_SIZE] = {
777     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
778   };
779 /* must be in same order as PieceMenuStrings! */
780 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
781     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
782     WhiteRook, WhiteQueen
783 };
784
785 typedef struct {
786     char piece;
787     char* widget;
788 } DropMenuEnables;
789
790 DropMenuEnables dmEnables[] = {
791     { 'P', "Pawn" },
792     { 'N', "Knight" },
793     { 'B', "Bishop" },
794     { 'R', "Rook" },
795     { 'Q', "Queen" }
796 };
797
798 Arg shellArgs[] = {
799     { XtNwidth, 0 },
800     { XtNheight, 0 },
801     { XtNminWidth, 0 },
802     { XtNminHeight, 0 },
803     { XtNmaxWidth, 0 },
804     { XtNmaxHeight, 0 }
805 };
806
807 Arg layoutArgs[] = {
808     { XtNborderWidth, 0 },
809     { XtNdefaultDistance, 0 },
810 };
811
812 Arg formArgs[] = {
813     { XtNborderWidth, 0 },
814     { XtNresizable, (XtArgVal) True },
815 };
816
817 Arg boardArgs[] = {
818     { XtNborderWidth, 0 },
819     { XtNwidth, 0 },
820     { XtNheight, 0 }
821 };
822
823 Arg titleArgs[] = {
824     { XtNjustify, (XtArgVal) XtJustifyRight },
825     { XtNlabel, (XtArgVal) "..." },
826     { XtNresizable, (XtArgVal) True },
827     { XtNresize, (XtArgVal) False }
828 };
829
830 Arg messageArgs[] = {
831     { XtNjustify, (XtArgVal) XtJustifyLeft },
832     { XtNlabel, (XtArgVal) "..." },
833     { XtNresizable, (XtArgVal) True },
834     { XtNresize, (XtArgVal) False }
835 };
836
837 Arg timerArgs[] = {
838     { XtNborderWidth, 0 },
839     { XtNjustify, (XtArgVal) XtJustifyLeft }
840 };
841
842 XtResource clientResources[] = {
843     { "flashCount", "flashCount", XtRInt, sizeof(int),
844         XtOffset(AppDataPtr, flashCount), XtRImmediate,
845         (XtPointer) FLASH_COUNT  },
846 };
847
848 XrmOptionDescRec shellOptions[] = {
849     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
850     { "-flash", "flashCount", XrmoptionNoArg, "3" },
851     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
852 };
853
854 XtActionsRec boardActions[] = {
855     { "DrawPosition", DrawPositionProc },
856     { "HandleUserMove", HandleUserMove },
857     { "AnimateUserMove", AnimateUserMove },
858     { "HandlePV", HandlePV },
859     { "SelectPV", SelectPV },
860     { "StopPV", StopPV },
861     { "FileNameAction", FileNameAction },
862     { "AskQuestionProc", AskQuestionProc },
863     { "AskQuestionReplyAction", AskQuestionReplyAction },
864     { "PieceMenuPopup", PieceMenuPopup },
865     { "WhiteClock", WhiteClock },
866     { "BlackClock", BlackClock },
867     { "Iconify", Iconify },
868     { "ResetProc", ResetProc },
869     { "LoadGameProc", LoadGameProc },
870     { "LoadNextGameProc", LoadNextGameProc },
871     { "LoadPrevGameProc", LoadPrevGameProc },
872     { "LoadSelectedProc", LoadSelectedProc },
873     { "SetFilterProc", SetFilterProc },
874     { "ReloadGameProc", ReloadGameProc },
875     { "LoadPositionProc", LoadPositionProc },
876     { "LoadNextPositionProc", LoadNextPositionProc },
877     { "LoadPrevPositionProc", LoadPrevPositionProc },
878     { "ReloadPositionProc", ReloadPositionProc },
879     { "CopyPositionProc", CopyPositionProc },
880     { "PastePositionProc", PastePositionProc },
881     { "CopyGameProc", CopyGameProc },
882     { "PasteGameProc", PasteGameProc },
883     { "SaveGameProc", SaveGameProc },
884     { "SavePositionProc", SavePositionProc },
885     { "MailMoveProc", MailMoveProc },
886     { "ReloadCmailMsgProc", ReloadCmailMsgProc },
887     { "QuitProc", QuitProc },
888     { "MachineWhiteProc", MachineWhiteProc },
889     { "MachineBlackProc", MachineBlackProc },
890     { "AnalysisModeProc", AnalyzeModeProc },
891     { "AnalyzeFileProc", AnalyzeFileProc },
892     { "TwoMachinesProc", TwoMachinesProc },
893     { "IcsClientProc", IcsClientProc },
894     { "EditGameProc", EditGameProc },
895     { "EditPositionProc", EditPositionProc },
896     { "TrainingProc", EditPositionProc },
897     { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
898     { "EvalGraphProc", EvalGraphProc},       // [HGM] Winboard_x avaluation graph window
899     { "ShowGameListProc", ShowGameListProc },
900     { "ShowMoveListProc", HistoryShowProc},
901     { "EditTagsProc", EditCommentProc },
902     { "EditCommentProc", EditCommentProc },
903     { "IcsAlarmProc", IcsAlarmProc },
904     { "IcsInputBoxProc", IcsInputBoxProc },
905     { "PauseProc", PauseProc },
906     { "AcceptProc", AcceptProc },
907     { "DeclineProc", DeclineProc },
908     { "RematchProc", RematchProc },
909     { "CallFlagProc", CallFlagProc },
910     { "DrawProc", DrawProc },
911     { "AdjournProc", AdjournProc },
912     { "AbortProc", AbortProc },
913     { "ResignProc", ResignProc },
914     { "AdjuWhiteProc", AdjuWhiteProc },
915     { "AdjuBlackProc", AdjuBlackProc },
916     { "AdjuDrawProc", AdjuDrawProc },
917     { "EnterKeyProc", EnterKeyProc },
918     { "UpKeyProc", UpKeyProc },
919     { "DownKeyProc", DownKeyProc },
920     { "StopObservingProc", StopObservingProc },
921     { "StopExaminingProc", StopExaminingProc },
922     { "UploadProc", UploadProc },
923     { "BackwardProc", BackwardProc },
924     { "ForwardProc", ForwardProc },
925     { "ToStartProc", ToStartProc },
926     { "ToEndProc", ToEndProc },
927     { "RevertProc", RevertProc },
928     { "AnnotateProc", AnnotateProc },
929     { "TruncateGameProc", TruncateGameProc },
930     { "MoveNowProc", MoveNowProc },
931     { "RetractMoveProc", RetractMoveProc },
932     { "AlwaysQueenProc", AlwaysQueenProc },
933     { "AnimateDraggingProc", AnimateDraggingProc },
934     { "AnimateMovingProc", AnimateMovingProc },
935     { "AutoflagProc", AutoflagProc },
936     { "AutoflipProc", AutoflipProc },
937     { "AutobsProc", AutobsProc },
938     { "AutoraiseProc", AutoraiseProc },
939     { "AutosaveProc", AutosaveProc },
940     { "BlindfoldProc", BlindfoldProc },
941     { "FlashMovesProc", FlashMovesProc },
942     { "FlipViewProc", FlipViewProc },
943     { "GetMoveListProc", GetMoveListProc },
944 #if HIGHDRAG
945     { "HighlightDraggingProc", HighlightDraggingProc },
946 #endif
947     { "HighlightLastMoveProc", HighlightLastMoveProc },
948     { "IcsAlarmProc", IcsAlarmProc },
949     { "MoveSoundProc", MoveSoundProc },
950     { "OldSaveStyleProc", OldSaveStyleProc },
951     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
952     { "PonderNextMoveProc", PonderNextMoveProc },
953     { "PopupExitMessageProc", PopupExitMessageProc },
954     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
955     { "PremoveProc", PremoveProc },
956     { "QuietPlayProc", QuietPlayProc },
957     { "ShowCoordsProc", ShowCoordsProc },
958     { "ShowThinkingProc", ShowThinkingProc },
959     { "HideThinkingProc", HideThinkingProc },
960     { "TestLegalityProc", TestLegalityProc },
961     { "SaveSettingsProc", SaveSettingsProc },
962     { "SaveOnExitProc", SaveOnExitProc },
963     { "InfoProc", InfoProc },
964     { "ManProc", ManProc },
965     { "HintProc", HintProc },
966     { "BookProc", BookProc },
967     { "AboutGameProc", AboutGameProc },
968     { "AboutProc", AboutProc },
969     { "DebugProc", DebugProc },
970     { "NothingProc", NothingProc },
971     { "CommentClick", (XtActionProc) CommentClick },
972     { "CommentPopDown", (XtActionProc) CommentPopDown },
973     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
974     { "TagsPopDown", (XtActionProc) TagsPopDown },
975     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
976     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
977     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
978     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
979     { "GameListPopDown", (XtActionProc) GameListPopDown },
980     { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
981     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
982     { "HistoryPopDown", (XtActionProc) HistoryPopDown },
983     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
984     { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
985     { "ShufflePopDown", (XtActionProc) ShufflePopDown },
986     { "EnginePopDown", (XtActionProc) EnginePopDown },
987     { "UciPopDown", (XtActionProc) UciPopDown },
988     { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
989     { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
990     { "SettingsPopDown", (XtActionProc) SettingsPopDown },
991     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
992 };
993
994 char globalTranslations[] =
995   ":<Key>R: ResignProc() \n \
996    :<Key>r: ResetProc() \n \
997    :<Key>g: LoadGameProc() \n \
998    :<Key>N: LoadNextGameProc() \n \
999    :<Key>P: LoadPrevGameProc() \n \
1000    :<Key>Q: QuitProc() \n \
1001    :<Key>F: ToEndProc() \n \
1002    :<Key>f: ForwardProc() \n \
1003    :<Key>B: ToStartProc() \n \
1004    :<Key>b: BackwardProc() \n \
1005    :<Key>p: PauseProc() \n \
1006    :<Key>d: DrawProc() \n \
1007    :<Key>t: CallFlagProc() \n \
1008    :<Key>i: Iconify() \n \
1009    :<Key>c: Iconify() \n \
1010    :<Key>v: FlipViewProc() \n \
1011    <KeyDown>Control_L: BackwardProc() \n \
1012    <KeyUp>Control_L: ForwardProc() \n \
1013    <KeyDown>Control_R: BackwardProc() \n \
1014    <KeyUp>Control_R: ForwardProc() \n \
1015    Shift<Key>1: AskQuestionProc(\"Direct command\",\
1016                                 \"Send to chess program:\",,1) \n \
1017    Shift<Key>2: AskQuestionProc(\"Direct command\",\
1018                                 \"Send to second chess program:\",,2) \n";
1019
1020 char boardTranslations[] =
1021    "<Btn1Down>: HandleUserMove() \n \
1022    <Btn1Up>: HandleUserMove() \n \
1023    <Btn1Motion>: AnimateUserMove() \n \
1024    <Btn3Motion>: HandlePV() \n \
1025    <Btn3Up>: PieceMenuPopup(menuB) \n \
1026    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1027                  PieceMenuPopup(menuB) \n \
1028    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1029                  PieceMenuPopup(menuW) \n \
1030    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1031                  PieceMenuPopup(menuW) \n \
1032    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1033                  PieceMenuPopup(menuB) \n";
1034
1035 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1036 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1037
1038 char ICSInputTranslations[] =
1039     "<Key>Up: UpKeyProc() \n "
1040     "<Key>Down: DownKeyProc() \n "
1041     "<Key>Return: EnterKeyProc() \n";
1042
1043 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1044 //             as the widget is destroyed before the up-click can call extend-end
1045 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1046
1047 String xboardResources[] = {
1048     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1049     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1050     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1051     NULL
1052   };
1053
1054
1055 /* Max possible square size */
1056 #define MAXSQSIZE 256
1057
1058 static int xpm_avail[MAXSQSIZE];
1059
1060 #ifdef HAVE_DIR_STRUCT
1061
1062 /* Extract piece size from filename */
1063 static int
1064 xpm_getsize(name, len, ext)
1065      char *name;
1066      int len;
1067      char *ext;
1068 {
1069     char *p, *d;
1070     char buf[10];
1071
1072     if (len < 4)
1073       return 0;
1074
1075     if ((p=strchr(name, '.')) == NULL ||
1076         StrCaseCmp(p+1, ext) != 0)
1077       return 0;
1078
1079     p = name + 3;
1080     d = buf;
1081
1082     while (*p && isdigit(*p))
1083       *(d++) = *(p++);
1084
1085     *d = 0;
1086     return atoi(buf);
1087 }
1088
1089 /* Setup xpm_avail */
1090 static int
1091 xpm_getavail(dirname, ext)
1092      char *dirname;
1093      char *ext;
1094 {
1095     DIR *dir;
1096     struct dirent *ent;
1097     int  i;
1098
1099     for (i=0; i<MAXSQSIZE; ++i)
1100       xpm_avail[i] = 0;
1101
1102     if (appData.debugMode)
1103       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1104
1105     dir = opendir(dirname);
1106     if (!dir)
1107       {
1108           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1109                   programName, dirname);
1110           exit(1);
1111       }
1112
1113     while ((ent=readdir(dir)) != NULL) {
1114         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1115         if (i > 0 && i < MAXSQSIZE)
1116           xpm_avail[i] = 1;
1117     }
1118
1119     closedir(dir);
1120
1121     return 0;
1122 }
1123
1124 void
1125 xpm_print_avail(fp, ext)
1126      FILE *fp;
1127      char *ext;
1128 {
1129     int i;
1130
1131     fprintf(fp, _("Available `%s' sizes:\n"), ext);
1132     for (i=1; i<MAXSQSIZE; ++i) {
1133         if (xpm_avail[i])
1134           printf("%d\n", i);
1135     }
1136 }
1137
1138 /* Return XPM piecesize closest to size */
1139 int
1140 xpm_closest_to(dirname, size, ext)
1141      char *dirname;
1142      int size;
1143      char *ext;
1144 {
1145     int i;
1146     int sm_diff = MAXSQSIZE;
1147     int sm_index = 0;
1148     int diff;
1149
1150     xpm_getavail(dirname, ext);
1151
1152     if (appData.debugMode)
1153       xpm_print_avail(stderr, ext);
1154
1155     for (i=1; i<MAXSQSIZE; ++i) {
1156         if (xpm_avail[i]) {
1157             diff = size - i;
1158             diff = (diff<0) ? -diff : diff;
1159             if (diff < sm_diff) {
1160                 sm_diff = diff;
1161                 sm_index = i;
1162             }
1163         }
1164     }
1165
1166     if (!sm_index) {
1167         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1168         exit(1);
1169     }
1170
1171     return sm_index;
1172 }
1173 #else   /* !HAVE_DIR_STRUCT */
1174 /* If we are on a system without a DIR struct, we can't
1175    read the directory, so we can't collect a list of
1176    filenames, etc., so we can't do any size-fitting. */
1177 int
1178 xpm_closest_to(dirname, size, ext)
1179      char *dirname;
1180      int size;
1181      char *ext;
1182 {
1183     fprintf(stderr, _("\
1184 Warning: No DIR structure found on this system --\n\
1185          Unable to autosize for XPM/XIM pieces.\n\
1186    Please report this error to frankm@hiwaay.net.\n\
1187    Include system type & operating system in message.\n"));
1188     return size;
1189 }
1190 #endif /* HAVE_DIR_STRUCT */
1191
1192 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1193                              "magenta", "cyan", "white" };
1194 typedef struct {
1195     int attr, bg, fg;
1196 } TextColors;
1197 TextColors textColors[(int)NColorClasses];
1198
1199 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1200 static int
1201 parse_color(str, which)
1202      char *str;
1203      int which;
1204 {
1205     char *p, buf[100], *d;
1206     int i;
1207
1208     if (strlen(str) > 99)       /* watch bounds on buf */
1209       return -1;
1210
1211     p = str;
1212     d = buf;
1213     for (i=0; i<which; ++i) {
1214         p = strchr(p, ',');
1215         if (!p)
1216           return -1;
1217         ++p;
1218     }
1219
1220     /* Could be looking at something like:
1221        black, , 1
1222        .. in which case we want to stop on a comma also */
1223     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1224       ++p;
1225
1226     if (*p == ',') {
1227         return -1;              /* Use default for empty field */
1228     }
1229
1230     if (which == 2 || isdigit(*p))
1231       return atoi(p);
1232
1233     while (*p && isalpha(*p))
1234       *(d++) = *(p++);
1235
1236     *d = 0;
1237
1238     for (i=0; i<8; ++i) {
1239         if (!StrCaseCmp(buf, cnames[i]))
1240           return which? (i+40) : (i+30);
1241     }
1242     if (!StrCaseCmp(buf, "default")) return -1;
1243
1244     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1245     return -2;
1246 }
1247
1248 static int
1249 parse_cpair(cc, str)
1250      ColorClass cc;
1251      char *str;
1252 {
1253     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1254         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1255                 programName, str);
1256         return -1;
1257     }
1258
1259     /* bg and attr are optional */
1260     textColors[(int)cc].bg = parse_color(str, 1);
1261     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1262         textColors[(int)cc].attr = 0;
1263     }
1264     return 0;
1265 }
1266
1267
1268 /* Arrange to catch delete-window events */
1269 Atom wm_delete_window;
1270 void
1271 CatchDeleteWindow(Widget w, String procname)
1272 {
1273   char buf[MSG_SIZ];
1274   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1275   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1276   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1277 }
1278
1279 void
1280 BoardToTop()
1281 {
1282   Arg args[16];
1283   XtSetArg(args[0], XtNiconic, False);
1284   XtSetValues(shellWidget, args, 1);
1285
1286   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1287 }
1288
1289 //---------------------------------------------------------------------------------------------------------
1290 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1291 #define XBOARD True
1292 #define JAWS_ARGS
1293 #define CW_USEDEFAULT (1<<31)
1294 #define ICS_TEXT_MENU_SIZE 90
1295 #define DEBUG_FILE "xboard.debug"
1296 #define SetCurrentDirectory chdir
1297 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1298 #define OPTCHAR "-"
1299 #define SEPCHAR " "
1300
1301 // these two must some day move to frontend.h, when they are implemented
1302 Boolean GameListIsUp();
1303
1304 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1305 #include "args.h"
1306
1307 // front-end part of option handling
1308
1309 // [HGM] This platform-dependent table provides the location for storing the color info
1310 extern char *crWhite, * crBlack;
1311
1312 void *
1313 colorVariable[] = {
1314   &appData.whitePieceColor, 
1315   &appData.blackPieceColor, 
1316   &appData.lightSquareColor,
1317   &appData.darkSquareColor, 
1318   &appData.highlightSquareColor,
1319   &appData.premoveHighlightColor,
1320   &appData.lowTimeWarningColor,
1321   NULL,
1322   NULL,
1323   NULL,
1324   NULL,
1325   NULL,
1326   &crWhite,
1327   &crBlack,
1328   NULL
1329 };
1330
1331 // [HGM] font: keep a font for each square size, even non-stndard ones
1332 #define NUM_SIZES 18
1333 #define MAX_SIZE 130
1334 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1335 char *fontTable[NUM_FONTS][MAX_SIZE];
1336
1337 void
1338 ParseFont(char *name, int number)
1339 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1340   int size;
1341   if(sscanf(name, "size%d:", &size)) {
1342     // [HGM] font: font is meant for specific boardSize (likely from settings file);
1343     //       defer processing it until we know if it matches our board size
1344     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1345         fontTable[number][size] = strdup(strchr(name, ':')+1);
1346         fontValid[number][size] = True;
1347     }
1348     return;
1349   }
1350   switch(number) {
1351     case 0: // CLOCK_FONT
1352         appData.clockFont = strdup(name);
1353       break;
1354     case 1: // MESSAGE_FONT
1355         appData.font = strdup(name);
1356       break;
1357     case 2: // COORD_FONT
1358         appData.coordFont = strdup(name);
1359       break;
1360     default:
1361       return;
1362   }
1363   fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1364 }
1365
1366 void
1367 SetFontDefaults()
1368 { // only 2 fonts currently
1369   appData.clockFont = CLOCK_FONT_NAME;
1370   appData.coordFont = COORD_FONT_NAME;
1371   appData.font  =   DEFAULT_FONT_NAME;
1372 }
1373
1374 void
1375 CreateFonts()
1376 { // no-op, until we identify the code for this already in XBoard and move it here
1377 }
1378
1379 void
1380 ParseColor(int n, char *name)
1381 { // in XBoard, just copy the color-name string
1382   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1383 }
1384
1385 void
1386 ParseTextAttribs(ColorClass cc, char *s)
1387 {   
1388     (&appData.colorShout)[cc] = strdup(s);
1389 }
1390
1391 void
1392 ParseBoardSize(void *addr, char *name)
1393 {
1394     appData.boardSize = strdup(name);
1395 }
1396
1397 void
1398 LoadAllSounds()
1399 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1400 }
1401
1402 void
1403 SetCommPortDefaults()
1404 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1405 }
1406
1407 // [HGM] args: these three cases taken out to stay in front-end
1408 void
1409 SaveFontArg(FILE *f, ArgDescriptor *ad)
1410 {
1411   char *name, buf[MSG_SIZ];
1412   int i, n = (int)ad->argLoc;
1413   switch(n) {
1414     case 0: // CLOCK_FONT
1415         name = appData.clockFont;
1416       break;
1417     case 1: // MESSAGE_FONT
1418         name = appData.font;
1419       break;
1420     case 2: // COORD_FONT
1421         name = appData.coordFont;
1422       break;
1423     default:
1424       return;
1425   }
1426   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1427     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1428         fontTable[n][squareSize] = strdup(name);
1429         fontValid[n][squareSize] = True;
1430         break;
1431   }
1432   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1433     fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]); 
1434 }
1435
1436 void
1437 ExportSounds()
1438 { // nothing to do, as the sounds are at all times represented by their text-string names already
1439 }
1440
1441 void
1442 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1443 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1444         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1445 }
1446
1447 void
1448 SaveColor(FILE *f, ArgDescriptor *ad)
1449 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1450         if(colorVariable[(int)ad->argLoc])
1451         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1452 }
1453
1454 void
1455 SaveBoardSize(FILE *f, char *name, void *addr)
1456 { // wrapper to shield back-end from BoardSize & sizeInfo
1457   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1458 }
1459
1460 void
1461 ParseCommPortSettings(char *s)
1462 { // no such option in XBoard (yet)
1463 }
1464
1465 extern Widget engineOutputShell;
1466 extern Widget tagsShell, editTagsShell;
1467 void
1468 GetActualPlacement(Widget wg, WindowPlacement *wp)
1469 {
1470   Arg args[16];
1471   Dimension w, h;
1472   Position x, y;
1473   int i;
1474
1475   if(!wg) return;
1476   
1477     i = 0;
1478     XtSetArg(args[i], XtNx, &x); i++;
1479     XtSetArg(args[i], XtNy, &y); i++;
1480     XtSetArg(args[i], XtNwidth, &w); i++;
1481     XtSetArg(args[i], XtNheight, &h); i++;
1482     XtGetValues(wg, args, i);
1483     wp->x = x - 4;
1484     wp->y = y - 23;
1485     wp->height = h;
1486     wp->width = w;
1487 }
1488
1489 void
1490 GetWindowCoords()
1491 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1492   // In XBoard this will have to wait until awareness of window parameters is implemented
1493   GetActualPlacement(shellWidget, &wpMain);
1494   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1495   if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1496   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1497   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1498   if(commentShell) GetActualPlacement(commentShell, &wpComment);
1499   else             GetActualPlacement(editShell,    &wpComment);
1500   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1501   else      GetActualPlacement(editTagsShell, &wpTags);
1502 }
1503
1504 void
1505 PrintCommPortSettings(FILE *f, char *name)
1506 { // This option does not exist in XBoard
1507 }
1508
1509 int
1510 MySearchPath(char *installDir, char *name, char *fullname)
1511 { // just append installDir and name. Perhaps ExpandPath should be used here?
1512   name = ExpandPathName(name);
1513   if(name && name[0] == '/') strcpy(fullname, name); else {
1514     sprintf(fullname, "%s%c%s", installDir, '/', name);
1515   }
1516   return 1;
1517 }
1518
1519 int
1520 MyGetFullPathName(char *name, char *fullname)
1521 { // should use ExpandPath?
1522   name = ExpandPathName(name);
1523   strcpy(fullname, name);
1524   return 1;
1525 }
1526
1527 void
1528 EnsureOnScreen(int *x, int *y, int minX, int minY)
1529 {
1530   return;
1531 }
1532
1533 int
1534 MainWindowUp()
1535 { // [HGM] args: allows testing if main window is realized from back-end
1536   return xBoardWindow != 0;
1537 }
1538
1539 void
1540 PopUpStartupDialog()
1541 {  // start menu not implemented in XBoard
1542 }
1543 char *
1544 ConvertToLine(int argc, char **argv)
1545 {
1546   static char line[128*1024], buf[1024];
1547   int i;
1548
1549   line[0] = NULLCHAR;
1550   for(i=1; i<argc; i++) {
1551     if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1552         && argv[i][0] != '{' )
1553          sprintf(buf, "{%s} ", argv[i]);
1554     else sprintf(buf, "%s ", argv[i]);
1555     strcat(line, buf);
1556   }
1557     line[strlen(line)-1] = NULLCHAR;
1558   return line;
1559 }
1560
1561 //--------------------------------------------------------------------------------------------
1562
1563 extern Boolean twoBoards, partnerUp;
1564
1565 #ifdef IDSIZES
1566   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1567 #else
1568 #define BoardSize int
1569 void InitDrawingSizes(BoardSize boardSize, int flags)
1570 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1571     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1572     Arg args[16];
1573     XtGeometryResult gres;
1574     int i;
1575
1576     if(!formWidget) return;
1577
1578     /*
1579      * Enable shell resizing.
1580      */
1581     shellArgs[0].value = (XtArgVal) &w;
1582     shellArgs[1].value = (XtArgVal) &h;
1583     XtGetValues(shellWidget, shellArgs, 2);
1584
1585     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1586     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1587     XtSetValues(shellWidget, &shellArgs[2], 4);
1588
1589     XtSetArg(args[0], XtNdefaultDistance, &sep);
1590     XtGetValues(formWidget, args, 1);
1591
1592     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1593     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1594     CreateGrid();
1595     hOffset = boardWidth + 10;
1596     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1597         secondSegments[i] = gridSegments[i];
1598         secondSegments[i].x1 += hOffset;
1599         secondSegments[i].x2 += hOffset;
1600     }
1601
1602     XtSetArg(args[0], XtNwidth, boardWidth);
1603     XtSetArg(args[1], XtNheight, boardHeight);
1604     XtSetValues(boardWidget, args, 2);
1605
1606     timerWidth = (boardWidth - sep) / 2;
1607     XtSetArg(args[0], XtNwidth, timerWidth);
1608     XtSetValues(whiteTimerWidget, args, 1);
1609     XtSetValues(blackTimerWidget, args, 1);
1610
1611     XawFormDoLayout(formWidget, False);
1612
1613     if (appData.titleInWindow) {
1614         i = 0;
1615         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1616         XtSetArg(args[i], XtNheight, &h);  i++;
1617         XtGetValues(titleWidget, args, i);
1618         if (smallLayout) {
1619             w = boardWidth - 2*bor;
1620         } else {
1621             XtSetArg(args[0], XtNwidth, &w);
1622             XtGetValues(menuBarWidget, args, 1);
1623             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1624         }
1625
1626         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1627         if (gres != XtGeometryYes && appData.debugMode) {
1628             fprintf(stderr,
1629                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1630                     programName, gres, w, h, wr, hr);
1631         }
1632     }
1633
1634     XawFormDoLayout(formWidget, True);
1635
1636     /*
1637      * Inhibit shell resizing.
1638      */
1639     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1640     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1641     shellArgs[4].value = shellArgs[2].value = w;
1642     shellArgs[5].value = shellArgs[3].value = h;
1643     XtSetValues(shellWidget, &shellArgs[0], 6);
1644
1645     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1646     // (only for xpm)
1647     if(useImages) {
1648       for(i=0; i<4; i++) {
1649         int p;
1650         for(p=0; p<=(int)WhiteKing; p++)
1651            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1652         if(gameInfo.variant == VariantShogi) {
1653            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1654            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1655            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1656            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1657            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1658         }
1659 #ifdef GOTHIC
1660         if(gameInfo.variant == VariantGothic) {
1661            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1662         }
1663 #endif
1664 #if !HAVE_LIBXPM
1665         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1666         for(p=0; p<=(int)WhiteKing; p++)
1667            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1668         if(gameInfo.variant == VariantShogi) {
1669            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1670            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1671            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1672            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1673            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1674         }
1675 #ifdef GOTHIC
1676         if(gameInfo.variant == VariantGothic) {
1677            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1678         }
1679 #endif
1680 #endif
1681       }
1682     } else {
1683       for(i=0; i<2; i++) {
1684         int p;
1685         for(p=0; p<=(int)WhiteKing; p++)
1686            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1687         if(gameInfo.variant == VariantShogi) {
1688            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1689            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1690            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1691            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1692            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1693         }
1694 #ifdef GOTHIC
1695         if(gameInfo.variant == VariantGothic) {
1696            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1697         }
1698 #endif
1699       }
1700     }
1701 #if HAVE_LIBXPM
1702     CreateAnimVars();
1703 #endif
1704 }
1705 #endif
1706
1707 int
1708 main(argc, argv)
1709      int argc;
1710      char **argv;
1711 {
1712     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1713     XSetWindowAttributes window_attributes;
1714     Arg args[16];
1715     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1716     XrmValue vFrom, vTo;
1717     XtGeometryResult gres;
1718     char *p;
1719     XrmDatabase xdb;
1720     int forceMono = False;
1721
1722     srandom(time(0)); // [HGM] book: make random truly random
1723
1724     setbuf(stdout, NULL);
1725     setbuf(stderr, NULL);
1726     debugFP = stderr;
1727
1728     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1729         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1730         exit(0);
1731     }
1732
1733     programName = strrchr(argv[0], '/');
1734     if (programName == NULL)
1735       programName = argv[0];
1736     else
1737       programName++;
1738
1739 #ifdef ENABLE_NLS
1740     XtSetLanguageProc(NULL, NULL, NULL);
1741     bindtextdomain(PACKAGE, LOCALEDIR);
1742     textdomain(PACKAGE);
1743 #endif
1744
1745     shellWidget =
1746       XtAppInitialize(&appContext, "XBoard", shellOptions,
1747                       XtNumber(shellOptions),
1748                       &argc, argv, xboardResources, NULL, 0);
1749     appData.boardSize = "";
1750     InitAppData(ConvertToLine(argc, argv));
1751     p = getenv("HOME");
1752     if (p == NULL) p = "/tmp";
1753     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1754     gameCopyFilename = (char*) malloc(i);
1755     gamePasteFilename = (char*) malloc(i);
1756     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1757     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1758
1759     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1760                               clientResources, XtNumber(clientResources),
1761                               NULL, 0);
1762
1763     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1764         static char buf[MSG_SIZ];
1765         EscapeExpand(buf, appData.initString);
1766         appData.initString = strdup(buf);
1767         EscapeExpand(buf, appData.secondInitString);
1768         appData.secondInitString = strdup(buf);
1769         EscapeExpand(buf, appData.firstComputerString);
1770         appData.firstComputerString = strdup(buf);
1771         EscapeExpand(buf, appData.secondComputerString);
1772         appData.secondComputerString = strdup(buf);
1773     }
1774
1775     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1776         chessDir = ".";
1777     } else {
1778         if (chdir(chessDir) != 0) {
1779             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1780             perror(chessDir);
1781             exit(1);
1782         }
1783     }
1784
1785     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1786         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1787         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1788            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1789            exit(errno);
1790         }
1791         setbuf(debugFP, NULL);
1792     }
1793
1794     /* [HGM,HR] make sure board size is acceptable */
1795     if(appData.NrFiles > BOARD_FILES ||
1796        appData.NrRanks > BOARD_RANKS   )
1797          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1798
1799 #if !HIGHDRAG
1800     /* This feature does not work; animation needs a rewrite */
1801     appData.highlightDragging = FALSE;
1802 #endif
1803     InitBackEnd1();
1804
1805     xDisplay = XtDisplay(shellWidget);
1806     xScreen = DefaultScreen(xDisplay);
1807     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1808
1809         gameInfo.variant = StringToVariant(appData.variant);
1810         InitPosition(FALSE);
1811
1812 #ifdef IDSIZE
1813     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1814 #else
1815     if (isdigit(appData.boardSize[0])) {
1816         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1817                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1818                    &fontPxlSize, &smallLayout, &tinyLayout);
1819         if (i == 0) {
1820             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1821                     programName, appData.boardSize);
1822             exit(2);
1823         }
1824         if (i < 7) {
1825             /* Find some defaults; use the nearest known size */
1826             SizeDefaults *szd, *nearest;
1827             int distance = 99999;
1828             nearest = szd = sizeDefaults;
1829             while (szd->name != NULL) {
1830                 if (abs(szd->squareSize - squareSize) < distance) {
1831                     nearest = szd;
1832                     distance = abs(szd->squareSize - squareSize);
1833                     if (distance == 0) break;
1834                 }
1835                 szd++;
1836             }
1837             if (i < 2) lineGap = nearest->lineGap;
1838             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1839             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1840             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1841             if (i < 6) smallLayout = nearest->smallLayout;
1842             if (i < 7) tinyLayout = nearest->tinyLayout;
1843         }
1844     } else {
1845         SizeDefaults *szd = sizeDefaults;
1846         if (*appData.boardSize == NULLCHAR) {
1847             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1848                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1849               szd++;
1850             }
1851             if (szd->name == NULL) szd--;
1852             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1853         } else {
1854             while (szd->name != NULL &&
1855                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1856             if (szd->name == NULL) {
1857                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1858                         programName, appData.boardSize);
1859                 exit(2);
1860             }
1861         }
1862         squareSize = szd->squareSize;
1863         lineGap = szd->lineGap;
1864         clockFontPxlSize = szd->clockFontPxlSize;
1865         coordFontPxlSize = szd->coordFontPxlSize;
1866         fontPxlSize = szd->fontPxlSize;
1867         smallLayout = szd->smallLayout;
1868         tinyLayout = szd->tinyLayout;
1869         // [HGM] font: use defaults from settings file if available and not overruled
1870     }
1871     if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1872         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1873     if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1874         appData.font = fontTable[MESSAGE_FONT][squareSize];
1875     if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1876         appData.coordFont = fontTable[COORD_FONT][squareSize];
1877
1878     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1879     if (strlen(appData.pixmapDirectory) > 0) {
1880         p = ExpandPathName(appData.pixmapDirectory);
1881         if (!p) {
1882             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1883                    appData.pixmapDirectory);
1884             exit(1);
1885         }
1886         if (appData.debugMode) {
1887           fprintf(stderr, _("\
1888 XBoard square size (hint): %d\n\
1889 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1890         }
1891         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1892         if (appData.debugMode) {
1893             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1894         }
1895     }
1896
1897     /* [HR] height treated separately (hacked) */
1898     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1899     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1900     if (appData.showJail == 1) {
1901         /* Jail on top and bottom */
1902         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1903         XtSetArg(boardArgs[2], XtNheight,
1904                  boardHeight + 2*(lineGap + squareSize));
1905     } else if (appData.showJail == 2) {
1906         /* Jail on sides */
1907         XtSetArg(boardArgs[1], XtNwidth,
1908                  boardWidth + 2*(lineGap + squareSize));
1909         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1910     } else {
1911         /* No jail */
1912         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1913         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1914     }
1915
1916     /*
1917      * Determine what fonts to use.
1918      */
1919     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1920     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1921     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1922     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1923     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1924     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1925     appData.font = FindFont(appData.font, fontPxlSize);
1926     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1927     countFontStruct = XQueryFont(xDisplay, countFontID);
1928 //    appData.font = FindFont(appData.font, fontPxlSize);
1929
1930     xdb = XtDatabase(xDisplay);
1931     XrmPutStringResource(&xdb, "*font", appData.font);
1932
1933     /*
1934      * Detect if there are not enough colors available and adapt.
1935      */
1936     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1937       appData.monoMode = True;
1938     }
1939
1940     if (!appData.monoMode) {
1941         vFrom.addr = (caddr_t) appData.lightSquareColor;
1942         vFrom.size = strlen(appData.lightSquareColor);
1943         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1944         if (vTo.addr == NULL) {
1945           appData.monoMode = True;
1946           forceMono = True;
1947         } else {
1948           lightSquareColor = *(Pixel *) vTo.addr;
1949         }
1950     }
1951     if (!appData.monoMode) {
1952         vFrom.addr = (caddr_t) appData.darkSquareColor;
1953         vFrom.size = strlen(appData.darkSquareColor);
1954         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1955         if (vTo.addr == NULL) {
1956           appData.monoMode = True;
1957           forceMono = True;
1958         } else {
1959           darkSquareColor = *(Pixel *) vTo.addr;
1960         }
1961     }
1962     if (!appData.monoMode) {
1963         vFrom.addr = (caddr_t) appData.whitePieceColor;
1964         vFrom.size = strlen(appData.whitePieceColor);
1965         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1966         if (vTo.addr == NULL) {
1967           appData.monoMode = True;
1968           forceMono = True;
1969         } else {
1970           whitePieceColor = *(Pixel *) vTo.addr;
1971         }
1972     }
1973     if (!appData.monoMode) {
1974         vFrom.addr = (caddr_t) appData.blackPieceColor;
1975         vFrom.size = strlen(appData.blackPieceColor);
1976         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1977         if (vTo.addr == NULL) {
1978           appData.monoMode = True;
1979           forceMono = True;
1980         } else {
1981           blackPieceColor = *(Pixel *) vTo.addr;
1982         }
1983     }
1984
1985     if (!appData.monoMode) {
1986         vFrom.addr = (caddr_t) appData.highlightSquareColor;
1987         vFrom.size = strlen(appData.highlightSquareColor);
1988         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1989         if (vTo.addr == NULL) {
1990           appData.monoMode = True;
1991           forceMono = True;
1992         } else {
1993           highlightSquareColor = *(Pixel *) vTo.addr;
1994         }
1995     }
1996
1997     if (!appData.monoMode) {
1998         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1999         vFrom.size = strlen(appData.premoveHighlightColor);
2000         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2001         if (vTo.addr == NULL) {
2002           appData.monoMode = True;
2003           forceMono = True;
2004         } else {
2005           premoveHighlightColor = *(Pixel *) vTo.addr;
2006         }
2007     }
2008
2009     if (forceMono) {
2010       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2011               programName);
2012       
2013       if (appData.bitmapDirectory == NULL ||
2014               appData.bitmapDirectory[0] == NULLCHAR)
2015             appData.bitmapDirectory = DEF_BITMAP_DIR;
2016     }
2017
2018     if (appData.lowTimeWarning && !appData.monoMode) {
2019       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2020       vFrom.size = strlen(appData.lowTimeWarningColor);
2021       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2022       if (vTo.addr == NULL) 
2023                 appData.monoMode = True;
2024       else
2025                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2026     }
2027
2028     if (appData.monoMode && appData.debugMode) {
2029         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2030                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2031                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2032     }
2033
2034     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2035         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2036         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2037         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2038         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2039         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2040         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2041         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2042         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2043         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2044       {
2045           if (appData.colorize) {
2046               fprintf(stderr,
2047                       _("%s: can't parse color names; disabling colorization\n"),
2048                       programName);
2049           }
2050           appData.colorize = FALSE;
2051       }
2052     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2053     textColors[ColorNone].attr = 0;
2054
2055     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2056
2057     /*
2058      * widget hierarchy
2059      */
2060     if (tinyLayout) {
2061         layoutName = "tinyLayout";
2062     } else if (smallLayout) {
2063         layoutName = "smallLayout";
2064     } else {
2065         layoutName = "normalLayout";
2066     }
2067     /* Outer layoutWidget is there only to provide a name for use in
2068        resources that depend on the layout style */
2069     layoutWidget =
2070       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2071                             layoutArgs, XtNumber(layoutArgs));
2072     formWidget =
2073       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2074                             formArgs, XtNumber(formArgs));
2075     XtSetArg(args[0], XtNdefaultDistance, &sep);
2076     XtGetValues(formWidget, args, 1);
2077
2078     j = 0;
2079     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2080     XtSetArg(args[0], XtNtop,    XtChainTop);
2081     XtSetArg(args[1], XtNbottom, XtChainTop);
2082     XtSetArg(args[2], XtNright,  XtChainLeft);
2083     XtSetValues(menuBarWidget, args, 3);
2084
2085     widgetList[j++] = whiteTimerWidget =
2086       XtCreateWidget("whiteTime", labelWidgetClass,
2087                      formWidget, timerArgs, XtNumber(timerArgs));
2088     XtSetArg(args[0], XtNfont, clockFontStruct);
2089     XtSetArg(args[1], XtNtop,    XtChainTop);
2090     XtSetArg(args[2], XtNbottom, XtChainTop);
2091     XtSetValues(whiteTimerWidget, args, 3);
2092
2093     widgetList[j++] = blackTimerWidget =
2094       XtCreateWidget("blackTime", labelWidgetClass,
2095                      formWidget, timerArgs, XtNumber(timerArgs));
2096     XtSetArg(args[0], XtNfont, clockFontStruct);
2097     XtSetArg(args[1], XtNtop,    XtChainTop);
2098     XtSetArg(args[2], XtNbottom, XtChainTop);
2099     XtSetValues(blackTimerWidget, args, 3);
2100
2101     if (appData.titleInWindow) {
2102         widgetList[j++] = titleWidget =
2103           XtCreateWidget("title", labelWidgetClass, formWidget,
2104                          titleArgs, XtNumber(titleArgs));
2105         XtSetArg(args[0], XtNtop,    XtChainTop);
2106         XtSetArg(args[1], XtNbottom, XtChainTop);
2107         XtSetValues(titleWidget, args, 2);
2108     }
2109
2110     if (appData.showButtonBar) {
2111       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2112       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2113       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2114       XtSetArg(args[2], XtNtop,    XtChainTop);
2115       XtSetArg(args[3], XtNbottom, XtChainTop);
2116       XtSetValues(buttonBarWidget, args, 4);
2117     }
2118
2119     widgetList[j++] = messageWidget =
2120       XtCreateWidget("message", labelWidgetClass, formWidget,
2121                      messageArgs, XtNumber(messageArgs));
2122     XtSetArg(args[0], XtNtop,    XtChainTop);
2123     XtSetArg(args[1], XtNbottom, XtChainTop);
2124     XtSetValues(messageWidget, args, 2);
2125
2126     widgetList[j++] = boardWidget =
2127       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2128                      XtNumber(boardArgs));
2129
2130     XtManageChildren(widgetList, j);
2131
2132     timerWidth = (boardWidth - sep) / 2;
2133     XtSetArg(args[0], XtNwidth, timerWidth);
2134     XtSetValues(whiteTimerWidget, args, 1);
2135     XtSetValues(blackTimerWidget, args, 1);
2136
2137     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2138     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2139     XtGetValues(whiteTimerWidget, args, 2);
2140
2141     if (appData.showButtonBar) {
2142       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2143       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2144       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2145     }
2146
2147     /*
2148      * formWidget uses these constraints but they are stored
2149      * in the children.
2150      */
2151     i = 0;
2152     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2153     XtSetValues(menuBarWidget, args, i);
2154     if (appData.titleInWindow) {
2155         if (smallLayout) {
2156             i = 0;
2157             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2158             XtSetValues(whiteTimerWidget, args, i);
2159             i = 0;
2160             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2161             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2162             XtSetValues(blackTimerWidget, args, i);
2163             i = 0;
2164             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2165             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2166             XtSetValues(titleWidget, args, i);
2167             i = 0;
2168             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2169             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2170             XtSetValues(messageWidget, args, i);
2171             if (appData.showButtonBar) {
2172               i = 0;
2173               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2174               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2175               XtSetValues(buttonBarWidget, args, i);
2176             }
2177         } else {
2178             i = 0;
2179             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2180             XtSetValues(whiteTimerWidget, args, i);
2181             i = 0;
2182             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2183             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2184             XtSetValues(blackTimerWidget, args, i);
2185             i = 0;
2186             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2187             XtSetValues(titleWidget, args, i);
2188             i = 0;
2189             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2190             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2191             XtSetValues(messageWidget, args, i);
2192             if (appData.showButtonBar) {
2193               i = 0;
2194               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2195               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2196               XtSetValues(buttonBarWidget, args, i);
2197             }
2198         }
2199     } else {
2200         i = 0;
2201         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2202         XtSetValues(whiteTimerWidget, args, i);
2203         i = 0;
2204         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2205         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2206         XtSetValues(blackTimerWidget, args, i);
2207         i = 0;
2208         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2209         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2210         XtSetValues(messageWidget, args, i);
2211         if (appData.showButtonBar) {
2212           i = 0;
2213           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2214           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2215           XtSetValues(buttonBarWidget, args, i);
2216         }
2217     }
2218     i = 0;
2219     XtSetArg(args[0], XtNfromVert, messageWidget);
2220     XtSetArg(args[1], XtNtop,    XtChainTop);
2221     XtSetArg(args[2], XtNbottom, XtChainBottom);
2222     XtSetArg(args[3], XtNleft,   XtChainLeft);
2223     XtSetArg(args[4], XtNright,  XtChainRight);
2224     XtSetValues(boardWidget, args, 5);
2225
2226     XtRealizeWidget(shellWidget);
2227
2228     if(wpMain.x > 0) {
2229       XtSetArg(args[0], XtNx, wpMain.x);
2230       XtSetArg(args[1], XtNy, wpMain.y);
2231       XtSetValues(shellWidget, args, 2);
2232     }
2233
2234     /*
2235      * Correct the width of the message and title widgets.
2236      * It is not known why some systems need the extra fudge term.
2237      * The value "2" is probably larger than needed.
2238      */
2239     XawFormDoLayout(formWidget, False);
2240
2241 #define WIDTH_FUDGE 2
2242     i = 0;
2243     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2244     XtSetArg(args[i], XtNheight, &h);  i++;
2245     XtGetValues(messageWidget, args, i);
2246     if (appData.showButtonBar) {
2247       i = 0;
2248       XtSetArg(args[i], XtNwidth, &w);  i++;
2249       XtGetValues(buttonBarWidget, args, i);
2250       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2251     } else {
2252       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2253     }
2254
2255     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2256     if (gres != XtGeometryYes && appData.debugMode) {
2257       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2258               programName, gres, w, h, wr, hr);
2259     }
2260
2261     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2262     /* The size used for the child widget in layout lags one resize behind
2263        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2264     w--;
2265     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2266     if (gres != XtGeometryYes && appData.debugMode) {
2267       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2268               programName, gres, w, h, wr, hr);
2269     }
2270     /* !! end hack */
2271     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2272     XtSetArg(args[1], XtNright, XtChainRight);
2273     XtSetValues(messageWidget, args, 2);
2274
2275     if (appData.titleInWindow) {
2276         i = 0;
2277         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2278         XtSetArg(args[i], XtNheight, &h);  i++;
2279         XtGetValues(titleWidget, args, i);
2280         if (smallLayout) {
2281             w = boardWidth - 2*bor;
2282         } else {
2283             XtSetArg(args[0], XtNwidth, &w);
2284             XtGetValues(menuBarWidget, args, 1);
2285             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2286         }
2287
2288         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2289         if (gres != XtGeometryYes && appData.debugMode) {
2290             fprintf(stderr,
2291                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2292                     programName, gres, w, h, wr, hr);
2293         }
2294     }
2295     XawFormDoLayout(formWidget, True);
2296
2297     xBoardWindow = XtWindow(boardWidget);
2298
2299     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2300     //       not need to go into InitDrawingSizes().
2301 #endif
2302
2303     /*
2304      * Create X checkmark bitmap and initialize option menu checks.
2305      */
2306     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2307                checkmark_bits, checkmark_width, checkmark_height);
2308     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2309     if (appData.alwaysPromoteToQueen) {
2310         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2311                     args, 1);
2312     }
2313     if (appData.animateDragging) {
2314         XtSetValues(XtNameToWidget(menuBarWidget,
2315                                    "menuOptions.Animate Dragging"),
2316                     args, 1);
2317     }
2318     if (appData.animate) {
2319         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2320                     args, 1);
2321     }
2322     if (appData.autoComment) {
2323         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2324                     args, 1);
2325     }
2326     if (appData.autoCallFlag) {
2327         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2328                     args, 1);
2329     }
2330     if (appData.autoFlipView) {
2331         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2332                     args, 1);
2333     }
2334     if (appData.autoObserve) {
2335         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2336                     args, 1);
2337     }
2338     if (appData.autoRaiseBoard) {
2339         XtSetValues(XtNameToWidget(menuBarWidget,
2340                                    "menuOptions.Auto Raise Board"), args, 1);
2341     }
2342     if (appData.autoSaveGames) {
2343         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2344                     args, 1);
2345     }
2346     if (appData.saveGameFile[0] != NULLCHAR) {
2347         /* Can't turn this off from menu */
2348         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2349                     args, 1);
2350         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2351                        False);
2352
2353     }
2354     if (appData.blindfold) {
2355         XtSetValues(XtNameToWidget(menuBarWidget,
2356                                    "menuOptions.Blindfold"), args, 1);
2357     }
2358     if (appData.flashCount > 0) {
2359         XtSetValues(XtNameToWidget(menuBarWidget,
2360                                    "menuOptions.Flash Moves"),
2361                     args, 1);
2362     }
2363     if (appData.getMoveList) {
2364         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2365                     args, 1);
2366     }
2367 #if HIGHDRAG
2368     if (appData.highlightDragging) {
2369         XtSetValues(XtNameToWidget(menuBarWidget,
2370                                    "menuOptions.Highlight Dragging"),
2371                     args, 1);
2372     }
2373 #endif
2374     if (appData.highlightLastMove) {
2375         XtSetValues(XtNameToWidget(menuBarWidget,
2376                                    "menuOptions.Highlight Last Move"),
2377                     args, 1);
2378     }
2379     if (appData.icsAlarm) {
2380         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2381                     args, 1);
2382     }
2383     if (appData.ringBellAfterMoves) {
2384         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2385                     args, 1);
2386     }
2387     if (appData.oldSaveStyle) {
2388         XtSetValues(XtNameToWidget(menuBarWidget,
2389                                    "menuOptions.Old Save Style"), args, 1);
2390     }
2391     if (appData.periodicUpdates) {
2392         XtSetValues(XtNameToWidget(menuBarWidget,
2393                                    "menuOptions.Periodic Updates"), args, 1);
2394     }
2395     if (appData.ponderNextMove) {
2396         XtSetValues(XtNameToWidget(menuBarWidget,
2397                                    "menuOptions.Ponder Next Move"), args, 1);
2398     }
2399     if (appData.popupExitMessage) {
2400         XtSetValues(XtNameToWidget(menuBarWidget,
2401                                    "menuOptions.Popup Exit Message"), args, 1);
2402     }
2403     if (appData.popupMoveErrors) {
2404         XtSetValues(XtNameToWidget(menuBarWidget,
2405                                    "menuOptions.Popup Move Errors"), args, 1);
2406     }
2407     if (appData.premove) {
2408         XtSetValues(XtNameToWidget(menuBarWidget,
2409                                    "menuOptions.Premove"), args, 1);
2410     }
2411     if (appData.quietPlay) {
2412         XtSetValues(XtNameToWidget(menuBarWidget,
2413                                    "menuOptions.Quiet Play"), args, 1);
2414     }
2415     if (appData.showCoords) {
2416         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2417                     args, 1);
2418     }
2419     if (appData.hideThinkingFromHuman) {
2420         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2421                     args, 1);
2422     }
2423     if (appData.testLegality) {
2424         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2425                     args, 1);
2426     }
2427     if (saveSettingsOnExit) {
2428         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2429                     args, 1);
2430     }
2431
2432     /*
2433      * Create an icon.
2434      */
2435     ReadBitmap(&wIconPixmap, "icon_white.bm",
2436                icon_white_bits, icon_white_width, icon_white_height);
2437     ReadBitmap(&bIconPixmap, "icon_black.bm",
2438                icon_black_bits, icon_black_width, icon_black_height);
2439     iconPixmap = wIconPixmap;
2440     i = 0;
2441     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2442     XtSetValues(shellWidget, args, i);
2443
2444     /*
2445      * Create a cursor for the board widget.
2446      */
2447     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2448     XChangeWindowAttributes(xDisplay, xBoardWindow,
2449                             CWCursor, &window_attributes);
2450
2451     /*
2452      * Inhibit shell resizing.
2453      */
2454     shellArgs[0].value = (XtArgVal) &w;
2455     shellArgs[1].value = (XtArgVal) &h;
2456     XtGetValues(shellWidget, shellArgs, 2);
2457     shellArgs[4].value = shellArgs[2].value = w;
2458     shellArgs[5].value = shellArgs[3].value = h;
2459     XtSetValues(shellWidget, &shellArgs[2], 4);
2460     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2461     marginH =  h - boardHeight;
2462
2463     CatchDeleteWindow(shellWidget, "QuitProc");
2464
2465     CreateGCs();
2466     CreateGrid();
2467 #if HAVE_LIBXPM
2468     if (appData.bitmapDirectory[0] != NULLCHAR) {
2469       CreatePieces();
2470     } else {
2471       CreateXPMPieces();
2472     }
2473 #else
2474     CreateXIMPieces();
2475     /* Create regular pieces */
2476     if (!useImages) CreatePieces();
2477 #endif
2478
2479     CreatePieceMenus();
2480
2481     if (appData.animate || appData.animateDragging)
2482       CreateAnimVars();
2483
2484     XtAugmentTranslations(formWidget,
2485                           XtParseTranslationTable(globalTranslations));
2486     XtAugmentTranslations(boardWidget,
2487                           XtParseTranslationTable(boardTranslations));
2488     XtAugmentTranslations(whiteTimerWidget,
2489                           XtParseTranslationTable(whiteTranslations));
2490     XtAugmentTranslations(blackTimerWidget,
2491                           XtParseTranslationTable(blackTranslations));
2492
2493     /* Why is the following needed on some versions of X instead
2494      * of a translation? */
2495     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2496                       (XtEventHandler) EventProc, NULL);
2497     /* end why */
2498
2499     /* [AS] Restore layout */
2500     if( wpMoveHistory.visible ) {
2501       HistoryPopUp();
2502     }
2503
2504     if( wpEvalGraph.visible ) 
2505       {
2506         EvalGraphPopUp();
2507       };
2508     
2509     if( wpEngineOutput.visible ) {
2510       EngineOutputPopUp();
2511     }
2512
2513     InitBackEnd2();
2514
2515     if (errorExitStatus == -1) {
2516         if (appData.icsActive) {
2517             /* We now wait until we see "login:" from the ICS before
2518                sending the logon script (problems with timestamp otherwise) */
2519             /*ICSInitScript();*/
2520             if (appData.icsInputBox) ICSInputBoxPopUp();
2521         }
2522
2523     #ifdef SIGWINCH
2524     signal(SIGWINCH, TermSizeSigHandler);
2525     #endif
2526         signal(SIGINT, IntSigHandler);
2527         signal(SIGTERM, IntSigHandler);
2528         if (*appData.cmailGameName != NULLCHAR) {
2529             signal(SIGUSR1, CmailSigHandler);
2530         }
2531     }
2532     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2533     InitPosition(TRUE);
2534     XtSetKeyboardFocus(shellWidget, formWidget);
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     XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
4643
4644     if (mutable) {
4645         j = 0;
4646         XtSetArg(args[j], XtNfromVert, edit);  j++;
4647         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4648         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4649         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4650         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4651         b_ok =
4652           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4653         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4654
4655         j = 0;
4656         XtSetArg(args[j], XtNfromVert, edit);  j++;
4657         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4658         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4659         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4660         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4661         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4662         b_cancel =
4663           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4664         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4665
4666         j = 0;
4667         XtSetArg(args[j], XtNfromVert, edit);  j++;
4668         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4669         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4670         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4671         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4672         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4673         b_clear =
4674           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4675         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4676     } else {
4677         j = 0;
4678         XtSetArg(args[j], XtNfromVert, edit);  j++;
4679         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4680         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4681         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4682         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4683         b_close =
4684           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4685         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4686
4687         j = 0;
4688         XtSetArg(args[j], XtNfromVert, edit);  j++;
4689         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4690         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4691         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4692         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4693         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4694         b_edit =
4695           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4696         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4697     }
4698
4699     XtRealizeWidget(shell);
4700
4701     if (commentX == -1) {
4702         int xx, yy;
4703         Window junk;
4704         Dimension pw_height;
4705         Dimension ew_height;
4706
4707         j = 0;
4708         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4709         XtGetValues(edit, args, j);
4710
4711         j = 0;
4712         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4713         XtGetValues(shell, args, j);
4714         commentH = pw_height + (lines - 1) * ew_height;
4715         commentW = bw_width - 16;
4716
4717         XSync(xDisplay, False);
4718 #ifdef NOTDEF
4719         /* This code seems to tickle an X bug if it is executed too soon
4720            after xboard starts up.  The coordinates get transformed as if
4721            the main window was positioned at (0, 0).
4722            */
4723         XtTranslateCoords(shellWidget,
4724                           (bw_width - commentW) / 2, 0 - commentH / 2,
4725                           &commentX, &commentY);
4726 #else  /*!NOTDEF*/
4727         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4728                               RootWindowOfScreen(XtScreen(shellWidget)),
4729                               (bw_width - commentW) / 2, 0 - commentH / 2,
4730                               &xx, &yy, &junk);
4731         commentX = xx;
4732         commentY = yy;
4733 #endif /*!NOTDEF*/
4734         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4735     }
4736
4737     if(wpComment.width > 0) {
4738       commentX = wpComment.x;
4739       commentY = wpComment.y;
4740       commentW = wpComment.width;
4741       commentH = wpComment.height;
4742     }
4743
4744     j = 0;
4745     XtSetArg(args[j], XtNheight, commentH);  j++;
4746     XtSetArg(args[j], XtNwidth, commentW);  j++;
4747     XtSetArg(args[j], XtNx, commentX);  j++;
4748     XtSetArg(args[j], XtNy, commentY);  j++;
4749     XtSetValues(shell, args, j);
4750     XtSetKeyboardFocus(shell, edit);
4751
4752     return shell;
4753 }
4754
4755 /* Used for analysis window and ICS input window */
4756 Widget MiscCreate(name, text, mutable, callback, lines)
4757      char *name, *text;
4758      int /*Boolean*/ mutable;
4759      XtCallbackProc callback;
4760      int lines;
4761 {
4762     Arg args[16];
4763     Widget shell, layout, form, edit;
4764     Position x, y;
4765     Dimension bw_width, pw_height, ew_height, w, h;
4766     int j;
4767     int xx, yy;
4768     Window junk;
4769
4770     j = 0;
4771     XtSetArg(args[j], XtNresizable, True);  j++;
4772 #if TOPLEVEL
4773     shell =
4774       XtCreatePopupShell(name, topLevelShellWidgetClass,
4775                          shellWidget, args, j);
4776 #else
4777     shell =
4778       XtCreatePopupShell(name, transientShellWidgetClass,
4779                          shellWidget, args, j);
4780 #endif
4781     layout =
4782       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4783                             layoutArgs, XtNumber(layoutArgs));
4784     form =
4785       XtCreateManagedWidget("form", formWidgetClass, layout,
4786                             formArgs, XtNumber(formArgs));
4787
4788     j = 0;
4789     if (mutable) {
4790         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4791         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4792     }
4793     XtSetArg(args[j], XtNstring, text);  j++;
4794     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4795     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4796     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4797     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4798     XtSetArg(args[j], XtNresizable, True);  j++;
4799     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4800     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4801     XtSetArg(args[j], XtNautoFill, True);  j++;
4802     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4803     edit =
4804       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4805
4806     XtRealizeWidget(shell);
4807
4808     j = 0;
4809     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4810     XtGetValues(boardWidget, args, j);
4811
4812     j = 0;
4813     XtSetArg(args[j], XtNheight, &ew_height);  j++;
4814     XtGetValues(edit, args, j);
4815
4816     j = 0;
4817     XtSetArg(args[j], XtNheight, &pw_height);  j++;
4818     XtGetValues(shell, args, j);
4819     h = pw_height + (lines - 1) * ew_height;
4820     w = bw_width - 16;
4821
4822     XSync(xDisplay, False);
4823 #ifdef NOTDEF
4824     /* This code seems to tickle an X bug if it is executed too soon
4825        after xboard starts up.  The coordinates get transformed as if
4826        the main window was positioned at (0, 0).
4827     */
4828     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4829 #else  /*!NOTDEF*/
4830     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4831                           RootWindowOfScreen(XtScreen(shellWidget)),
4832                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4833 #endif /*!NOTDEF*/
4834     x = xx;
4835     y = yy;
4836     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4837
4838     j = 0;
4839     XtSetArg(args[j], XtNheight, h);  j++;
4840     XtSetArg(args[j], XtNwidth, w);  j++;
4841     XtSetArg(args[j], XtNx, x);  j++;
4842     XtSetArg(args[j], XtNy, y);  j++;
4843     XtSetValues(shell, args, j);
4844
4845     return shell;
4846 }
4847
4848
4849 static int savedIndex;  /* gross that this is global */
4850
4851 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4852 {
4853         String val;
4854         XawTextPosition index, dummy;
4855         Arg arg;
4856
4857         XawTextGetSelectionPos(w, &index, &dummy);
4858         XtSetArg(arg, XtNstring, &val);
4859         XtGetValues(w, &arg, 1);
4860         ReplaceComment(savedIndex, val);
4861         if(savedIndex != currentMove) ToNrEvent(savedIndex);
4862         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4863 }
4864
4865 void EditCommentPopUp(index, title, text)
4866      int index;
4867      char *title, *text;
4868 {
4869     Widget edit;
4870     Arg args[16];
4871     int j;
4872
4873     savedIndex = index;
4874     if (text == NULL) text = "";
4875
4876     if (editShell == NULL) {
4877         editShell =
4878           CommentCreate(title, text, True, EditCommentCallback, 4);
4879         XtRealizeWidget(editShell);
4880         CatchDeleteWindow(editShell, "EditCommentPopDown");
4881     } else {
4882         edit = XtNameToWidget(editShell, "*form.text");
4883         j = 0;
4884         XtSetArg(args[j], XtNstring, text); j++;
4885         XtSetValues(edit, args, j);
4886         j = 0;
4887         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4888         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4889         XtSetValues(editShell, args, j);
4890     }
4891
4892     XtPopup(editShell, XtGrabNone);
4893
4894     editUp = True;
4895     j = 0;
4896     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4897     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4898                 args, j);
4899 }
4900
4901 void EditCommentCallback(w, client_data, call_data)
4902      Widget w;
4903      XtPointer client_data, call_data;
4904 {
4905     String name, val;
4906     Arg args[16];
4907     int j;
4908     Widget edit;
4909
4910     j = 0;
4911     XtSetArg(args[j], XtNlabel, &name);  j++;
4912     XtGetValues(w, args, j);
4913
4914     if (strcmp(name, _("ok")) == 0) {
4915         edit = XtNameToWidget(editShell, "*form.text");
4916         j = 0;
4917         XtSetArg(args[j], XtNstring, &val); j++;
4918         XtGetValues(edit, args, j);
4919         ReplaceComment(savedIndex, val);
4920         EditCommentPopDown();
4921     } else if (strcmp(name, _("cancel")) == 0) {
4922         EditCommentPopDown();
4923     } else if (strcmp(name, _("clear")) == 0) {
4924         edit = XtNameToWidget(editShell, "*form.text");
4925         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4926         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4927     }
4928 }
4929
4930 void EditCommentPopDown()
4931 {
4932     Arg args[16];
4933     int j;
4934
4935     if (!editUp) return;
4936     j = 0;
4937     XtSetArg(args[j], XtNx, &commentX); j++;
4938     XtSetArg(args[j], XtNy, &commentY); j++;
4939     XtSetArg(args[j], XtNheight, &commentH); j++;
4940     XtSetArg(args[j], XtNwidth, &commentW); j++;
4941     XtGetValues(editShell, args, j);
4942     XtPopdown(editShell);
4943     editUp = False;
4944     j = 0;
4945     XtSetArg(args[j], XtNleftBitmap, None); j++;
4946     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4947                 args, j);
4948 }
4949
4950 void ICSInputBoxPopUp()
4951 {
4952     Widget edit;
4953     Arg args[16];
4954     int j;
4955     char *title = _("ICS Input");
4956     XtTranslations tr;
4957
4958     if (ICSInputShell == NULL) {
4959         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
4960         tr = XtParseTranslationTable(ICSInputTranslations);
4961         edit = XtNameToWidget(ICSInputShell, "*form.text");
4962         XtOverrideTranslations(edit, tr);
4963         XtRealizeWidget(ICSInputShell);
4964         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
4965
4966     } else {
4967         edit = XtNameToWidget(ICSInputShell, "*form.text");
4968         j = 0;
4969         XtSetArg(args[j], XtNstring, ""); j++;
4970         XtSetValues(edit, args, j);
4971         j = 0;
4972         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4973         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4974         XtSetValues(ICSInputShell, args, j);
4975     }
4976
4977     XtPopup(ICSInputShell, XtGrabNone);
4978     XtSetKeyboardFocus(ICSInputShell, edit);
4979
4980     ICSInputBoxUp = True;
4981     j = 0;
4982     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4983     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4984                 args, j);
4985 }
4986
4987 void ICSInputSendText()
4988 {
4989     Widget edit;
4990     int j;
4991     Arg args[16];
4992     String val;
4993
4994     edit = XtNameToWidget(ICSInputShell, "*form.text");
4995     j = 0;
4996     XtSetArg(args[j], XtNstring, &val); j++;
4997     XtGetValues(edit, args, j);
4998     SaveInHistory(val);
4999     SendMultiLineToICS(val);
5000     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5001     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5002 }
5003
5004 void ICSInputBoxPopDown()
5005 {
5006     Arg args[16];
5007     int j;
5008
5009     if (!ICSInputBoxUp) return;
5010     j = 0;
5011     XtPopdown(ICSInputShell);
5012     ICSInputBoxUp = False;
5013     j = 0;
5014     XtSetArg(args[j], XtNleftBitmap, None); j++;
5015     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5016                 args, j);
5017 }
5018
5019 void CommentPopUp(title, text)
5020      char *title, *text;
5021 {
5022     Arg args[16];
5023     int j;
5024     Widget edit;
5025
5026     savedIndex = currentMove; // [HGM] vari
5027     if (commentShell == NULL) {
5028         commentShell =
5029           CommentCreate(title, text, False, CommentCallback, 4);
5030         XtRealizeWidget(commentShell);
5031         CatchDeleteWindow(commentShell, "CommentPopDown");
5032     } else {
5033         edit = XtNameToWidget(commentShell, "*form.text");
5034         j = 0;
5035         XtSetArg(args[j], XtNstring, text); j++;
5036         XtSetValues(edit, args, j);
5037         j = 0;
5038         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5039         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5040         XtSetValues(commentShell, args, j);
5041     }
5042
5043     XtPopup(commentShell, XtGrabNone);
5044     XSync(xDisplay, False);
5045
5046     commentUp = True;
5047 }
5048
5049 void CommentCallback(w, client_data, call_data)
5050      Widget w;
5051      XtPointer client_data, call_data;
5052 {
5053     String name;
5054     Arg args[16];
5055     int j;
5056
5057     j = 0;
5058     XtSetArg(args[j], XtNlabel, &name);  j++;
5059     XtGetValues(w, args, j);
5060
5061     if (strcmp(name, _("close")) == 0) {
5062         CommentPopDown();
5063     } else if (strcmp(name, _("edit")) == 0) {
5064         CommentPopDown();
5065         EditCommentEvent();
5066     }
5067 }
5068
5069
5070 void CommentPopDown()
5071 {
5072     Arg args[16];
5073     int j;
5074
5075     if (!commentUp) return;
5076     j = 0;
5077     XtSetArg(args[j], XtNx, &commentX); j++;
5078     XtSetArg(args[j], XtNy, &commentY); j++;
5079     XtSetArg(args[j], XtNwidth, &commentW); j++;
5080     XtSetArg(args[j], XtNheight, &commentH); j++;
5081     XtGetValues(commentShell, args, j);
5082     XtPopdown(commentShell);
5083     XSync(xDisplay, False);
5084     commentUp = False;
5085 }
5086
5087 void FileNamePopUp(label, def, proc, openMode)
5088      char *label;
5089      char *def;
5090      FileProc proc;
5091      char *openMode;
5092 {
5093     Arg args[16];
5094     Widget popup, layout, dialog, edit;
5095     Window root, child;
5096     int x, y, i;
5097     int win_x, win_y;
5098     unsigned int mask;
5099
5100     fileProc = proc;            /* I can't see a way not */
5101     fileOpenMode = openMode;    /*   to use globals here */
5102     {   // [HGM] use file-selector dialog stolen from Ghostview
5103         char *name;
5104         int index; // this is not supported yet
5105         FILE *f;
5106         if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5107             NULL, openMode, NULL, &name))
5108                 (void) (*fileProc)(f, index=0, name);
5109     }
5110 }
5111
5112 void FileNamePopDown()
5113 {
5114     if (!filenameUp) return;
5115     XtPopdown(fileNameShell);
5116     XtDestroyWidget(fileNameShell);
5117     filenameUp = False;
5118     ModeHighlight();
5119 }
5120
5121 void FileNameCallback(w, client_data, call_data)
5122      Widget w;
5123      XtPointer client_data, call_data;
5124 {
5125     String name;
5126     Arg args[16];
5127
5128     XtSetArg(args[0], XtNlabel, &name);
5129     XtGetValues(w, args, 1);
5130
5131     if (strcmp(name, _("cancel")) == 0) {
5132         FileNamePopDown();
5133         return;
5134     }
5135
5136     FileNameAction(w, NULL, NULL, NULL);
5137 }
5138
5139 void FileNameAction(w, event, prms, nprms)
5140      Widget w;
5141      XEvent *event;
5142      String *prms;
5143      Cardinal *nprms;
5144 {
5145     char buf[MSG_SIZ];
5146     String name;
5147     FILE *f;
5148     char *p, *fullname;
5149     int index;
5150
5151     name = XawDialogGetValueString(w = XtParent(w));
5152
5153     if ((name != NULL) && (*name != NULLCHAR)) {
5154         strcpy(buf, name);
5155         XtPopdown(w = XtParent(XtParent(w)));
5156         XtDestroyWidget(w);
5157         filenameUp = False;
5158
5159         p = strrchr(buf, ' ');
5160         if (p == NULL) {
5161             index = 0;
5162         } else {
5163             *p++ = NULLCHAR;
5164             index = atoi(p);
5165         }
5166         fullname = ExpandPathName(buf);
5167         if (!fullname) {
5168             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5169         }
5170         else {
5171             f = fopen(fullname, fileOpenMode);
5172             if (f == NULL) {
5173                 DisplayError(_("Failed to open file"), errno);
5174             } else {
5175                 (void) (*fileProc)(f, index, buf);
5176             }
5177         }
5178         ModeHighlight();
5179         return;
5180     }
5181
5182     XtPopdown(w = XtParent(XtParent(w)));
5183     XtDestroyWidget(w);
5184     filenameUp = False;
5185     ModeHighlight();
5186 }
5187
5188 void PromotionPopUp()
5189 {
5190     Arg args[16];
5191     Widget dialog, layout;
5192     Position x, y;
5193     Dimension bw_width, pw_width;
5194     int j;
5195
5196     j = 0;
5197     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5198     XtGetValues(boardWidget, args, j);
5199
5200     j = 0;
5201     XtSetArg(args[j], XtNresizable, True); j++;
5202     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5203     promotionShell =
5204       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5205                          shellWidget, args, j);
5206     layout =
5207       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5208                             layoutArgs, XtNumber(layoutArgs));
5209
5210     j = 0;
5211     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5212     XtSetArg(args[j], XtNborderWidth, 0); j++;
5213     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5214                                    layout, args, j);
5215
5216   if(gameInfo.variant != VariantShogi) {
5217     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5218                        (XtPointer) dialog);
5219     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5220                        (XtPointer) dialog);
5221     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5222                        (XtPointer) dialog);
5223     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5224                        (XtPointer) dialog);
5225     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5226         gameInfo.variant == VariantGiveaway) {
5227       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5228                          (XtPointer) dialog);
5229     }
5230     if(gameInfo.variant == VariantCapablanca || 
5231        gameInfo.variant == VariantGothic || 
5232        gameInfo.variant == VariantCapaRandom) {
5233       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5234                          (XtPointer) dialog);
5235       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5236                          (XtPointer) dialog);
5237     }
5238   } else // [HGM] shogi
5239   {
5240       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5241                          (XtPointer) dialog);
5242       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5243                          (XtPointer) dialog);
5244   }
5245     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5246                        (XtPointer) dialog);
5247
5248     XtRealizeWidget(promotionShell);
5249     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5250
5251     j = 0;
5252     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5253     XtGetValues(promotionShell, args, j);
5254
5255     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5256                       lineGap + squareSize/3 +
5257                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5258                        0 : 6*(squareSize + lineGap)), &x, &y);
5259
5260     j = 0;
5261     XtSetArg(args[j], XtNx, x); j++;
5262     XtSetArg(args[j], XtNy, y); j++;
5263     XtSetValues(promotionShell, args, j);
5264
5265     XtPopup(promotionShell, XtGrabNone);
5266
5267     promotionUp = True;
5268 }
5269
5270 void PromotionPopDown()
5271 {
5272     if (!promotionUp) return;
5273     XtPopdown(promotionShell);
5274     XtDestroyWidget(promotionShell);
5275     promotionUp = False;
5276 }
5277
5278 void PromotionCallback(w, client_data, call_data)
5279      Widget w;
5280      XtPointer client_data, call_data;
5281 {
5282     String name;
5283     Arg args[16];
5284     int promoChar;
5285
5286     XtSetArg(args[0], XtNlabel, &name);
5287     XtGetValues(w, args, 1);
5288
5289     PromotionPopDown();
5290
5291     if (fromX == -1) return;
5292
5293     if (strcmp(name, _("cancel")) == 0) {
5294         fromX = fromY = -1;
5295         ClearHighlights();
5296         return;
5297     } else if (strcmp(name, _("Knight")) == 0) {
5298         promoChar = 'n';
5299     } else if (strcmp(name, _("Promote")) == 0) {
5300         promoChar = '+';
5301     } else if (strcmp(name, _("Defer")) == 0) {
5302         promoChar = '=';
5303     } else {
5304         promoChar = ToLower(name[0]);
5305     }
5306
5307     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5308
5309     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5310     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5311     fromX = fromY = -1;
5312 }
5313
5314
5315 void ErrorCallback(w, client_data, call_data)
5316      Widget w;
5317      XtPointer client_data, call_data;
5318 {
5319     errorUp = False;
5320     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5321     XtDestroyWidget(w);
5322     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5323 }
5324
5325
5326 void ErrorPopDown()
5327 {
5328     if (!errorUp) return;
5329     errorUp = False;
5330     XtPopdown(errorShell);
5331     XtDestroyWidget(errorShell);
5332     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5333 }
5334
5335 void ErrorPopUp(title, label, modal)
5336      char *title, *label;
5337      int modal;
5338 {
5339     Arg args[16];
5340     Widget dialog, layout;
5341     Position x, y;
5342     int xx, yy;
5343     Window junk;
5344     Dimension bw_width, pw_width;
5345     Dimension pw_height;
5346     int i;
5347
5348     i = 0;
5349     XtSetArg(args[i], XtNresizable, True);  i++;
5350     XtSetArg(args[i], XtNtitle, title); i++;
5351     errorShell =
5352       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5353                          shellWidget, args, i);
5354     layout =
5355       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5356                             layoutArgs, XtNumber(layoutArgs));
5357
5358     i = 0;
5359     XtSetArg(args[i], XtNlabel, label); i++;
5360     XtSetArg(args[i], XtNborderWidth, 0); i++;
5361     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5362                                    layout, args, i);
5363
5364     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5365
5366     XtRealizeWidget(errorShell);
5367     CatchDeleteWindow(errorShell, "ErrorPopDown");
5368
5369     i = 0;
5370     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5371     XtGetValues(boardWidget, args, i);
5372     i = 0;
5373     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5374     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5375     XtGetValues(errorShell, args, i);
5376
5377 #ifdef NOTDEF
5378     /* This code seems to tickle an X bug if it is executed too soon
5379        after xboard starts up.  The coordinates get transformed as if
5380        the main window was positioned at (0, 0).
5381        */
5382     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5383                       0 - pw_height + squareSize / 3, &x, &y);
5384 #else
5385     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5386                           RootWindowOfScreen(XtScreen(boardWidget)),
5387                           (bw_width - pw_width) / 2,
5388                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5389     x = xx;
5390     y = yy;
5391 #endif
5392     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5393
5394     i = 0;
5395     XtSetArg(args[i], XtNx, x);  i++;
5396     XtSetArg(args[i], XtNy, y);  i++;
5397     XtSetValues(errorShell, args, i);
5398
5399     errorUp = True;
5400     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5401 }
5402
5403 /* Disable all user input other than deleting the window */
5404 static int frozen = 0;
5405 void FreezeUI()
5406 {
5407   if (frozen) return;
5408   /* Grab by a widget that doesn't accept input */
5409   XtAddGrab(messageWidget, TRUE, FALSE);
5410   frozen = 1;
5411 }
5412
5413 /* Undo a FreezeUI */
5414 void ThawUI()
5415 {
5416   if (!frozen) return;
5417   XtRemoveGrab(messageWidget);
5418   frozen = 0;
5419 }
5420
5421 char *ModeToWidgetName(mode)
5422      GameMode mode;
5423 {
5424     switch (mode) {
5425       case BeginningOfGame:
5426         if (appData.icsActive)
5427           return "menuMode.ICS Client";
5428         else if (appData.noChessProgram ||
5429                  *appData.cmailGameName != NULLCHAR)
5430           return "menuMode.Edit Game";
5431         else
5432           return "menuMode.Machine Black";
5433       case MachinePlaysBlack:
5434         return "menuMode.Machine Black";
5435       case MachinePlaysWhite:
5436         return "menuMode.Machine White";
5437       case AnalyzeMode:
5438         return "menuMode.Analysis Mode";
5439       case AnalyzeFile:
5440         return "menuMode.Analyze File";
5441       case TwoMachinesPlay:
5442         return "menuMode.Two Machines";
5443       case EditGame:
5444         return "menuMode.Edit Game";
5445       case PlayFromGameFile:
5446         return "menuFile.Load Game";
5447       case EditPosition:
5448         return "menuMode.Edit Position";
5449       case Training:
5450         return "menuMode.Training";
5451       case IcsPlayingWhite:
5452       case IcsPlayingBlack:
5453       case IcsObserving:
5454       case IcsIdle:
5455       case IcsExamining:
5456         return "menuMode.ICS Client";
5457       default:
5458       case EndOfGame:
5459         return NULL;
5460     }
5461 }
5462
5463 void ModeHighlight()
5464 {
5465     Arg args[16];
5466     static int oldPausing = FALSE;
5467     static GameMode oldmode = (GameMode) -1;
5468     char *wname;
5469
5470     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5471
5472     if (pausing != oldPausing) {
5473         oldPausing = pausing;
5474         if (pausing) {
5475             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5476         } else {
5477             XtSetArg(args[0], XtNleftBitmap, None);
5478         }
5479         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5480                     args, 1);
5481
5482         if (appData.showButtonBar) {
5483           /* Always toggle, don't set.  Previous code messes up when
5484              invoked while the button is pressed, as releasing it
5485              toggles the state again. */
5486           {
5487             Pixel oldbg, oldfg;
5488             XtSetArg(args[0], XtNbackground, &oldbg);
5489             XtSetArg(args[1], XtNforeground, &oldfg);
5490             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5491                         args, 2);
5492             XtSetArg(args[0], XtNbackground, oldfg);
5493             XtSetArg(args[1], XtNforeground, oldbg);
5494           }
5495           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5496         }
5497     }
5498
5499     wname = ModeToWidgetName(oldmode);
5500     if (wname != NULL) {
5501         XtSetArg(args[0], XtNleftBitmap, None);
5502         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5503     }
5504     wname = ModeToWidgetName(gameMode);
5505     if (wname != NULL) {
5506         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5507         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5508     }
5509     oldmode = gameMode;
5510
5511     /* Maybe all the enables should be handled here, not just this one */
5512     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5513                    gameMode == Training || gameMode == PlayFromGameFile);
5514 }
5515
5516
5517 /*
5518  * Button/menu procedures
5519  */
5520 void ResetProc(w, event, prms, nprms)
5521      Widget w;
5522      XEvent *event;
5523      String *prms;
5524      Cardinal *nprms;
5525 {
5526     ResetGameEvent();
5527 }
5528
5529 int LoadGamePopUp(f, gameNumber, title)
5530      FILE *f;
5531      int gameNumber;
5532      char *title;
5533 {
5534     cmailMsgLoaded = FALSE;
5535     if (gameNumber == 0) {
5536         int error = GameListBuild(f);
5537         if (error) {
5538             DisplayError(_("Cannot build game list"), error);
5539         } else if (!ListEmpty(&gameList) &&
5540                    ((ListGame *) gameList.tailPred)->number > 1) {
5541             GameListPopUp(f, title);
5542             return TRUE;
5543         }
5544         GameListDestroy();
5545         gameNumber = 1;
5546     }
5547     return LoadGame(f, gameNumber, title, FALSE);
5548 }
5549
5550 void LoadGameProc(w, event, prms, nprms)
5551      Widget w;
5552      XEvent *event;
5553      String *prms;
5554      Cardinal *nprms;
5555 {
5556     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5557         Reset(FALSE, TRUE);
5558     }
5559     FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5560 }
5561
5562 void LoadNextGameProc(w, event, prms, nprms)
5563      Widget w;
5564      XEvent *event;
5565      String *prms;
5566      Cardinal *nprms;
5567 {
5568     ReloadGame(1);
5569 }
5570
5571 void LoadPrevGameProc(w, event, prms, nprms)
5572      Widget w;
5573      XEvent *event;
5574      String *prms;
5575      Cardinal *nprms;
5576 {
5577     ReloadGame(-1);
5578 }
5579
5580 void ReloadGameProc(w, event, prms, nprms)
5581      Widget w;
5582      XEvent *event;
5583      String *prms;
5584      Cardinal *nprms;
5585 {
5586     ReloadGame(0);
5587 }
5588
5589 void LoadNextPositionProc(w, event, prms, nprms)
5590      Widget w;
5591      XEvent *event;
5592      String *prms;
5593      Cardinal *nprms;
5594 {
5595     ReloadPosition(1);
5596 }
5597
5598 void LoadPrevPositionProc(w, event, prms, nprms)
5599      Widget w;
5600      XEvent *event;
5601      String *prms;
5602      Cardinal *nprms;
5603 {
5604     ReloadPosition(-1);
5605 }
5606
5607 void ReloadPositionProc(w, event, prms, nprms)
5608      Widget w;
5609      XEvent *event;
5610      String *prms;
5611      Cardinal *nprms;
5612 {
5613     ReloadPosition(0);
5614 }
5615
5616 void LoadPositionProc(w, event, prms, nprms)
5617      Widget w;
5618      XEvent *event;
5619      String *prms;
5620      Cardinal *nprms;
5621 {
5622     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5623         Reset(FALSE, TRUE);
5624     }
5625     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5626 }
5627
5628 void SaveGameProc(w, event, prms, nprms)
5629      Widget w;
5630      XEvent *event;
5631      String *prms;
5632      Cardinal *nprms;
5633 {
5634     FileNamePopUp(_("Save game file name?"),
5635                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5636                   SaveGame, "a");
5637 }
5638
5639 void SavePositionProc(w, event, prms, nprms)
5640      Widget w;
5641      XEvent *event;
5642      String *prms;
5643      Cardinal *nprms;
5644 {
5645     FileNamePopUp(_("Save position file name?"),
5646                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5647                   SavePosition, "a");
5648 }
5649
5650 void ReloadCmailMsgProc(w, event, prms, nprms)
5651      Widget w;
5652      XEvent *event;
5653      String *prms;
5654      Cardinal *nprms;
5655 {
5656     ReloadCmailMsgEvent(FALSE);
5657 }
5658
5659 void MailMoveProc(w, event, prms, nprms)
5660      Widget w;
5661      XEvent *event;
5662      String *prms;
5663      Cardinal *nprms;
5664 {
5665     MailMoveEvent();
5666 }
5667
5668 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5669 char *selected_fen_position=NULL;
5670
5671 Boolean
5672 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5673                  Atom *type_return, XtPointer *value_return,
5674                  unsigned long *length_return, int *format_return)
5675 {
5676   char *selection_tmp;
5677
5678   if (!selected_fen_position) return False; /* should never happen */
5679   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5680     /* note: since no XtSelectionDoneProc was registered, Xt will
5681      * automatically call XtFree on the value returned.  So have to
5682      * make a copy of it allocated with XtMalloc */
5683     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5684     strcpy(selection_tmp, selected_fen_position);
5685
5686     *value_return=selection_tmp;
5687     *length_return=strlen(selection_tmp);
5688     *type_return=*target;
5689     *format_return = 8; /* bits per byte */
5690     return True;
5691   } else if (*target == XA_TARGETS(xDisplay)) {
5692     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5693     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5694     targets_tmp[1] = XA_STRING;
5695     *value_return = targets_tmp;
5696     *type_return = XA_ATOM;
5697     *length_return = 2;
5698     *format_return = 8 * sizeof(Atom);
5699     if (*format_return > 32) {
5700       *length_return *= *format_return / 32;
5701       *format_return = 32;
5702     }
5703     return True;
5704   } else {
5705     return False;
5706   }
5707 }
5708
5709 /* note: when called from menu all parameters are NULL, so no clue what the
5710  * Widget which was clicked on was, or what the click event was
5711  */
5712 void CopyPositionProc(w, event, prms, nprms)
5713   Widget w;
5714   XEvent *event;
5715   String *prms;
5716   Cardinal *nprms;
5717   {
5718     /*
5719      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5720      * have a notion of a position that is selected but not copied.
5721      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5722      */
5723     if(gameMode == EditPosition) EditPositionDone(TRUE);
5724     if (selected_fen_position) free(selected_fen_position);
5725     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5726     if (!selected_fen_position) return;
5727     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5728                    CurrentTime,
5729                    SendPositionSelection,
5730                    NULL/* lose_ownership_proc */ ,
5731                    NULL/* transfer_done_proc */);
5732     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5733                    CurrentTime,
5734                    SendPositionSelection,
5735                    NULL/* lose_ownership_proc */ ,
5736                    NULL/* transfer_done_proc */);
5737   }
5738
5739 /* function called when the data to Paste is ready */
5740 static void
5741 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5742            Atom *type, XtPointer value, unsigned long *len, int *format)
5743 {
5744   char *fenstr=value;
5745   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5746   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5747   EditPositionPasteFEN(fenstr);
5748   XtFree(value);
5749 }
5750
5751 /* called when Paste Position button is pressed,
5752  * all parameters will be NULL */
5753 void PastePositionProc(w, event, prms, nprms)
5754   Widget w;
5755   XEvent *event;
5756   String *prms;
5757   Cardinal *nprms;
5758 {
5759     XtGetSelectionValue(menuBarWidget, 
5760       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5761       /* (XtSelectionCallbackProc) */ PastePositionCB,
5762       NULL, /* client_data passed to PastePositionCB */
5763
5764       /* better to use the time field from the event that triggered the
5765        * call to this function, but that isn't trivial to get
5766        */
5767       CurrentTime
5768     );
5769     return;
5770 }
5771
5772 static Boolean
5773 SendGameSelection(Widget w, Atom *selection, Atom *target,
5774                   Atom *type_return, XtPointer *value_return,
5775                   unsigned long *length_return, int *format_return)
5776 {
5777   char *selection_tmp;
5778
5779   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5780     FILE* f = fopen(gameCopyFilename, "r");
5781     long len;
5782     size_t count;
5783     if (f == NULL) return False;
5784     fseek(f, 0, 2);
5785     len = ftell(f);
5786     rewind(f);
5787     selection_tmp = XtMalloc(len + 1);
5788     count = fread(selection_tmp, 1, len, f);
5789     if (len != count) {
5790       XtFree(selection_tmp);
5791       return False;
5792     }
5793     selection_tmp[len] = NULLCHAR;
5794     *value_return = selection_tmp;
5795     *length_return = len;
5796     *type_return = *target;
5797     *format_return = 8; /* bits per byte */
5798     return True;
5799   } else if (*target == XA_TARGETS(xDisplay)) {
5800     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5801     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5802     targets_tmp[1] = XA_STRING;
5803     *value_return = targets_tmp;
5804     *type_return = XA_ATOM;
5805     *length_return = 2;
5806     *format_return = 8 * sizeof(Atom);
5807     if (*format_return > 32) {
5808       *length_return *= *format_return / 32;
5809       *format_return = 32;
5810     }
5811     return True;
5812   } else {
5813     return False;
5814   }
5815 }
5816
5817 /* note: when called from menu all parameters are NULL, so no clue what the
5818  * Widget which was clicked on was, or what the click event was
5819  */
5820 void CopyGameProc(w, event, prms, nprms)
5821   Widget w;
5822   XEvent *event;
5823   String *prms;
5824   Cardinal *nprms;
5825 {
5826   int ret;
5827
5828   ret = SaveGameToFile(gameCopyFilename, FALSE);
5829   if (!ret) return;
5830
5831   /*
5832    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5833    * have a notion of a game that is selected but not copied.
5834    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5835    */
5836   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5837                  CurrentTime,
5838                  SendGameSelection,
5839                  NULL/* lose_ownership_proc */ ,
5840                  NULL/* transfer_done_proc */);
5841   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5842                  CurrentTime,
5843                  SendGameSelection,
5844                  NULL/* lose_ownership_proc */ ,
5845                  NULL/* transfer_done_proc */);
5846 }
5847
5848 /* function called when the data to Paste is ready */
5849 static void
5850 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5851             Atom *type, XtPointer value, unsigned long *len, int *format)
5852 {
5853   FILE* f;
5854   if (value == NULL || *len == 0) {
5855     return; /* nothing had been selected to copy */
5856   }
5857   f = fopen(gamePasteFilename, "w");
5858   if (f == NULL) {
5859     DisplayError(_("Can't open temp file"), errno);
5860     return;
5861   }
5862   fwrite(value, 1, *len, f);
5863   fclose(f);
5864   XtFree(value);
5865   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5866 }
5867
5868 /* called when Paste Game button is pressed,
5869  * all parameters will be NULL */
5870 void PasteGameProc(w, event, prms, nprms)
5871   Widget w;
5872   XEvent *event;
5873   String *prms;
5874   Cardinal *nprms;
5875 {
5876     XtGetSelectionValue(menuBarWidget,
5877       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5878       /* (XtSelectionCallbackProc) */ PasteGameCB,
5879       NULL, /* client_data passed to PasteGameCB */
5880
5881       /* better to use the time field from the event that triggered the
5882        * call to this function, but that isn't trivial to get
5883        */
5884       CurrentTime
5885     );
5886     return;
5887 }
5888
5889
5890 void AutoSaveGame()
5891 {
5892     SaveGameProc(NULL, NULL, NULL, NULL);
5893 }
5894
5895
5896 void QuitProc(w, event, prms, nprms)
5897      Widget w;
5898      XEvent *event;
5899      String *prms;
5900      Cardinal *nprms;
5901 {
5902     ExitEvent(0);
5903 }
5904
5905 void PauseProc(w, event, prms, nprms)
5906      Widget w;
5907      XEvent *event;
5908      String *prms;
5909      Cardinal *nprms;
5910 {
5911     PauseEvent();
5912 }
5913
5914
5915 void MachineBlackProc(w, event, prms, nprms)
5916      Widget w;
5917      XEvent *event;
5918      String *prms;
5919      Cardinal *nprms;
5920 {
5921     MachineBlackEvent();
5922 }
5923
5924 void MachineWhiteProc(w, event, prms, nprms)
5925      Widget w;
5926      XEvent *event;
5927      String *prms;
5928      Cardinal *nprms;
5929 {
5930     MachineWhiteEvent();
5931 }
5932
5933 void AnalyzeModeProc(w, event, prms, nprms)
5934      Widget w;
5935      XEvent *event;
5936      String *prms;
5937      Cardinal *nprms;
5938 {
5939     char buf[MSG_SIZ];
5940
5941     if (!first.analysisSupport) {
5942       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5943       DisplayError(buf, 0);
5944       return;
5945     }
5946     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5947     if (appData.icsActive) {
5948         if (gameMode != IcsObserving) {
5949             sprintf(buf,_("You are not observing a game"));
5950             DisplayError(buf, 0);
5951             /* secure check */
5952             if (appData.icsEngineAnalyze) {
5953                 if (appData.debugMode)
5954                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5955                 ExitAnalyzeMode();
5956                 ModeHighlight();
5957             }
5958             return;
5959         }
5960         /* if enable, use want disable icsEngineAnalyze */
5961         if (appData.icsEngineAnalyze) {
5962                 ExitAnalyzeMode();
5963                 ModeHighlight();
5964                 return;
5965         }
5966         appData.icsEngineAnalyze = TRUE;
5967         if (appData.debugMode)
5968             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5969     }
5970     if (!appData.showThinking)
5971       ShowThinkingProc(w,event,prms,nprms);
5972
5973     AnalyzeModeEvent();
5974 }
5975
5976 void AnalyzeFileProc(w, event, prms, nprms)
5977      Widget w;
5978      XEvent *event;
5979      String *prms;
5980      Cardinal *nprms;
5981 {
5982     if (!first.analysisSupport) {
5983       char buf[MSG_SIZ];
5984       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5985       DisplayError(buf, 0);
5986       return;
5987     }
5988     Reset(FALSE, TRUE);
5989
5990     if (!appData.showThinking)
5991       ShowThinkingProc(w,event,prms,nprms);
5992
5993     AnalyzeFileEvent();
5994     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
5995     AnalysisPeriodicEvent(1);
5996 }
5997
5998 void TwoMachinesProc(w, event, prms, nprms)
5999      Widget w;
6000      XEvent *event;
6001      String *prms;
6002      Cardinal *nprms;
6003 {
6004     TwoMachinesEvent();
6005 }
6006
6007 void IcsClientProc(w, event, prms, nprms)
6008      Widget w;
6009      XEvent *event;
6010      String *prms;
6011      Cardinal *nprms;
6012 {
6013     IcsClientEvent();
6014 }
6015
6016 void EditGameProc(w, event, prms, nprms)
6017      Widget w;
6018      XEvent *event;
6019      String *prms;
6020      Cardinal *nprms;
6021 {
6022     EditGameEvent();
6023 }
6024
6025 void EditPositionProc(w, event, prms, nprms)
6026      Widget w;
6027      XEvent *event;
6028      String *prms;
6029      Cardinal *nprms;
6030 {
6031     EditPositionEvent();
6032 }
6033
6034 void TrainingProc(w, event, prms, nprms)
6035      Widget w;
6036      XEvent *event;
6037      String *prms;
6038      Cardinal *nprms;
6039 {
6040     TrainingEvent();
6041 }
6042
6043 void EditCommentProc(w, event, prms, nprms)
6044      Widget w;
6045      XEvent *event;
6046      String *prms;
6047      Cardinal *nprms;
6048 {
6049     if (editUp) {
6050         EditCommentPopDown();
6051     } else {
6052         EditCommentEvent();
6053     }
6054 }
6055
6056 void IcsInputBoxProc(w, event, prms, nprms)
6057      Widget w;
6058      XEvent *event;
6059      String *prms;
6060      Cardinal *nprms;
6061 {
6062     if (ICSInputBoxUp) {
6063         ICSInputBoxPopDown();
6064     } else {
6065         ICSInputBoxPopUp();
6066     }
6067 }
6068
6069 void AcceptProc(w, event, prms, nprms)
6070      Widget w;
6071      XEvent *event;
6072      String *prms;
6073      Cardinal *nprms;
6074 {
6075     AcceptEvent();
6076 }
6077
6078 void DeclineProc(w, event, prms, nprms)
6079      Widget w;
6080      XEvent *event;
6081      String *prms;
6082      Cardinal *nprms;
6083 {
6084     DeclineEvent();
6085 }
6086
6087 void RematchProc(w, event, prms, nprms)
6088      Widget w;
6089      XEvent *event;
6090      String *prms;
6091      Cardinal *nprms;
6092 {
6093     RematchEvent();
6094 }
6095
6096 void CallFlagProc(w, event, prms, nprms)
6097      Widget w;
6098      XEvent *event;
6099      String *prms;
6100      Cardinal *nprms;
6101 {
6102     CallFlagEvent();
6103 }
6104
6105 void DrawProc(w, event, prms, nprms)
6106      Widget w;
6107      XEvent *event;
6108      String *prms;
6109      Cardinal *nprms;
6110 {
6111     DrawEvent();
6112 }
6113
6114 void AbortProc(w, event, prms, nprms)
6115      Widget w;
6116      XEvent *event;
6117      String *prms;
6118      Cardinal *nprms;
6119 {
6120     AbortEvent();
6121 }
6122
6123 void AdjournProc(w, event, prms, nprms)
6124      Widget w;
6125      XEvent *event;
6126      String *prms;
6127      Cardinal *nprms;
6128 {
6129     AdjournEvent();
6130 }
6131
6132 void ResignProc(w, event, prms, nprms)
6133      Widget w;
6134      XEvent *event;
6135      String *prms;
6136      Cardinal *nprms;
6137 {
6138     ResignEvent();
6139 }
6140
6141 void AdjuWhiteProc(w, event, prms, nprms)
6142      Widget w;
6143      XEvent *event;
6144      String *prms;
6145      Cardinal *nprms;
6146 {
6147     UserAdjudicationEvent(+1);
6148 }
6149
6150 void AdjuBlackProc(w, event, prms, nprms)
6151      Widget w;
6152      XEvent *event;
6153      String *prms;
6154      Cardinal *nprms;
6155 {
6156     UserAdjudicationEvent(-1);
6157 }
6158
6159 void AdjuDrawProc(w, event, prms, nprms)
6160      Widget w;
6161      XEvent *event;
6162      String *prms;
6163      Cardinal *nprms;
6164 {
6165     UserAdjudicationEvent(0);
6166 }
6167
6168 void EnterKeyProc(w, event, prms, nprms)
6169      Widget w;
6170      XEvent *event;
6171      String *prms;
6172      Cardinal *nprms;
6173 {
6174     if (ICSInputBoxUp == True)
6175       ICSInputSendText();
6176 }
6177
6178 void UpKeyProc(w, event, prms, nprms)
6179      Widget w;
6180      XEvent *event;
6181      String *prms;
6182      Cardinal *nprms;
6183 {   // [HGM] input: let up-arrow recall previous line from history
6184     Widget edit;
6185     int j;
6186     Arg args[16];
6187     String val;
6188     XawTextBlock t;
6189
6190     if (!ICSInputBoxUp) return;
6191     edit = XtNameToWidget(ICSInputShell, "*form.text");
6192     j = 0;
6193     XtSetArg(args[j], XtNstring, &val); j++;
6194     XtGetValues(edit, args, j);
6195     val = PrevInHistory(val);
6196     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6197     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6198     if(val) {
6199         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6200         XawTextReplace(edit, 0, 0, &t);
6201         XawTextSetInsertionPoint(edit, 9999);
6202     }
6203 }
6204
6205 void DownKeyProc(w, event, prms, nprms)
6206      Widget w;
6207      XEvent *event;
6208      String *prms;
6209      Cardinal *nprms;
6210 {   // [HGM] input: let down-arrow recall next line from history
6211     Widget edit;
6212     String val;
6213     XawTextBlock t;
6214
6215     if (!ICSInputBoxUp) return;
6216     edit = XtNameToWidget(ICSInputShell, "*form.text");
6217     val = NextInHistory();
6218     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6219     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6220     if(val) {
6221         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6222         XawTextReplace(edit, 0, 0, &t);
6223         XawTextSetInsertionPoint(edit, 9999);
6224     }
6225 }
6226
6227 void StopObservingProc(w, event, prms, nprms)
6228      Widget w;
6229      XEvent *event;
6230      String *prms;
6231      Cardinal *nprms;
6232 {
6233     StopObservingEvent();
6234 }
6235
6236 void StopExaminingProc(w, event, prms, nprms)
6237      Widget w;
6238      XEvent *event;
6239      String *prms;
6240      Cardinal *nprms;
6241 {
6242     StopExaminingEvent();
6243 }
6244
6245 void UploadProc(w, event, prms, nprms)
6246      Widget w;
6247      XEvent *event;
6248      String *prms;
6249      Cardinal *nprms;
6250 {
6251     UploadGameEvent();
6252 }
6253
6254
6255 void ForwardProc(w, event, prms, nprms)
6256      Widget w;
6257      XEvent *event;
6258      String *prms;
6259      Cardinal *nprms;
6260 {
6261     ForwardEvent();
6262 }
6263
6264
6265 void BackwardProc(w, event, prms, nprms)
6266      Widget w;
6267      XEvent *event;
6268      String *prms;
6269      Cardinal *nprms;
6270 {
6271     BackwardEvent();
6272 }
6273
6274 void ToStartProc(w, event, prms, nprms)
6275      Widget w;
6276      XEvent *event;
6277      String *prms;
6278      Cardinal *nprms;
6279 {
6280     ToStartEvent();
6281 }
6282
6283 void ToEndProc(w, event, prms, nprms)
6284      Widget w;
6285      XEvent *event;
6286      String *prms;
6287      Cardinal *nprms;
6288 {
6289     ToEndEvent();
6290 }
6291
6292 void RevertProc(w, event, prms, nprms)
6293      Widget w;
6294      XEvent *event;
6295      String *prms;
6296      Cardinal *nprms;
6297 {
6298     RevertEvent(False);
6299 }
6300
6301 void AnnotateProc(w, event, prms, nprms)
6302      Widget w;
6303      XEvent *event;
6304      String *prms;
6305      Cardinal *nprms;
6306 {
6307     RevertEvent(True);
6308 }
6309
6310 void TruncateGameProc(w, event, prms, nprms)
6311      Widget w;
6312      XEvent *event;
6313      String *prms;
6314      Cardinal *nprms;
6315 {
6316     TruncateGameEvent();
6317 }
6318 void RetractMoveProc(w, event, prms, nprms)
6319      Widget w;
6320      XEvent *event;
6321      String *prms;
6322      Cardinal *nprms;
6323 {
6324     RetractMoveEvent();
6325 }
6326
6327 void MoveNowProc(w, event, prms, nprms)
6328      Widget w;
6329      XEvent *event;
6330      String *prms;
6331      Cardinal *nprms;
6332 {
6333     MoveNowEvent();
6334 }
6335
6336
6337 void AlwaysQueenProc(w, event, prms, nprms)
6338      Widget w;
6339      XEvent *event;
6340      String *prms;
6341      Cardinal *nprms;
6342 {
6343     Arg args[16];
6344
6345     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6346
6347     if (appData.alwaysPromoteToQueen) {
6348         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6349     } else {
6350         XtSetArg(args[0], XtNleftBitmap, None);
6351     }
6352     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6353                 args, 1);
6354 }
6355
6356 void AnimateDraggingProc(w, event, prms, nprms)
6357      Widget w;
6358      XEvent *event;
6359      String *prms;
6360      Cardinal *nprms;
6361 {
6362     Arg args[16];
6363
6364     appData.animateDragging = !appData.animateDragging;
6365
6366     if (appData.animateDragging) {
6367         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6368         CreateAnimVars();
6369     } else {
6370         XtSetArg(args[0], XtNleftBitmap, None);
6371     }
6372     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6373                 args, 1);
6374 }
6375
6376 void AnimateMovingProc(w, event, prms, nprms)
6377      Widget w;
6378      XEvent *event;
6379      String *prms;
6380      Cardinal *nprms;
6381 {
6382     Arg args[16];
6383
6384     appData.animate = !appData.animate;
6385
6386     if (appData.animate) {
6387         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6388         CreateAnimVars();
6389     } else {
6390         XtSetArg(args[0], XtNleftBitmap, None);
6391     }
6392     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6393                 args, 1);
6394 }
6395
6396 void AutocommProc(w, event, prms, nprms)
6397      Widget w;
6398      XEvent *event;
6399      String *prms;
6400      Cardinal *nprms;
6401 {
6402     Arg args[16];
6403
6404     appData.autoComment = !appData.autoComment;
6405
6406     if (appData.autoComment) {
6407         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6408     } else {
6409         XtSetArg(args[0], XtNleftBitmap, None);
6410     }
6411     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6412                 args, 1);
6413 }
6414
6415
6416 void AutoflagProc(w, event, prms, nprms)
6417      Widget w;
6418      XEvent *event;
6419      String *prms;
6420      Cardinal *nprms;
6421 {
6422     Arg args[16];
6423
6424     appData.autoCallFlag = !appData.autoCallFlag;
6425
6426     if (appData.autoCallFlag) {
6427         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6428     } else {
6429         XtSetArg(args[0], XtNleftBitmap, None);
6430     }
6431     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6432                 args, 1);
6433 }
6434
6435 void AutoflipProc(w, event, prms, nprms)
6436      Widget w;
6437      XEvent *event;
6438      String *prms;
6439      Cardinal *nprms;
6440 {
6441     Arg args[16];
6442
6443     appData.autoFlipView = !appData.autoFlipView;
6444
6445     if (appData.autoFlipView) {
6446         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6447     } else {
6448         XtSetArg(args[0], XtNleftBitmap, None);
6449     }
6450     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6451                 args, 1);
6452 }
6453
6454 void AutobsProc(w, event, prms, nprms)
6455      Widget w;
6456      XEvent *event;
6457      String *prms;
6458      Cardinal *nprms;
6459 {
6460     Arg args[16];
6461
6462     appData.autoObserve = !appData.autoObserve;
6463
6464     if (appData.autoObserve) {
6465         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6466     } else {
6467         XtSetArg(args[0], XtNleftBitmap, None);
6468     }
6469     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6470                 args, 1);
6471 }
6472
6473 void AutoraiseProc(w, event, prms, nprms)
6474      Widget w;
6475      XEvent *event;
6476      String *prms;
6477      Cardinal *nprms;
6478 {
6479     Arg args[16];
6480
6481     appData.autoRaiseBoard = !appData.autoRaiseBoard;
6482
6483     if (appData.autoRaiseBoard) {
6484         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6485     } else {
6486         XtSetArg(args[0], XtNleftBitmap, None);
6487     }
6488     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
6489                 args, 1);
6490 }
6491
6492 void AutosaveProc(w, event, prms, nprms)
6493      Widget w;
6494      XEvent *event;
6495      String *prms;
6496      Cardinal *nprms;
6497 {
6498     Arg args[16];
6499
6500     appData.autoSaveGames = !appData.autoSaveGames;
6501
6502     if (appData.autoSaveGames) {
6503         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6504     } else {
6505         XtSetArg(args[0], XtNleftBitmap, None);
6506     }
6507     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
6508                 args, 1);
6509 }
6510
6511 void BlindfoldProc(w, event, prms, nprms)
6512      Widget w;
6513      XEvent *event;
6514      String *prms;
6515      Cardinal *nprms;
6516 {
6517     Arg args[16];
6518
6519     appData.blindfold = !appData.blindfold;
6520
6521     if (appData.blindfold) {
6522         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6523     } else {
6524         XtSetArg(args[0], XtNleftBitmap, None);
6525     }
6526     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6527                 args, 1);
6528
6529     DrawPosition(True, NULL);
6530 }
6531
6532 void TestLegalityProc(w, event, prms, nprms)
6533      Widget w;
6534      XEvent *event;
6535      String *prms;
6536      Cardinal *nprms;
6537 {
6538     Arg args[16];
6539
6540     appData.testLegality = !appData.testLegality;
6541
6542     if (appData.testLegality) {
6543         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6544     } else {
6545         XtSetArg(args[0], XtNleftBitmap, None);
6546     }
6547     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6548                 args, 1);
6549 }
6550
6551
6552 void FlashMovesProc(w, event, prms, nprms)
6553      Widget w;
6554      XEvent *event;
6555      String *prms;
6556      Cardinal *nprms;
6557 {
6558     Arg args[16];
6559
6560     if (appData.flashCount == 0) {
6561         appData.flashCount = 3;
6562     } else {
6563         appData.flashCount = -appData.flashCount;
6564     }
6565
6566     if (appData.flashCount > 0) {
6567         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6568     } else {
6569         XtSetArg(args[0], XtNleftBitmap, None);
6570     }
6571     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6572                 args, 1);
6573 }
6574
6575 void FlipViewProc(w, event, prms, nprms)
6576      Widget w;
6577      XEvent *event;
6578      String *prms;
6579      Cardinal *nprms;
6580 {
6581     flipView = !flipView;
6582     DrawPosition(True, NULL);
6583 }
6584
6585 void GetMoveListProc(w, event, prms, nprms)
6586      Widget w;
6587      XEvent *event;
6588      String *prms;
6589      Cardinal *nprms;
6590 {
6591     Arg args[16];
6592
6593     appData.getMoveList = !appData.getMoveList;
6594
6595     if (appData.getMoveList) {
6596         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6597         GetMoveListEvent();
6598     } else {
6599         XtSetArg(args[0], XtNleftBitmap, None);
6600     }
6601     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
6602                 args, 1);
6603 }
6604
6605 #if HIGHDRAG
6606 void HighlightDraggingProc(w, event, prms, nprms)
6607      Widget w;
6608      XEvent *event;
6609      String *prms;
6610      Cardinal *nprms;
6611 {
6612     Arg args[16];
6613
6614     appData.highlightDragging = !appData.highlightDragging;
6615
6616     if (appData.highlightDragging) {
6617         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6618     } else {
6619         XtSetArg(args[0], XtNleftBitmap, None);
6620     }
6621     XtSetValues(XtNameToWidget(menuBarWidget,
6622                                "menuOptions.Highlight Dragging"), args, 1);
6623 }
6624 #endif
6625
6626 void HighlightLastMoveProc(w, event, prms, nprms)
6627      Widget w;
6628      XEvent *event;
6629      String *prms;
6630      Cardinal *nprms;
6631 {
6632     Arg args[16];
6633
6634     appData.highlightLastMove = !appData.highlightLastMove;
6635
6636     if (appData.highlightLastMove) {
6637         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6638     } else {
6639         XtSetArg(args[0], XtNleftBitmap, None);
6640     }
6641     XtSetValues(XtNameToWidget(menuBarWidget,
6642                                "menuOptions.Highlight Last Move"), args, 1);
6643 }
6644
6645 void IcsAlarmProc(w, event, prms, nprms)
6646      Widget w;
6647      XEvent *event;
6648      String *prms;
6649      Cardinal *nprms;
6650 {
6651     Arg args[16];
6652
6653     appData.icsAlarm = !appData.icsAlarm;
6654
6655     if (appData.icsAlarm) {
6656         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6657     } else {
6658         XtSetArg(args[0], XtNleftBitmap, None);
6659     }
6660     XtSetValues(XtNameToWidget(menuBarWidget,
6661                                "menuOptions.ICS Alarm"), args, 1);
6662 }
6663
6664 void MoveSoundProc(w, event, prms, nprms)
6665      Widget w;
6666      XEvent *event;
6667      String *prms;
6668      Cardinal *nprms;
6669 {
6670     Arg args[16];
6671
6672     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6673
6674     if (appData.ringBellAfterMoves) {
6675         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6676     } else {
6677         XtSetArg(args[0], XtNleftBitmap, None);
6678     }
6679     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6680                 args, 1);
6681 }
6682
6683
6684 void OldSaveStyleProc(w, event, prms, nprms)
6685      Widget w;
6686      XEvent *event;
6687      String *prms;
6688      Cardinal *nprms;
6689 {
6690     Arg args[16];
6691
6692     appData.oldSaveStyle = !appData.oldSaveStyle;
6693
6694     if (appData.oldSaveStyle) {
6695         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6696     } else {
6697         XtSetArg(args[0], XtNleftBitmap, None);
6698     }
6699     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
6700                 args, 1);
6701 }
6702
6703 void PeriodicUpdatesProc(w, event, prms, nprms)
6704      Widget w;
6705      XEvent *event;
6706      String *prms;
6707      Cardinal *nprms;
6708 {
6709     Arg args[16];
6710
6711     PeriodicUpdatesEvent(!appData.periodicUpdates);
6712
6713     if (appData.periodicUpdates) {
6714         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6715     } else {
6716         XtSetArg(args[0], XtNleftBitmap, None);
6717     }
6718     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6719                 args, 1);
6720 }
6721
6722 void PonderNextMoveProc(w, event, prms, nprms)
6723      Widget w;
6724      XEvent *event;
6725      String *prms;
6726      Cardinal *nprms;
6727 {
6728     Arg args[16];
6729
6730     PonderNextMoveEvent(!appData.ponderNextMove);
6731
6732     if (appData.ponderNextMove) {
6733         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6734     } else {
6735         XtSetArg(args[0], XtNleftBitmap, None);
6736     }
6737     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6738                 args, 1);
6739 }
6740
6741 void PopupExitMessageProc(w, event, prms, nprms)
6742      Widget w;
6743      XEvent *event;
6744      String *prms;
6745      Cardinal *nprms;
6746 {
6747     Arg args[16];
6748
6749     appData.popupExitMessage = !appData.popupExitMessage;
6750
6751     if (appData.popupExitMessage) {
6752         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6753     } else {
6754         XtSetArg(args[0], XtNleftBitmap, None);
6755     }
6756     XtSetValues(XtNameToWidget(menuBarWidget,
6757                                "menuOptions.Popup Exit Message"), args, 1);
6758 }
6759
6760 void PopupMoveErrorsProc(w, event, prms, nprms)
6761      Widget w;
6762      XEvent *event;
6763      String *prms;
6764      Cardinal *nprms;
6765 {
6766     Arg args[16];
6767
6768     appData.popupMoveErrors = !appData.popupMoveErrors;
6769
6770     if (appData.popupMoveErrors) {
6771         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6772     } else {
6773         XtSetArg(args[0], XtNleftBitmap, None);
6774     }
6775     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6776                 args, 1);
6777 }
6778
6779 void PremoveProc(w, event, prms, nprms)
6780      Widget w;
6781      XEvent *event;
6782      String *prms;
6783      Cardinal *nprms;
6784 {
6785     Arg args[16];
6786
6787     appData.premove = !appData.premove;
6788
6789     if (appData.premove) {
6790         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6791     } else {
6792         XtSetArg(args[0], XtNleftBitmap, None);
6793     }
6794     XtSetValues(XtNameToWidget(menuBarWidget,
6795                                "menuOptions.Premove"), args, 1);
6796 }
6797
6798 void QuietPlayProc(w, event, prms, nprms)
6799      Widget w;
6800      XEvent *event;
6801      String *prms;
6802      Cardinal *nprms;
6803 {
6804     Arg args[16];
6805
6806     appData.quietPlay = !appData.quietPlay;
6807
6808     if (appData.quietPlay) {
6809         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6810     } else {
6811         XtSetArg(args[0], XtNleftBitmap, None);
6812     }
6813     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
6814                 args, 1);
6815 }
6816
6817 void ShowCoordsProc(w, event, prms, nprms)
6818      Widget w;
6819      XEvent *event;
6820      String *prms;
6821      Cardinal *nprms;
6822 {
6823     Arg args[16];
6824
6825     appData.showCoords = !appData.showCoords;
6826
6827     if (appData.showCoords) {
6828         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6829     } else {
6830         XtSetArg(args[0], XtNleftBitmap, None);
6831     }
6832     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6833                 args, 1);
6834
6835     DrawPosition(True, NULL);
6836 }
6837
6838 void ShowThinkingProc(w, event, prms, nprms)
6839      Widget w;
6840      XEvent *event;
6841      String *prms;
6842      Cardinal *nprms;
6843 {
6844     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6845     ShowThinkingEvent();
6846 }
6847
6848 void HideThinkingProc(w, event, prms, nprms)
6849      Widget w;
6850      XEvent *event;
6851      String *prms;
6852      Cardinal *nprms;
6853 {
6854     Arg args[16];
6855
6856     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6857     ShowThinkingEvent();
6858
6859     if (appData.hideThinkingFromHuman) {
6860         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6861     } else {
6862         XtSetArg(args[0], XtNleftBitmap, None);
6863     }
6864     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6865                 args, 1);
6866 }
6867
6868 void SaveOnExitProc(w, event, prms, nprms)
6869      Widget w;
6870      XEvent *event;
6871      String *prms;
6872      Cardinal *nprms;
6873 {
6874     Arg args[16];
6875
6876     saveSettingsOnExit = !saveSettingsOnExit;
6877
6878     if (saveSettingsOnExit) {
6879         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6880     } else {
6881         XtSetArg(args[0], XtNleftBitmap, None);
6882     }
6883     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6884                 args, 1);
6885 }
6886
6887 void SaveSettingsProc(w, event, prms, nprms)
6888      Widget w;
6889      XEvent *event;
6890      String *prms;
6891      Cardinal *nprms;
6892 {
6893      SaveSettings(settingsFileName);
6894 }
6895
6896 void InfoProc(w, event, prms, nprms)
6897      Widget w;
6898      XEvent *event;
6899      String *prms;
6900      Cardinal *nprms;
6901 {
6902     char buf[MSG_SIZ];
6903     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6904             INFODIR, INFOFILE);
6905     system(buf);
6906 }
6907
6908 void ManProc(w, event, prms, nprms)
6909      Widget w;
6910      XEvent *event;
6911      String *prms;
6912      Cardinal *nprms;
6913 {
6914     char buf[MSG_SIZ];
6915     String name;
6916     if (nprms && *nprms > 0)
6917       name = prms[0];
6918     else
6919       name = "xboard";
6920     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6921     system(buf);
6922 }
6923
6924 void HintProc(w, event, prms, nprms)
6925      Widget w;
6926      XEvent *event;
6927      String *prms;
6928      Cardinal *nprms;
6929 {
6930     HintEvent();
6931 }
6932
6933 void BookProc(w, event, prms, nprms)
6934      Widget w;
6935      XEvent *event;
6936      String *prms;
6937      Cardinal *nprms;
6938 {
6939     BookEvent();
6940 }
6941
6942 void AboutProc(w, event, prms, nprms)
6943      Widget w;
6944      XEvent *event;
6945      String *prms;
6946      Cardinal *nprms;
6947 {
6948     char buf[MSG_SIZ];
6949 #if ZIPPY
6950     char *zippy = " (with Zippy code)";
6951 #else
6952     char *zippy = "";
6953 #endif
6954     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6955             programVersion, zippy,
6956             "Copyright 1991 Digital Equipment Corporation",
6957             "Enhancements Copyright 1992-2009 Free Software Foundation",
6958             "Enhancements Copyright 2005 Alessandro Scotti",
6959             PACKAGE, " is free software and carries NO WARRANTY;",
6960             "see the file COPYING for more information.");
6961     ErrorPopUp(_("About XBoard"), buf, FALSE);
6962 }
6963
6964 void DebugProc(w, event, prms, nprms)
6965      Widget w;
6966      XEvent *event;
6967      String *prms;
6968      Cardinal *nprms;
6969 {
6970     appData.debugMode = !appData.debugMode;
6971 }
6972
6973 void AboutGameProc(w, event, prms, nprms)
6974      Widget w;
6975      XEvent *event;
6976      String *prms;
6977      Cardinal *nprms;
6978 {
6979     AboutGameEvent();
6980 }
6981
6982 void NothingProc(w, event, prms, nprms)
6983      Widget w;
6984      XEvent *event;
6985      String *prms;
6986      Cardinal *nprms;
6987 {
6988     return;
6989 }
6990
6991 void Iconify(w, event, prms, nprms)
6992      Widget w;
6993      XEvent *event;
6994      String *prms;
6995      Cardinal *nprms;
6996 {
6997     Arg args[16];
6998
6999     fromX = fromY = -1;
7000     XtSetArg(args[0], XtNiconic, True);
7001     XtSetValues(shellWidget, args, 1);
7002 }
7003
7004 void DisplayMessage(message, extMessage)
7005      char *message, *extMessage;
7006 {
7007   /* display a message in the message widget */
7008   
7009   char buf[MSG_SIZ];
7010   Arg arg;
7011   
7012   if (extMessage) 
7013     {
7014       if (*message) 
7015         {
7016           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
7017           message = buf;
7018         } 
7019       else 
7020         {
7021           message = extMessage;
7022         };
7023     };
7024   
7025   /* need to test if messageWidget already exists, since this function
7026      can also be called during the startup, if for example a Xresource
7027      is not set up correctly */
7028   if(messageWidget)
7029     {
7030       XtSetArg(arg, XtNlabel, message);
7031       XtSetValues(messageWidget, &arg, 1);
7032     };
7033   
7034   return;
7035 }
7036
7037 void DisplayTitle(text)
7038      char *text;
7039 {
7040     Arg args[16];
7041     int i;
7042     char title[MSG_SIZ];
7043     char icon[MSG_SIZ];
7044
7045     if (text == NULL) text = "";
7046
7047     if (appData.titleInWindow) {
7048         i = 0;
7049         XtSetArg(args[i], XtNlabel, text);   i++;
7050         XtSetValues(titleWidget, args, i);
7051     }
7052
7053     if (*text != NULLCHAR) {
7054         strcpy(icon, text);
7055         strcpy(title, text);
7056     } else if (appData.icsActive) {
7057         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7058         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7059     } else if (appData.cmailGameName[0] != NULLCHAR) {
7060         snprintf(icon, sizeof(icon), "%s", "CMail");
7061         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7062 #ifdef GOTHIC
7063     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7064     } else if (gameInfo.variant == VariantGothic) {
7065         strcpy(icon, programName);
7066         strcpy(title, GOTHIC);
7067 #endif
7068 #ifdef FALCON
7069     } else if (gameInfo.variant == VariantFalcon) {
7070         strcpy(icon, programName);
7071         strcpy(title, FALCON);
7072 #endif
7073     } else if (appData.noChessProgram) {
7074         strcpy(icon, programName);
7075         strcpy(title, programName);
7076     } else {
7077         strcpy(icon, first.tidy);
7078         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7079     }
7080     i = 0;
7081     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
7082     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
7083     XtSetValues(shellWidget, args, i);
7084 }
7085
7086
7087 void DisplayError(message, error)
7088      String message;
7089      int error;
7090 {
7091     char buf[MSG_SIZ];
7092
7093     if (error == 0) {
7094         if (appData.debugMode || appData.matchMode) {
7095             fprintf(stderr, "%s: %s\n", programName, message);
7096         }
7097     } else {
7098         if (appData.debugMode || appData.matchMode) {
7099             fprintf(stderr, "%s: %s: %s\n",
7100                     programName, message, strerror(error));
7101         }
7102         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7103         message = buf;
7104     }
7105     ErrorPopUp(_("Error"), message, FALSE);
7106 }
7107
7108
7109 void DisplayMoveError(message)
7110      String message;
7111 {
7112     fromX = fromY = -1;
7113     ClearHighlights();
7114     DrawPosition(FALSE, NULL);
7115     if (appData.debugMode || appData.matchMode) {
7116         fprintf(stderr, "%s: %s\n", programName, message);
7117     }
7118     if (appData.popupMoveErrors) {
7119         ErrorPopUp(_("Error"), message, FALSE);
7120     } else {
7121         DisplayMessage(message, "");
7122     }
7123 }
7124
7125
7126 void DisplayFatalError(message, error, status)
7127      String message;
7128      int error, status;
7129 {
7130     char buf[MSG_SIZ];
7131
7132     errorExitStatus = status;
7133     if (error == 0) {
7134         fprintf(stderr, "%s: %s\n", programName, message);
7135     } else {
7136         fprintf(stderr, "%s: %s: %s\n",
7137                 programName, message, strerror(error));
7138         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7139         message = buf;
7140     }
7141     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7142       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7143     } else {
7144       ExitEvent(status);
7145     }
7146 }
7147
7148 void DisplayInformation(message)
7149      String message;
7150 {
7151     ErrorPopDown();
7152     ErrorPopUp(_("Information"), message, TRUE);
7153 }
7154
7155 void DisplayNote(message)
7156      String message;
7157 {
7158     ErrorPopDown();
7159     ErrorPopUp(_("Note"), message, FALSE);
7160 }
7161
7162 static int
7163 NullXErrorCheck(dpy, error_event)
7164      Display *dpy;
7165      XErrorEvent *error_event;
7166 {
7167     return 0;
7168 }
7169
7170 void DisplayIcsInteractionTitle(message)
7171      String message;
7172 {
7173   if (oldICSInteractionTitle == NULL) {
7174     /* Magic to find the old window title, adapted from vim */
7175     char *wina = getenv("WINDOWID");
7176     if (wina != NULL) {
7177       Window win = (Window) atoi(wina);
7178       Window root, parent, *children;
7179       unsigned int nchildren;
7180       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7181       for (;;) {
7182         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7183         if (!XQueryTree(xDisplay, win, &root, &parent,
7184                         &children, &nchildren)) break;
7185         if (children) XFree((void *)children);
7186         if (parent == root || parent == 0) break;
7187         win = parent;
7188       }
7189       XSetErrorHandler(oldHandler);
7190     }
7191     if (oldICSInteractionTitle == NULL) {
7192       oldICSInteractionTitle = "xterm";
7193     }
7194   }
7195   printf("\033]0;%s\007", message);
7196   fflush(stdout);
7197 }
7198
7199 char pendingReplyPrefix[MSG_SIZ];
7200 ProcRef pendingReplyPR;
7201
7202 void AskQuestionProc(w, event, prms, nprms)
7203      Widget w;
7204      XEvent *event;
7205      String *prms;
7206      Cardinal *nprms;
7207 {
7208     if (*nprms != 4) {
7209         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7210                 *nprms);
7211         return;
7212     }
7213     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7214 }
7215
7216 void AskQuestionPopDown()
7217 {
7218     if (!askQuestionUp) return;
7219     XtPopdown(askQuestionShell);
7220     XtDestroyWidget(askQuestionShell);
7221     askQuestionUp = False;
7222 }
7223
7224 void AskQuestionReplyAction(w, event, prms, nprms)
7225      Widget w;
7226      XEvent *event;
7227      String *prms;
7228      Cardinal *nprms;
7229 {
7230     char buf[MSG_SIZ];
7231     int err;
7232     String reply;
7233
7234     reply = XawDialogGetValueString(w = XtParent(w));
7235     strcpy(buf, pendingReplyPrefix);
7236     if (*buf) strcat(buf, " ");
7237     strcat(buf, reply);
7238     strcat(buf, "\n");
7239     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7240     AskQuestionPopDown();
7241
7242     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7243 }
7244
7245 void AskQuestionCallback(w, client_data, call_data)
7246      Widget w;
7247      XtPointer client_data, call_data;
7248 {
7249     String name;
7250     Arg args[16];
7251
7252     XtSetArg(args[0], XtNlabel, &name);
7253     XtGetValues(w, args, 1);
7254
7255     if (strcmp(name, _("cancel")) == 0) {
7256         AskQuestionPopDown();
7257     } else {
7258         AskQuestionReplyAction(w, NULL, NULL, NULL);
7259     }
7260 }
7261
7262 void AskQuestion(title, question, replyPrefix, pr)
7263      char *title, *question, *replyPrefix;
7264      ProcRef pr;
7265 {
7266     Arg args[16];
7267     Widget popup, layout, dialog, edit;
7268     Window root, child;
7269     int x, y, i;
7270     int win_x, win_y;
7271     unsigned int mask;
7272
7273     strcpy(pendingReplyPrefix, replyPrefix);
7274     pendingReplyPR = pr;
7275
7276     i = 0;
7277     XtSetArg(args[i], XtNresizable, True); i++;
7278     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7279     askQuestionShell = popup =
7280       XtCreatePopupShell(title, transientShellWidgetClass,
7281                          shellWidget, args, i);
7282
7283     layout =
7284       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7285                             layoutArgs, XtNumber(layoutArgs));
7286
7287     i = 0;
7288     XtSetArg(args[i], XtNlabel, question); i++;
7289     XtSetArg(args[i], XtNvalue, ""); i++;
7290     XtSetArg(args[i], XtNborderWidth, 0); i++;
7291     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7292                                    layout, args, i);
7293
7294     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7295                        (XtPointer) dialog);
7296     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7297                        (XtPointer) dialog);
7298
7299     XtRealizeWidget(popup);
7300     CatchDeleteWindow(popup, "AskQuestionPopDown");
7301
7302     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7303                   &x, &y, &win_x, &win_y, &mask);
7304
7305     XtSetArg(args[0], XtNx, x - 10);
7306     XtSetArg(args[1], XtNy, y - 30);
7307     XtSetValues(popup, args, 2);
7308
7309     XtPopup(popup, XtGrabExclusive);
7310     askQuestionUp = True;
7311
7312     edit = XtNameToWidget(dialog, "*value");
7313     XtSetKeyboardFocus(popup, edit);
7314 }
7315
7316
7317 void
7318 PlaySound(name)
7319      char *name;
7320 {
7321   if (*name == NULLCHAR) {
7322     return;
7323   } else if (strcmp(name, "$") == 0) {
7324     putc(BELLCHAR, stderr);
7325   } else {
7326     char buf[2048];
7327     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7328     system(buf);
7329   }
7330 }
7331
7332 void
7333 RingBell()
7334 {
7335   PlaySound(appData.soundMove);
7336 }
7337
7338 void
7339 PlayIcsWinSound()
7340 {
7341   PlaySound(appData.soundIcsWin);
7342 }
7343
7344 void
7345 PlayIcsLossSound()
7346 {
7347   PlaySound(appData.soundIcsLoss);
7348 }
7349
7350 void
7351 PlayIcsDrawSound()
7352 {
7353   PlaySound(appData.soundIcsDraw);
7354 }
7355
7356 void
7357 PlayIcsUnfinishedSound()
7358 {
7359   PlaySound(appData.soundIcsUnfinished);
7360 }
7361
7362 void
7363 PlayAlarmSound()
7364 {
7365   PlaySound(appData.soundIcsAlarm);
7366 }
7367
7368 void
7369 EchoOn()
7370 {
7371     system("stty echo");
7372 }
7373
7374 void
7375 EchoOff()
7376 {
7377     system("stty -echo");
7378 }
7379
7380 void
7381 Colorize(cc, continuation)
7382      ColorClass cc;
7383      int continuation;
7384 {
7385     char buf[MSG_SIZ];
7386     int count, outCount, error;
7387
7388     if (textColors[(int)cc].bg > 0) {
7389         if (textColors[(int)cc].fg > 0) {
7390             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7391                     textColors[(int)cc].fg, textColors[(int)cc].bg);
7392         } else {
7393             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7394                     textColors[(int)cc].bg);
7395         }
7396     } else {
7397         if (textColors[(int)cc].fg > 0) {
7398             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7399                     textColors[(int)cc].fg);
7400         } else {
7401             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
7402         }
7403     }
7404     count = strlen(buf);
7405     outCount = OutputToProcess(NoProc, buf, count, &error);
7406     if (outCount < count) {
7407         DisplayFatalError(_("Error writing to display"), error, 1);
7408     }
7409
7410     if (continuation) return;
7411     switch (cc) {
7412     case ColorShout:
7413       PlaySound(appData.soundShout);
7414       break;
7415     case ColorSShout:
7416       PlaySound(appData.soundSShout);
7417       break;
7418     case ColorChannel1:
7419       PlaySound(appData.soundChannel1);
7420       break;
7421     case ColorChannel:
7422       PlaySound(appData.soundChannel);
7423       break;
7424     case ColorKibitz:
7425       PlaySound(appData.soundKibitz);
7426       break;
7427     case ColorTell:
7428       PlaySound(appData.soundTell);
7429       break;
7430     case ColorChallenge:
7431       PlaySound(appData.soundChallenge);
7432       break;
7433     case ColorRequest:
7434       PlaySound(appData.soundRequest);
7435       break;
7436     case ColorSeek:
7437       PlaySound(appData.soundSeek);
7438       break;
7439     case ColorNormal:
7440     case ColorNone:
7441     default:
7442       break;
7443     }
7444 }
7445
7446 char *UserName()
7447 {
7448     return getpwuid(getuid())->pw_name;
7449 }
7450
7451 static char *ExpandPathName(path)
7452      char *path;
7453 {
7454     static char static_buf[2000];
7455     char *d, *s, buf[2000];
7456     struct passwd *pwd;
7457
7458     s = path;
7459     d = static_buf;
7460
7461     while (*s && isspace(*s))
7462       ++s;
7463
7464     if (!*s) {
7465         *d = 0;
7466         return static_buf;
7467     }
7468
7469     if (*s == '~') {
7470         if (*(s+1) == '/') {
7471             strcpy(d, getpwuid(getuid())->pw_dir);
7472             strcat(d, s+1);
7473         }
7474         else {
7475             strcpy(buf, s+1);
7476             *strchr(buf, '/') = 0;
7477             pwd = getpwnam(buf);
7478             if (!pwd)
7479               {
7480                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7481                           buf, path);
7482                   return NULL;
7483               }
7484             strcpy(d, pwd->pw_dir);
7485             strcat(d, strchr(s+1, '/'));
7486         }
7487     }
7488     else
7489       strcpy(d, s);
7490
7491     return static_buf;
7492 }
7493
7494 char *HostName()
7495 {
7496     static char host_name[MSG_SIZ];
7497
7498 #if HAVE_GETHOSTNAME
7499     gethostname(host_name, MSG_SIZ);
7500     return host_name;
7501 #else  /* not HAVE_GETHOSTNAME */
7502 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7503     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7504     return host_name;
7505 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7506     return "localhost";
7507 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7508 #endif /* not HAVE_GETHOSTNAME */
7509 }
7510
7511 XtIntervalId delayedEventTimerXID = 0;
7512 DelayedEventCallback delayedEventCallback = 0;
7513
7514 void
7515 FireDelayedEvent()
7516 {
7517     delayedEventTimerXID = 0;
7518     delayedEventCallback();
7519 }
7520
7521 void
7522 ScheduleDelayedEvent(cb, millisec)
7523      DelayedEventCallback cb; long millisec;
7524 {
7525     if(delayedEventTimerXID && delayedEventCallback == cb)
7526         // [HGM] alive: replace, rather than add or flush identical event
7527         XtRemoveTimeOut(delayedEventTimerXID);
7528     delayedEventCallback = cb;
7529     delayedEventTimerXID =
7530       XtAppAddTimeOut(appContext, millisec,
7531                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7532 }
7533
7534 DelayedEventCallback
7535 GetDelayedEvent()
7536 {
7537   if (delayedEventTimerXID) {
7538     return delayedEventCallback;
7539   } else {
7540     return NULL;
7541   }
7542 }
7543
7544 void
7545 CancelDelayedEvent()
7546 {
7547   if (delayedEventTimerXID) {
7548     XtRemoveTimeOut(delayedEventTimerXID);
7549     delayedEventTimerXID = 0;
7550   }
7551 }
7552
7553 XtIntervalId loadGameTimerXID = 0;
7554
7555 int LoadGameTimerRunning()
7556 {
7557     return loadGameTimerXID != 0;
7558 }
7559
7560 int StopLoadGameTimer()
7561 {
7562     if (loadGameTimerXID != 0) {
7563         XtRemoveTimeOut(loadGameTimerXID);
7564         loadGameTimerXID = 0;
7565         return TRUE;
7566     } else {
7567         return FALSE;
7568     }
7569 }
7570
7571 void
7572 LoadGameTimerCallback(arg, id)
7573      XtPointer arg;
7574      XtIntervalId *id;
7575 {
7576     loadGameTimerXID = 0;
7577     AutoPlayGameLoop();
7578 }
7579
7580 void
7581 StartLoadGameTimer(millisec)
7582      long millisec;
7583 {
7584     loadGameTimerXID =
7585       XtAppAddTimeOut(appContext, millisec,
7586                       (XtTimerCallbackProc) LoadGameTimerCallback,
7587                       (XtPointer) 0);
7588 }
7589
7590 XtIntervalId analysisClockXID = 0;
7591
7592 void
7593 AnalysisClockCallback(arg, id)
7594      XtPointer arg;
7595      XtIntervalId *id;
7596 {
7597     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7598          || appData.icsEngineAnalyze) { // [DM]
7599         AnalysisPeriodicEvent(0);
7600         StartAnalysisClock();
7601     }
7602 }
7603
7604 void
7605 StartAnalysisClock()
7606 {
7607     analysisClockXID =
7608       XtAppAddTimeOut(appContext, 2000,
7609                       (XtTimerCallbackProc) AnalysisClockCallback,
7610                       (XtPointer) 0);
7611 }
7612
7613 XtIntervalId clockTimerXID = 0;
7614
7615 int ClockTimerRunning()
7616 {
7617     return clockTimerXID != 0;
7618 }
7619
7620 int StopClockTimer()
7621 {
7622     if (clockTimerXID != 0) {
7623         XtRemoveTimeOut(clockTimerXID);
7624         clockTimerXID = 0;
7625         return TRUE;
7626     } else {
7627         return FALSE;
7628     }
7629 }
7630
7631 void
7632 ClockTimerCallback(arg, id)
7633      XtPointer arg;
7634      XtIntervalId *id;
7635 {
7636     clockTimerXID = 0;
7637     DecrementClocks();
7638 }
7639
7640 void
7641 StartClockTimer(millisec)
7642      long millisec;
7643 {
7644     clockTimerXID =
7645       XtAppAddTimeOut(appContext, millisec,
7646                       (XtTimerCallbackProc) ClockTimerCallback,
7647                       (XtPointer) 0);
7648 }
7649
7650 void
7651 DisplayTimerLabel(w, color, timer, highlight)
7652      Widget w;
7653      char *color;
7654      long timer;
7655      int highlight;
7656 {
7657     char buf[MSG_SIZ];
7658     Arg args[16];
7659
7660     /* check for low time warning */
7661     Pixel foregroundOrWarningColor = timerForegroundPixel;
7662
7663     if (timer > 0 &&
7664         appData.lowTimeWarning && 
7665         (timer / 1000) < appData.icsAlarmTime)
7666       foregroundOrWarningColor = lowTimeWarningColor;
7667
7668     if (appData.clockMode) {
7669         sprintf(buf, "%s: %s", color, TimeString(timer));
7670         XtSetArg(args[0], XtNlabel, buf);
7671     } else {
7672         sprintf(buf, "%s  ", color);
7673         XtSetArg(args[0], XtNlabel, buf);
7674     }
7675
7676     if (highlight) {
7677
7678         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7679         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7680     } else {
7681         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7682         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7683     }
7684
7685     XtSetValues(w, args, 3);
7686 }
7687
7688 void
7689 DisplayWhiteClock(timeRemaining, highlight)
7690      long timeRemaining;
7691      int highlight;
7692 {
7693     Arg args[16];
7694
7695     if(appData.noGUI) return;
7696     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7697     if (highlight && iconPixmap == bIconPixmap) {
7698         iconPixmap = wIconPixmap;
7699         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7700         XtSetValues(shellWidget, args, 1);
7701     }
7702 }
7703
7704 void
7705 DisplayBlackClock(timeRemaining, highlight)
7706      long timeRemaining;
7707      int highlight;
7708 {
7709     Arg args[16];
7710
7711     if(appData.noGUI) return;
7712     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7713     if (highlight && iconPixmap == wIconPixmap) {
7714         iconPixmap = bIconPixmap;
7715         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7716         XtSetValues(shellWidget, args, 1);
7717     }
7718 }
7719
7720 #define CPNone 0
7721 #define CPReal 1
7722 #define CPComm 2
7723 #define CPSock 3
7724 #define CPLoop 4
7725 typedef int CPKind;
7726
7727 typedef struct {
7728     CPKind kind;
7729     int pid;
7730     int fdTo, fdFrom;
7731 } ChildProc;
7732
7733
7734 int StartChildProcess(cmdLine, dir, pr)
7735      char *cmdLine;
7736      char *dir;
7737      ProcRef *pr;
7738 {
7739     char *argv[64], *p;
7740     int i, pid;
7741     int to_prog[2], from_prog[2];
7742     ChildProc *cp;
7743     char buf[MSG_SIZ];
7744
7745     if (appData.debugMode) {
7746         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7747     }
7748
7749     /* We do NOT feed the cmdLine to the shell; we just
7750        parse it into blank-separated arguments in the
7751        most simple-minded way possible.
7752        */
7753     i = 0;
7754     strcpy(buf, cmdLine);
7755     p = buf;
7756     for (;;) {
7757         while(*p == ' ') p++;
7758         argv[i++] = p;
7759         if(*p == '"' || *p == '\'')
7760              p = strchr(++argv[i-1], *p);
7761         else p = strchr(p, ' ');
7762         if (p == NULL) break;
7763         *p++ = NULLCHAR;
7764     }
7765     argv[i] = NULL;
7766
7767     SetUpChildIO(to_prog, from_prog);
7768
7769     if ((pid = fork()) == 0) {
7770         /* Child process */
7771         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7772         close(to_prog[1]);     // first close the unused pipe ends
7773         close(from_prog[0]);
7774         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7775         dup2(from_prog[1], 1);
7776         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7777         close(from_prog[1]);                   // and closing again loses one of the pipes!
7778         if(fileno(stderr) >= 2) // better safe than sorry...
7779                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7780
7781         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7782             perror(dir);
7783             exit(1);
7784         }
7785
7786         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7787
7788         execvp(argv[0], argv);
7789
7790         /* If we get here, exec failed */
7791         perror(argv[0]);
7792         exit(1);
7793     }
7794
7795     /* Parent process */
7796     close(to_prog[0]);
7797     close(from_prog[1]);
7798
7799     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7800     cp->kind = CPReal;
7801     cp->pid = pid;
7802     cp->fdFrom = from_prog[0];
7803     cp->fdTo = to_prog[1];
7804     *pr = (ProcRef) cp;
7805     return 0;
7806 }
7807
7808 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7809 static RETSIGTYPE AlarmCallBack(int n)
7810 {
7811     return;
7812 }
7813
7814 void
7815 DestroyChildProcess(pr, signalType)
7816      ProcRef pr;
7817      int signalType;
7818 {
7819     ChildProc *cp = (ChildProc *) pr;
7820
7821     if (cp->kind != CPReal) return;
7822     cp->kind = CPNone;
7823     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7824         signal(SIGALRM, AlarmCallBack);
7825         alarm(3);
7826         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7827             kill(cp->pid, SIGKILL); // kill it forcefully
7828             wait((int *) 0);        // and wait again
7829         }
7830     } else {
7831         if (signalType) {
7832             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7833         }
7834         /* Process is exiting either because of the kill or because of
7835            a quit command sent by the backend; either way, wait for it to die.
7836         */
7837         wait((int *) 0);
7838     }
7839     close(cp->fdFrom);
7840     close(cp->fdTo);
7841 }
7842
7843 void
7844 InterruptChildProcess(pr)
7845      ProcRef pr;
7846 {
7847     ChildProc *cp = (ChildProc *) pr;
7848
7849     if (cp->kind != CPReal) return;
7850     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7851 }
7852
7853 int OpenTelnet(host, port, pr)
7854      char *host;
7855      char *port;
7856      ProcRef *pr;
7857 {
7858     char cmdLine[MSG_SIZ];
7859
7860     if (port[0] == NULLCHAR) {
7861       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7862     } else {
7863       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7864     }
7865     return StartChildProcess(cmdLine, "", pr);
7866 }
7867
7868 int OpenTCP(host, port, pr)
7869      char *host;
7870      char *port;
7871      ProcRef *pr;
7872 {
7873 #if OMIT_SOCKETS
7874     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7875 #else  /* !OMIT_SOCKETS */
7876     int s;
7877     struct sockaddr_in sa;
7878     struct hostent     *hp;
7879     unsigned short uport;
7880     ChildProc *cp;
7881
7882     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7883         return errno;
7884     }
7885
7886     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7887     sa.sin_family = AF_INET;
7888     sa.sin_addr.s_addr = INADDR_ANY;
7889     uport = (unsigned short) 0;
7890     sa.sin_port = htons(uport);
7891     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7892         return errno;
7893     }
7894
7895     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7896     if (!(hp = gethostbyname(host))) {
7897         int b0, b1, b2, b3;
7898         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7899             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7900             hp->h_addrtype = AF_INET;
7901             hp->h_length = 4;
7902             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7903             hp->h_addr_list[0] = (char *) malloc(4);
7904             hp->h_addr_list[0][0] = b0;
7905             hp->h_addr_list[0][1] = b1;
7906             hp->h_addr_list[0][2] = b2;
7907             hp->h_addr_list[0][3] = b3;
7908         } else {
7909             return ENOENT;
7910         }
7911     }
7912     sa.sin_family = hp->h_addrtype;
7913     uport = (unsigned short) atoi(port);
7914     sa.sin_port = htons(uport);
7915     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7916
7917     if (connect(s, (struct sockaddr *) &sa,
7918                 sizeof(struct sockaddr_in)) < 0) {
7919         return errno;
7920     }
7921
7922     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7923     cp->kind = CPSock;
7924     cp->pid = 0;
7925     cp->fdFrom = s;
7926     cp->fdTo = s;
7927     *pr = (ProcRef) cp;
7928
7929 #endif /* !OMIT_SOCKETS */
7930
7931     return 0;
7932 }
7933
7934 int OpenCommPort(name, pr)
7935      char *name;
7936      ProcRef *pr;
7937 {
7938     int fd;
7939     ChildProc *cp;
7940
7941     fd = open(name, 2, 0);
7942     if (fd < 0) return errno;
7943
7944     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7945     cp->kind = CPComm;
7946     cp->pid = 0;
7947     cp->fdFrom = fd;
7948     cp->fdTo = fd;
7949     *pr = (ProcRef) cp;
7950
7951     return 0;
7952 }
7953
7954 int OpenLoopback(pr)
7955      ProcRef *pr;
7956 {
7957     ChildProc *cp;
7958     int to[2], from[2];
7959
7960     SetUpChildIO(to, from);
7961
7962     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7963     cp->kind = CPLoop;
7964     cp->pid = 0;
7965     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7966     cp->fdTo = to[1];
7967     *pr = (ProcRef) cp;
7968
7969     return 0;
7970 }
7971
7972 int OpenRcmd(host, user, cmd, pr)
7973      char *host, *user, *cmd;
7974      ProcRef *pr;
7975 {
7976     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7977     return -1;
7978 }
7979
7980 #define INPUT_SOURCE_BUF_SIZE 8192
7981
7982 typedef struct {
7983     CPKind kind;
7984     int fd;
7985     int lineByLine;
7986     char *unused;
7987     InputCallback func;
7988     XtInputId xid;
7989     char buf[INPUT_SOURCE_BUF_SIZE];
7990     VOIDSTAR closure;
7991 } InputSource;
7992
7993 void
7994 DoInputCallback(closure, source, xid)
7995      caddr_t closure;
7996      int *source;
7997      XtInputId *xid;
7998 {
7999     InputSource *is = (InputSource *) closure;
8000     int count;
8001     int error;
8002     char *p, *q;
8003
8004     if (is->lineByLine) {
8005         count = read(is->fd, is->unused,
8006                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8007         if (count <= 0) {
8008             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8009             return;
8010         }
8011         is->unused += count;
8012         p = is->buf;
8013         while (p < is->unused) {
8014             q = memchr(p, '\n', is->unused - p);
8015             if (q == NULL) break;
8016             q++;
8017             (is->func)(is, is->closure, p, q - p, 0);
8018             p = q;
8019         }
8020         q = is->buf;
8021         while (p < is->unused) {
8022             *q++ = *p++;
8023         }
8024         is->unused = q;
8025     } else {
8026         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8027         if (count == -1)
8028           error = errno;
8029         else
8030           error = 0;
8031         (is->func)(is, is->closure, is->buf, count, error);
8032     }
8033 }
8034
8035 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8036      ProcRef pr;
8037      int lineByLine;
8038      InputCallback func;
8039      VOIDSTAR closure;
8040 {
8041     InputSource *is;
8042     ChildProc *cp = (ChildProc *) pr;
8043
8044     is = (InputSource *) calloc(1, sizeof(InputSource));
8045     is->lineByLine = lineByLine;
8046     is->func = func;
8047     if (pr == NoProc) {
8048         is->kind = CPReal;
8049         is->fd = fileno(stdin);
8050     } else {
8051         is->kind = cp->kind;
8052         is->fd = cp->fdFrom;
8053     }
8054     if (lineByLine) {
8055         is->unused = is->buf;
8056     }
8057
8058     is->xid = XtAppAddInput(appContext, is->fd,
8059                             (XtPointer) (XtInputReadMask),
8060                             (XtInputCallbackProc) DoInputCallback,
8061                             (XtPointer) is);
8062     is->closure = closure;
8063     return (InputSourceRef) is;
8064 }
8065
8066 void
8067 RemoveInputSource(isr)
8068      InputSourceRef isr;
8069 {
8070     InputSource *is = (InputSource *) isr;
8071
8072     if (is->xid == 0) return;
8073     XtRemoveInput(is->xid);
8074     is->xid = 0;
8075 }
8076
8077 int OutputToProcess(pr, message, count, outError)
8078      ProcRef pr;
8079      char *message;
8080      int count;
8081      int *outError;
8082 {
8083     static int line = 0;
8084     ChildProc *cp = (ChildProc *) pr;
8085     int outCount;
8086
8087     if (pr == NoProc)
8088     {
8089         if (appData.noJoin || !appData.useInternalWrap)
8090             outCount = fwrite(message, 1, count, stdout);
8091         else
8092         {
8093             int width = get_term_width();
8094             int len = wrap(NULL, message, count, width, &line);
8095             char *msg = malloc(len);
8096             int dbgchk;
8097
8098             if (!msg)
8099                 outCount = fwrite(message, 1, count, stdout);
8100             else
8101             {
8102                 dbgchk = wrap(msg, message, count, width, &line);
8103                 if (dbgchk != len && appData.debugMode)
8104                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8105                 outCount = fwrite(msg, 1, dbgchk, stdout);
8106                 free(msg);
8107             }
8108         }
8109     }
8110     else
8111       outCount = write(cp->fdTo, message, count);
8112
8113     if (outCount == -1)
8114       *outError = errno;
8115     else
8116       *outError = 0;
8117
8118     return outCount;
8119 }
8120
8121 /* Output message to process, with "ms" milliseconds of delay
8122    between each character. This is needed when sending the logon
8123    script to ICC, which for some reason doesn't like the
8124    instantaneous send. */
8125 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8126      ProcRef pr;
8127      char *message;
8128      int count;
8129      int *outError;
8130      long msdelay;
8131 {
8132     ChildProc *cp = (ChildProc *) pr;
8133     int outCount = 0;
8134     int r;
8135
8136     while (count--) {
8137         r = write(cp->fdTo, message++, 1);
8138         if (r == -1) {
8139             *outError = errno;
8140             return outCount;
8141         }
8142         ++outCount;
8143         if (msdelay >= 0)
8144           TimeDelay(msdelay);
8145     }
8146
8147     return outCount;
8148 }
8149
8150 /****   Animation code by Hugh Fisher, DCS, ANU.
8151
8152         Known problem: if a window overlapping the board is
8153         moved away while a piece is being animated underneath,
8154         the newly exposed area won't be updated properly.
8155         I can live with this.
8156
8157         Known problem: if you look carefully at the animation
8158         of pieces in mono mode, they are being drawn as solid
8159         shapes without interior detail while moving. Fixing
8160         this would be a major complication for minimal return.
8161 ****/
8162
8163 /*      Masks for XPM pieces. Black and white pieces can have
8164         different shapes, but in the interest of retaining my
8165         sanity pieces must have the same outline on both light
8166         and dark squares, and all pieces must use the same
8167         background square colors/images.                */
8168
8169 static int xpmDone = 0;
8170
8171 static void
8172 CreateAnimMasks (pieceDepth)
8173      int pieceDepth;
8174 {
8175   ChessSquare   piece;
8176   Pixmap        buf;
8177   GC            bufGC, maskGC;
8178   int           kind, n;
8179   unsigned long plane;
8180   XGCValues     values;
8181
8182   /* Need a bitmap just to get a GC with right depth */
8183   buf = XCreatePixmap(xDisplay, xBoardWindow,
8184                         8, 8, 1);
8185   values.foreground = 1;
8186   values.background = 0;
8187   /* Don't use XtGetGC, not read only */
8188   maskGC = XCreateGC(xDisplay, buf,
8189                     GCForeground | GCBackground, &values);
8190   XFreePixmap(xDisplay, buf);
8191
8192   buf = XCreatePixmap(xDisplay, xBoardWindow,
8193                       squareSize, squareSize, pieceDepth);
8194   values.foreground = XBlackPixel(xDisplay, xScreen);
8195   values.background = XWhitePixel(xDisplay, xScreen);
8196   bufGC = XCreateGC(xDisplay, buf,
8197                     GCForeground | GCBackground, &values);
8198
8199   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8200     /* Begin with empty mask */
8201     if(!xpmDone) // [HGM] pieces: keep using existing
8202     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8203                                  squareSize, squareSize, 1);
8204     XSetFunction(xDisplay, maskGC, GXclear);
8205     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8206                    0, 0, squareSize, squareSize);
8207
8208     /* Take a copy of the piece */
8209     if (White(piece))
8210       kind = 0;
8211     else
8212       kind = 2;
8213     XSetFunction(xDisplay, bufGC, GXcopy);
8214     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8215               buf, bufGC,
8216               0, 0, squareSize, squareSize, 0, 0);
8217
8218     /* XOR the background (light) over the piece */
8219     XSetFunction(xDisplay, bufGC, GXxor);
8220     if (useImageSqs)
8221       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8222                 0, 0, squareSize, squareSize, 0, 0);
8223     else {
8224       XSetForeground(xDisplay, bufGC, lightSquareColor);
8225       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8226     }
8227
8228     /* We now have an inverted piece image with the background
8229        erased. Construct mask by just selecting all the non-zero
8230        pixels - no need to reconstruct the original image.      */
8231     XSetFunction(xDisplay, maskGC, GXor);
8232     plane = 1;
8233     /* Might be quicker to download an XImage and create bitmap
8234        data from it rather than this N copies per piece, but it
8235        only takes a fraction of a second and there is a much
8236        longer delay for loading the pieces.             */
8237     for (n = 0; n < pieceDepth; n ++) {
8238       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8239                  0, 0, squareSize, squareSize,
8240                  0, 0, plane);
8241       plane = plane << 1;
8242     }
8243   }
8244   /* Clean up */
8245   XFreePixmap(xDisplay, buf);
8246   XFreeGC(xDisplay, bufGC);
8247   XFreeGC(xDisplay, maskGC);
8248 }
8249
8250 static void
8251 InitAnimState (anim, info)
8252   AnimState * anim;
8253   XWindowAttributes * info;
8254 {
8255   XtGCMask  mask;
8256   XGCValues values;
8257
8258   /* Each buffer is square size, same depth as window */
8259   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8260                         squareSize, squareSize, info->depth);
8261   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8262                         squareSize, squareSize, info->depth);
8263
8264   /* Create a plain GC for blitting */
8265   mask = GCForeground | GCBackground | GCFunction |
8266          GCPlaneMask | GCGraphicsExposures;
8267   values.foreground = XBlackPixel(xDisplay, xScreen);
8268   values.background = XWhitePixel(xDisplay, xScreen);
8269   values.function   = GXcopy;
8270   values.plane_mask = AllPlanes;
8271   values.graphics_exposures = False;
8272   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8273
8274   /* Piece will be copied from an existing context at
8275      the start of each new animation/drag. */
8276   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8277
8278   /* Outline will be a read-only copy of an existing */
8279   anim->outlineGC = None;
8280 }
8281
8282 static void
8283 CreateAnimVars ()
8284 {
8285   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8286   XWindowAttributes info;
8287
8288   if (xpmDone && gameInfo.variant == old) return;
8289   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8290   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8291
8292   InitAnimState(&game, &info);
8293   InitAnimState(&player, &info);
8294
8295   /* For XPM pieces, we need bitmaps to use as masks. */
8296   if (useImages)
8297     CreateAnimMasks(info.depth);
8298    xpmDone = 1;
8299 }
8300
8301 #ifndef HAVE_USLEEP
8302
8303 static Boolean frameWaiting;
8304
8305 static RETSIGTYPE FrameAlarm (sig)
8306      int sig;
8307 {
8308   frameWaiting = False;
8309   /* In case System-V style signals.  Needed?? */
8310   signal(SIGALRM, FrameAlarm);
8311 }
8312
8313 static void
8314 FrameDelay (time)
8315      int time;
8316 {
8317   struct itimerval delay;
8318
8319   XSync(xDisplay, False);
8320
8321   if (time > 0) {
8322     frameWaiting = True;
8323     signal(SIGALRM, FrameAlarm);
8324     delay.it_interval.tv_sec =
8325       delay.it_value.tv_sec = time / 1000;
8326     delay.it_interval.tv_usec =
8327       delay.it_value.tv_usec = (time % 1000) * 1000;
8328     setitimer(ITIMER_REAL, &delay, NULL);
8329     while (frameWaiting) pause();
8330     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8331     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8332     setitimer(ITIMER_REAL, &delay, NULL);
8333   }
8334 }
8335
8336 #else
8337
8338 static void
8339 FrameDelay (time)
8340      int time;
8341 {
8342   XSync(xDisplay, False);
8343   if (time > 0)
8344     usleep(time * 1000);
8345 }
8346
8347 #endif
8348
8349 /*      Convert board position to corner of screen rect and color       */
8350
8351 static void
8352 ScreenSquare(column, row, pt, color)
8353      int column; int row; XPoint * pt; int * color;
8354 {
8355   if (flipView) {
8356     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8357     pt->y = lineGap + row * (squareSize + lineGap);
8358   } else {
8359     pt->x = lineGap + column * (squareSize + lineGap);
8360     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8361   }
8362   *color = SquareColor(row, column);
8363 }
8364
8365 /*      Convert window coords to square                 */
8366
8367 static void
8368 BoardSquare(x, y, column, row)
8369      int x; int y; int * column; int * row;
8370 {
8371   *column = EventToSquare(x, BOARD_WIDTH);
8372   if (flipView && *column >= 0)
8373     *column = BOARD_WIDTH - 1 - *column;
8374   *row = EventToSquare(y, BOARD_HEIGHT);
8375   if (!flipView && *row >= 0)
8376     *row = BOARD_HEIGHT - 1 - *row;
8377 }
8378
8379 /*   Utilities  */
8380
8381 #undef Max  /* just in case */
8382 #undef Min
8383 #define Max(a, b) ((a) > (b) ? (a) : (b))
8384 #define Min(a, b) ((a) < (b) ? (a) : (b))
8385
8386 static void
8387 SetRect(rect, x, y, width, height)
8388      XRectangle * rect; int x; int y; int width; int height;
8389 {
8390   rect->x = x;
8391   rect->y = y;
8392   rect->width  = width;
8393   rect->height = height;
8394 }
8395
8396 /*      Test if two frames overlap. If they do, return
8397         intersection rect within old and location of
8398         that rect within new. */
8399
8400 static Boolean
8401 Intersect(old, new, size, area, pt)
8402      XPoint * old; XPoint * new;
8403      int size; XRectangle * area; XPoint * pt;
8404 {
8405   if (old->x > new->x + size || new->x > old->x + size ||
8406       old->y > new->y + size || new->y > old->y + size) {
8407     return False;
8408   } else {
8409     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8410             size - abs(old->x - new->x), size - abs(old->y - new->y));
8411     pt->x = Max(old->x - new->x, 0);
8412     pt->y = Max(old->y - new->y, 0);
8413     return True;
8414   }
8415 }
8416
8417 /*      For two overlapping frames, return the rect(s)
8418         in the old that do not intersect with the new.   */
8419
8420 static void
8421 CalcUpdateRects(old, new, size, update, nUpdates)
8422      XPoint * old; XPoint * new; int size;
8423      XRectangle update[]; int * nUpdates;
8424 {
8425   int        count;
8426
8427   /* If old = new (shouldn't happen) then nothing to draw */
8428   if (old->x == new->x && old->y == new->y) {
8429     *nUpdates = 0;
8430     return;
8431   }
8432   /* Work out what bits overlap. Since we know the rects
8433      are the same size we don't need a full intersect calc. */
8434   count = 0;
8435   /* Top or bottom edge? */
8436   if (new->y > old->y) {
8437     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8438     count ++;
8439   } else if (old->y > new->y) {
8440     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8441                               size, old->y - new->y);
8442     count ++;
8443   }
8444   /* Left or right edge - don't overlap any update calculated above. */
8445   if (new->x > old->x) {
8446     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8447                               new->x - old->x, size - abs(new->y - old->y));
8448     count ++;
8449   } else if (old->x > new->x) {
8450     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8451                               old->x - new->x, size - abs(new->y - old->y));
8452     count ++;
8453   }
8454   /* Done */
8455   *nUpdates = count;
8456 }
8457
8458 /*      Generate a series of frame coords from start->mid->finish.
8459         The movement rate doubles until the half way point is
8460         reached, then halves back down to the final destination,
8461         which gives a nice slow in/out effect. The algorithmn
8462         may seem to generate too many intermediates for short
8463         moves, but remember that the purpose is to attract the
8464         viewers attention to the piece about to be moved and
8465         then to where it ends up. Too few frames would be less
8466         noticeable.                                             */
8467
8468 static void
8469 Tween(start, mid, finish, factor, frames, nFrames)
8470      XPoint * start; XPoint * mid;
8471      XPoint * finish; int factor;
8472      XPoint frames[]; int * nFrames;
8473 {
8474   int fraction, n, count;
8475
8476   count = 0;
8477
8478   /* Slow in, stepping 1/16th, then 1/8th, ... */
8479   fraction = 1;
8480   for (n = 0; n < factor; n++)
8481     fraction *= 2;
8482   for (n = 0; n < factor; n++) {
8483     frames[count].x = start->x + (mid->x - start->x) / fraction;
8484     frames[count].y = start->y + (mid->y - start->y) / fraction;
8485     count ++;
8486     fraction = fraction / 2;
8487   }
8488
8489   /* Midpoint */
8490   frames[count] = *mid;
8491   count ++;
8492
8493   /* Slow out, stepping 1/2, then 1/4, ... */
8494   fraction = 2;
8495   for (n = 0; n < factor; n++) {
8496     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8497     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8498     count ++;
8499     fraction = fraction * 2;
8500   }
8501   *nFrames = count;
8502 }
8503
8504 /*      Draw a piece on the screen without disturbing what's there      */
8505
8506 static void
8507 SelectGCMask(piece, clip, outline, mask)
8508      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8509 {
8510   GC source;
8511
8512   /* Bitmap for piece being moved. */
8513   if (appData.monoMode) {
8514       *mask = *pieceToSolid(piece);
8515   } else if (useImages) {
8516 #if HAVE_LIBXPM
8517       *mask = xpmMask[piece];
8518 #else
8519       *mask = ximMaskPm[piece];
8520 #endif
8521   } else {
8522       *mask = *pieceToSolid(piece);
8523   }
8524
8525   /* GC for piece being moved. Square color doesn't matter, but
8526      since it gets modified we make a copy of the original. */
8527   if (White(piece)) {
8528     if (appData.monoMode)
8529       source = bwPieceGC;
8530     else
8531       source = wlPieceGC;
8532   } else {
8533     if (appData.monoMode)
8534       source = wbPieceGC;
8535     else
8536       source = blPieceGC;
8537   }
8538   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8539
8540   /* Outline only used in mono mode and is not modified */
8541   if (White(piece))
8542     *outline = bwPieceGC;
8543   else
8544     *outline = wbPieceGC;
8545 }
8546
8547 static void
8548 OverlayPiece(piece, clip, outline,  dest)
8549      ChessSquare piece; GC clip; GC outline; Drawable dest;
8550 {
8551   int   kind;
8552
8553   if (!useImages) {
8554     /* Draw solid rectangle which will be clipped to shape of piece */
8555     XFillRectangle(xDisplay, dest, clip,
8556                    0, 0, squareSize, squareSize);
8557     if (appData.monoMode)
8558       /* Also draw outline in contrasting color for black
8559          on black / white on white cases                */
8560       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8561                  0, 0, squareSize, squareSize, 0, 0, 1);
8562   } else {
8563     /* Copy the piece */
8564     if (White(piece))
8565       kind = 0;
8566     else
8567       kind = 2;
8568     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8569               dest, clip,
8570               0, 0, squareSize, squareSize,
8571               0, 0);
8572   }
8573 }
8574
8575 /* Animate the movement of a single piece */
8576
8577 static void
8578 BeginAnimation(anim, piece, startColor, start)
8579      AnimState *anim;
8580      ChessSquare piece;
8581      int startColor;
8582      XPoint * start;
8583 {
8584   Pixmap mask;
8585
8586   /* The old buffer is initialised with the start square (empty) */
8587   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
8588   anim->prevFrame = *start;
8589
8590   /* The piece will be drawn using its own bitmap as a matte    */
8591   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8592   XSetClipMask(xDisplay, anim->pieceGC, mask);
8593 }
8594
8595 static void
8596 AnimationFrame(anim, frame, piece)
8597      AnimState *anim;
8598      XPoint *frame;
8599      ChessSquare piece;
8600 {
8601   XRectangle updates[4];
8602   XRectangle overlap;
8603   XPoint     pt;
8604   int        count, i;
8605
8606   /* Save what we are about to draw into the new buffer */
8607   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8608             frame->x, frame->y, squareSize, squareSize,
8609             0, 0);
8610
8611   /* Erase bits of the previous frame */
8612   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8613     /* Where the new frame overlapped the previous,
8614        the contents in newBuf are wrong. */
8615     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8616               overlap.x, overlap.y,
8617               overlap.width, overlap.height,
8618               pt.x, pt.y);
8619     /* Repaint the areas in the old that don't overlap new */
8620     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8621     for (i = 0; i < count; i++)
8622       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8623                 updates[i].x - anim->prevFrame.x,
8624                 updates[i].y - anim->prevFrame.y,
8625                 updates[i].width, updates[i].height,
8626                 updates[i].x, updates[i].y);
8627   } else {
8628     /* Easy when no overlap */
8629     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8630                   0, 0, squareSize, squareSize,
8631                   anim->prevFrame.x, anim->prevFrame.y);
8632   }
8633
8634   /* Save this frame for next time round */
8635   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8636                 0, 0, squareSize, squareSize,
8637                 0, 0);
8638   anim->prevFrame = *frame;
8639
8640   /* Draw piece over original screen contents, not current,
8641      and copy entire rect. Wipes out overlapping piece images. */
8642   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8643   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8644                 0, 0, squareSize, squareSize,
8645                 frame->x, frame->y);
8646 }
8647
8648 static void
8649 EndAnimation (anim, finish)
8650      AnimState *anim;
8651      XPoint *finish;
8652 {
8653   XRectangle updates[4];
8654   XRectangle overlap;
8655   XPoint     pt;
8656   int        count, i;
8657
8658   /* The main code will redraw the final square, so we
8659      only need to erase the bits that don't overlap.    */
8660   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8661     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8662     for (i = 0; i < count; i++)
8663       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8664                 updates[i].x - anim->prevFrame.x,
8665                 updates[i].y - anim->prevFrame.y,
8666                 updates[i].width, updates[i].height,
8667                 updates[i].x, updates[i].y);
8668   } else {
8669     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8670                 0, 0, squareSize, squareSize,
8671                 anim->prevFrame.x, anim->prevFrame.y);
8672   }
8673 }
8674
8675 static void
8676 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8677      AnimState *anim;
8678      ChessSquare piece; int startColor;
8679      XPoint * start; XPoint * finish;
8680      XPoint frames[]; int nFrames;
8681 {
8682   int n;
8683
8684   BeginAnimation(anim, piece, startColor, start);
8685   for (n = 0; n < nFrames; n++) {
8686     AnimationFrame(anim, &(frames[n]), piece);
8687     FrameDelay(appData.animSpeed);
8688   }
8689   EndAnimation(anim, finish);
8690 }
8691
8692 /* Main control logic for deciding what to animate and how */
8693
8694 void
8695 AnimateMove(board, fromX, fromY, toX, toY)
8696      Board board;
8697      int fromX;
8698      int fromY;
8699      int toX;
8700      int toY;
8701 {
8702   ChessSquare piece;
8703   int hop;
8704   XPoint      start, finish, mid;
8705   XPoint      frames[kFactor * 2 + 1];
8706   int         nFrames, startColor, endColor;
8707
8708   /* Are we animating? */
8709   if (!appData.animate || appData.blindfold)
8710     return;
8711
8712   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
8713      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
8714         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8715
8716   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8717   piece = board[fromY][fromX];
8718   if (piece >= EmptySquare) return;
8719
8720 #if DONT_HOP
8721   hop = FALSE;
8722 #else
8723   hop = (piece == WhiteKnight || piece == BlackKnight);
8724 #endif
8725
8726   if (appData.debugMode) {
8727       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8728                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8729              piece, fromX, fromY, toX, toY);  }
8730
8731   ScreenSquare(fromX, fromY, &start, &startColor);
8732   ScreenSquare(toX, toY, &finish, &endColor);
8733
8734   if (hop) {
8735     /* Knight: make diagonal movement then straight */
8736     if (abs(toY - fromY) < abs(toX - fromX)) {
8737        mid.x = start.x + (finish.x - start.x) / 2;
8738        mid.y = finish.y;
8739      } else {
8740        mid.x = finish.x;
8741        mid.y = start.y + (finish.y - start.y) / 2;
8742      }
8743   } else {
8744     mid.x = start.x + (finish.x - start.x) / 2;
8745     mid.y = start.y + (finish.y - start.y) / 2;
8746   }
8747
8748   /* Don't use as many frames for very short moves */
8749   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8750     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8751   else
8752     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8753   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8754
8755   /* Be sure end square is redrawn */
8756   damage[0][toY][toX] = True;
8757 }
8758
8759 void
8760 DragPieceBegin(x, y)
8761      int x; int y;
8762 {
8763     int  boardX, boardY, color;
8764     XPoint corner;
8765
8766     /* Are we animating? */
8767     if (!appData.animateDragging || appData.blindfold)
8768       return;
8769
8770     /* Figure out which square we start in and the
8771        mouse position relative to top left corner. */
8772     BoardSquare(x, y, &boardX, &boardY);
8773     player.startBoardX = boardX;
8774     player.startBoardY = boardY;
8775     ScreenSquare(boardX, boardY, &corner, &color);
8776     player.startSquare  = corner;
8777     player.startColor   = color;
8778     /* As soon as we start dragging, the piece will jump slightly to
8779        be centered over the mouse pointer. */
8780     player.mouseDelta.x = squareSize/2;
8781     player.mouseDelta.y = squareSize/2;
8782     /* Initialise animation */
8783     player.dragPiece = PieceForSquare(boardX, boardY);
8784     /* Sanity check */
8785     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8786         player.dragActive = True;
8787         BeginAnimation(&player, player.dragPiece, color, &corner);
8788         /* Mark this square as needing to be redrawn. Note that
8789            we don't remove the piece though, since logically (ie
8790            as seen by opponent) the move hasn't been made yet. */
8791            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8792               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8793            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8794                      corner.x, corner.y, squareSize, squareSize,
8795                      0, 0); // [HGM] zh: unstack in stead of grab
8796         damage[0][boardY][boardX] = True;
8797     } else {
8798         player.dragActive = False;
8799     }
8800 }
8801
8802 static void
8803 DragPieceMove(x, y)
8804      int x; int y;
8805 {
8806     XPoint corner;
8807
8808     /* Are we animating? */
8809     if (!appData.animateDragging || appData.blindfold)
8810       return;
8811
8812     /* Sanity check */
8813     if (! player.dragActive)
8814       return;
8815     /* Move piece, maintaining same relative position
8816        of mouse within square    */
8817     corner.x = x - player.mouseDelta.x;
8818     corner.y = y - player.mouseDelta.y;
8819     AnimationFrame(&player, &corner, player.dragPiece);
8820 #if HIGHDRAG*0
8821     if (appData.highlightDragging) {
8822         int boardX, boardY;
8823         BoardSquare(x, y, &boardX, &boardY);
8824         SetHighlights(fromX, fromY, boardX, boardY);
8825     }
8826 #endif
8827 }
8828
8829 void
8830 DragPieceEnd(x, y)
8831      int x; int y;
8832 {
8833     int boardX, boardY, color;
8834     XPoint corner;
8835
8836     /* Are we animating? */
8837     if (!appData.animateDragging || appData.blindfold)
8838       return;
8839
8840     /* Sanity check */
8841     if (! player.dragActive)
8842       return;
8843     /* Last frame in sequence is square piece is
8844        placed on, which may not match mouse exactly. */
8845     BoardSquare(x, y, &boardX, &boardY);
8846     ScreenSquare(boardX, boardY, &corner, &color);
8847     EndAnimation(&player, &corner);
8848
8849     /* Be sure end square is redrawn */
8850     damage[0][boardY][boardX] = True;
8851
8852     /* This prevents weird things happening with fast successive
8853        clicks which on my Sun at least can cause motion events
8854        without corresponding press/release. */
8855     player.dragActive = False;
8856 }
8857
8858 /* Handle expose event while piece being dragged */
8859
8860 static void
8861 DrawDragPiece ()
8862 {
8863   if (!player.dragActive || appData.blindfold)
8864     return;
8865
8866   /* What we're doing: logically, the move hasn't been made yet,
8867      so the piece is still in it's original square. But visually
8868      it's being dragged around the board. So we erase the square
8869      that the piece is on and draw it at the last known drag point. */
8870   BlankSquare(player.startSquare.x, player.startSquare.y,
8871                 player.startColor, EmptySquare, xBoardWindow);
8872   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8873   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8874 }
8875
8876 #include <sys/ioctl.h>
8877 int get_term_width()
8878 {
8879     int fd, default_width;
8880
8881     fd = STDIN_FILENO;
8882     default_width = 79; // this is FICS default anyway...
8883
8884 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8885     struct ttysize win;
8886     if (!ioctl(fd, TIOCGSIZE, &win))
8887         default_width = win.ts_cols;
8888 #elif defined(TIOCGWINSZ)
8889     struct winsize win;
8890     if (!ioctl(fd, TIOCGWINSZ, &win))
8891         default_width = win.ws_col;
8892 #endif
8893     return default_width;
8894 }
8895
8896 void update_ics_width()
8897 {
8898     static int old_width = 0;
8899     int new_width = get_term_width();
8900
8901     if (old_width != new_width)
8902        ics_printf("set width %d\n", new_width);
8903     old_width = new_width;
8904 }
8905
8906 void NotifyFrontendLogin()
8907 {
8908     update_ics_width();
8909 }