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