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