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