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