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