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