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