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