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