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