Fix linegap option in board dialog
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #define HIGHDRAG 1
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <pwd.h>
63 #include <math.h>
64
65 #if !OMIT_SOCKETS
66 # if HAVE_SYS_SOCKET_H
67 #  include <sys/socket.h>
68 #  include <netinet/in.h>
69 #  include <netdb.h>
70 # else /* not HAVE_SYS_SOCKET_H */
71 #  if HAVE_LAN_SOCKET_H
72 #   include <lan/socket.h>
73 #   include <lan/in.h>
74 #   include <lan/netdb.h>
75 #  else /* not HAVE_LAN_SOCKET_H */
76 #   define OMIT_SOCKETS 1
77 #  endif /* not HAVE_LAN_SOCKET_H */
78 # endif /* not HAVE_SYS_SOCKET_H */
79 #endif /* !OMIT_SOCKETS */
80
81 #if STDC_HEADERS
82 # include <stdlib.h>
83 # include <string.h>
84 #else /* not STDC_HEADERS */
85 extern char *getenv();
86 # if HAVE_STRING_H
87 #  include <string.h>
88 # else /* not HAVE_STRING_H */
89 #  include <strings.h>
90 # endif /* not HAVE_STRING_H */
91 #endif /* not STDC_HEADERS */
92
93 #if HAVE_SYS_FCNTL_H
94 # include <sys/fcntl.h>
95 #else /* not HAVE_SYS_FCNTL_H */
96 # if HAVE_FCNTL_H
97 #  include <fcntl.h>
98 # endif /* HAVE_FCNTL_H */
99 #endif /* not HAVE_SYS_FCNTL_H */
100
101 #if HAVE_SYS_SYSTEMINFO_H
102 # include <sys/systeminfo.h>
103 #endif /* HAVE_SYS_SYSTEMINFO_H */
104
105 #if TIME_WITH_SYS_TIME
106 # include <sys/time.h>
107 # include <time.h>
108 #else
109 # if HAVE_SYS_TIME_H
110 #  include <sys/time.h>
111 # else
112 #  include <time.h>
113 # endif
114 #endif
115
116 #if HAVE_UNISTD_H
117 # include <unistd.h>
118 #endif
119
120 #if HAVE_SYS_WAIT_H
121 # include <sys/wait.h>
122 #endif
123
124 #if HAVE_DIRENT_H
125 # include <dirent.h>
126 # define NAMLEN(dirent) strlen((dirent)->d_name)
127 # define HAVE_DIR_STRUCT
128 #else
129 # define dirent direct
130 # define NAMLEN(dirent) (dirent)->d_namlen
131 # if HAVE_SYS_NDIR_H
132 #  include <sys/ndir.h>
133 #  define HAVE_DIR_STRUCT
134 # endif
135 # if HAVE_SYS_DIR_H
136 #  include <sys/dir.h>
137 #  define HAVE_DIR_STRUCT
138 # endif
139 # if HAVE_NDIR_H
140 #  include <ndir.h>
141 #  define HAVE_DIR_STRUCT
142 # endif
143 #endif
144
145 #include <X11/Intrinsic.h>
146 #include <X11/StringDefs.h>
147 #include <X11/Shell.h>
148 #include <X11/cursorfont.h>
149 #include <X11/Xatom.h>
150 #include <X11/Xmu/Atoms.h>
151 #if USE_XAW3D
152 #include <X11/Xaw3d/Dialog.h>
153 #include <X11/Xaw3d/Form.h>
154 #include <X11/Xaw3d/List.h>
155 #include <X11/Xaw3d/Label.h>
156 #include <X11/Xaw3d/SimpleMenu.h>
157 #include <X11/Xaw3d/SmeBSB.h>
158 #include <X11/Xaw3d/SmeLine.h>
159 #include <X11/Xaw3d/Box.h>
160 #include <X11/Xaw3d/MenuButton.h>
161 #include <X11/Xaw3d/Text.h>
162 #include <X11/Xaw3d/AsciiText.h>
163 #else
164 #include <X11/Xaw/Dialog.h>
165 #include <X11/Xaw/Form.h>
166 #include <X11/Xaw/List.h>
167 #include <X11/Xaw/Label.h>
168 #include <X11/Xaw/SimpleMenu.h>
169 #include <X11/Xaw/SmeBSB.h>
170 #include <X11/Xaw/SmeLine.h>
171 #include <X11/Xaw/Box.h>
172 #include <X11/Xaw/MenuButton.h>
173 #include <X11/Xaw/Text.h>
174 #include <X11/Xaw/AsciiText.h>
175 #endif
176
177 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
178 #include "common.h"
179
180 #if HAVE_LIBXPM
181 #include <X11/xpm.h>
182 #include "pixmaps/pixmaps.h"
183 #define IMAGE_EXT "xpm"
184 #else
185 #define IMAGE_EXT "xim"
186 #include "bitmaps/bitmaps.h"
187 #endif
188
189 #include "bitmaps/icon_white.bm"
190 #include "bitmaps/icon_black.bm"
191 #include "bitmaps/checkmark.bm"
192
193 #include "frontend.h"
194 #include "backend.h"
195 #include "backendz.h"
196 #include "moves.h"
197 #include "xboard.h"
198 #include "childio.h"
199 #include "xgamelist.h"
200 #include "xhistory.h"
201 #include "xedittags.h"
202 #include "gettext.h"
203
204 // must be moved to xengineoutput.h
205
206 void EngineOutputProc P((Widget w, XEvent *event,
207                          String *prms, Cardinal *nprms));
208 void EvalGraphProc P((Widget w, XEvent *event,
209                       String *prms, Cardinal *nprms));
210
211
212 #ifdef __EMX__
213 #ifndef HAVE_USLEEP
214 #define HAVE_USLEEP
215 #endif
216 #define usleep(t)   _sleep2(((t)+500)/1000)
217 #endif
218
219 #ifdef ENABLE_NLS
220 # define  _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
222 #else
223 # define  _(s) (s)
224 # define N_(s)  s
225 #endif
226
227 typedef struct {
228     String string;
229     String ref;
230     XtActionProc proc;
231 } MenuItem;
232
233 typedef struct {
234     String name;
235     String ref;
236     MenuItem *mi;
237 } Menu;
238
239 int main P((int argc, char **argv));
240 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
241                 char *init_path, char *mode, int (*show_entry)(), char **name_return));
242 RETSIGTYPE CmailSigHandler P((int sig));
243 RETSIGTYPE IntSigHandler P((int sig));
244 RETSIGTYPE TermSizeSigHandler P((int sig));
245 void CreateGCs P((int redo));
246 void CreateXIMPieces P((void));
247 void CreateXPMPieces P((void));
248 void CreateXPMBoard P((char *s, int n));
249 void CreatePieces P((void));
250 void CreatePieceMenus P((void));
251 Widget CreateMenuBar P((Menu *mb));
252 Widget CreateButtonBar P ((MenuItem *mi));
253 char *FindFont P((char *pattern, int targetPxlSize));
254 void PieceMenuPopup P((Widget w, XEvent *event,
255                        String *params, Cardinal *num_params));
256 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
257 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
258 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
259                    u_int wreq, u_int hreq));
260 void CreateGrid P((void));
261 int EventToSquare P((int x, int limit));
262 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
263 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
264 void HandleUserMove P((Widget w, XEvent *event,
265                      String *prms, Cardinal *nprms));
266 void AnimateUserMove P((Widget w, XEvent * event,
267                      String * params, Cardinal * nParams));
268 void HandlePV P((Widget w, XEvent * event,
269                      String * params, Cardinal * nParams));
270 void SelectPV P((Widget w, XEvent * event,
271                      String * params, Cardinal * nParams));
272 void StopPV P((Widget w, XEvent * event,
273                      String * params, Cardinal * nParams));
274 void WhiteClock P((Widget w, XEvent *event,
275                    String *prms, Cardinal *nprms));
276 void BlackClock P((Widget w, XEvent *event,
277                    String *prms, Cardinal *nprms));
278 void DrawPositionProc P((Widget w, XEvent *event,
279                      String *prms, Cardinal *nprms));
280 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
281                      Board board));
282 void CommentClick P((Widget w, XEvent * event,
283                    String * params, Cardinal * nParams));
284 void CommentPopUp P((char *title, char *label));
285 void CommentPopDown P((void));
286 void CommentCallback P((Widget w, XtPointer client_data,
287                         XtPointer call_data));
288 void ICSInputBoxPopUp P((void));
289 void ICSInputBoxPopDown P((void));
290 void FileNamePopUp P((char *label, char *def,
291                       FileProc proc, char *openMode));
292 void FileNamePopDown P((void));
293 void FileNameCallback P((Widget w, XtPointer client_data,
294                          XtPointer call_data));
295 void FileNameAction P((Widget w, XEvent *event,
296                        String *prms, Cardinal *nprms));
297 void AskQuestionReplyAction P((Widget w, XEvent *event,
298                           String *prms, Cardinal *nprms));
299 void AskQuestionProc P((Widget w, XEvent *event,
300                           String *prms, Cardinal *nprms));
301 void AskQuestionPopDown P((void));
302 void PromotionPopDown P((void));
303 void PromotionCallback P((Widget w, XtPointer client_data,
304                           XtPointer call_data));
305 void EditCommentPopDown P((void));
306 void EditCommentCallback P((Widget w, XtPointer client_data,
307                             XtPointer call_data));
308 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
309 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
310 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
311 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
312                          Cardinal *nprms));
313 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
314                          Cardinal *nprms));
315 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
316                        Cardinal *nprms));
317 void LoadPositionProc P((Widget w, XEvent *event,
318                          String *prms, Cardinal *nprms));
319 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
320                          Cardinal *nprms));
321 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
322                          Cardinal *nprms));
323 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
324                        Cardinal *nprms));
325 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
326                          Cardinal *nprms));
327 void PastePositionProc P((Widget w, XEvent *event, String *prms,
328                           Cardinal *nprms));
329 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
330 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
331 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
332 void SavePositionProc P((Widget w, XEvent *event,
333                          String *prms, Cardinal *nprms));
334 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
336                             Cardinal *nprms));
337 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
340                          Cardinal *nprms));
341 void MachineWhiteProc P((Widget w, XEvent *event,
342                          String *prms, Cardinal *nprms));
343 void AnalyzeModeProc P((Widget w, XEvent *event,
344                          String *prms, Cardinal *nprms));
345 void AnalyzeFileProc P((Widget w, XEvent *event,
346                          String *prms, Cardinal *nprms));
347 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
348                         Cardinal *nprms));
349 void 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 #if HAVE_LIBXPM
3445 void CreateXPMBoard(char *s, int kind)
3446 {
3447     XpmAttributes attr;
3448     attr.valuemask = 0;
3449     if(s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
3450     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3451         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3452     }
3453 }
3454
3455 void FreeXPMPieces()
3456 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
3457     // thisroutine has to be called t free the old piece pixmaps
3458     int piece, kind;
3459     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
3460         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
3461     if(useImageSqs) {
3462         XFreePixmap(xDisplay, xpmLightSquare);
3463         XFreePixmap(xDisplay, xpmDarkSquare);
3464     }
3465 }
3466
3467 void CreateXPMPieces()
3468 {
3469     int piece, kind, r;
3470     char buf[MSG_SIZ];
3471     u_int ss = squareSize;
3472     XpmAttributes attr;
3473     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3474     XpmColorSymbol symbols[4];
3475     static int redo = False;
3476
3477     if(redo) FreeXPMPieces(); else redo = 1;
3478
3479     /* The XSynchronize calls were copied from CreatePieces.
3480        Not sure if needed, but can't hurt */
3481     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3482
3483     /* Setup translations so piece colors match square colors */
3484     symbols[0].name = "light_piece";
3485     symbols[0].value = appData.whitePieceColor;
3486     symbols[1].name = "dark_piece";
3487     symbols[1].value = appData.blackPieceColor;
3488     symbols[2].name = "light_square";
3489     symbols[2].value = appData.lightSquareColor;
3490     symbols[3].name = "dark_square";
3491     symbols[3].value = appData.darkSquareColor;
3492
3493     attr.valuemask = XpmColorSymbols;
3494     attr.colorsymbols = symbols;
3495     attr.numsymbols = 4;
3496
3497     if (appData.monoMode) {
3498       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3499                         0, 2);
3500       ExitEvent(2);
3501     }
3502     if (strlen(appData.pixmapDirectory) == 0) {
3503         XpmPieces* pieces = builtInXpms;
3504         useImages = 1;
3505         /* Load pieces */
3506         while (pieces->size != squareSize && pieces->size) pieces++;
3507         if (!pieces->size) {
3508           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3509           exit(1);
3510         }
3511         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3512             for (kind=0; kind<4; kind++) {
3513
3514                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3515                                                pieces->xpm[piece][kind],
3516                                                &(xpmPieceBitmap2[kind][piece]),
3517                                                NULL, &attr)) != 0) {
3518                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3519                           r, buf);
3520                   exit(1);
3521                 }
3522                 if(piece <= (int) WhiteKing)
3523                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3524             }
3525         }
3526         useImageSqs = 0;
3527         xpmJailSquare = xpmLightSquare;
3528     } else {
3529         useImages = 1;
3530
3531         fprintf(stderr, _("\nLoading XPMs...\n"));
3532
3533         /* Load pieces */
3534         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3535             fprintf(stderr, "%d ", piece+1);
3536             for (kind=0; kind<4; kind++) {
3537               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3538                         ExpandPathName(appData.pixmapDirectory),
3539                         piece > (int) WhiteKing ? "w" : "",
3540                         pieceBitmapNames[piece],
3541                         xpmkind[kind], ss);
3542                 if (appData.debugMode) {
3543                     fprintf(stderr, _("(File:%s:) "), buf);
3544                 }
3545                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3546                                            &(xpmPieceBitmap2[kind][piece]),
3547                                            NULL, &attr)) != 0) {
3548                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3549                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3550                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3551                                 ExpandPathName(appData.pixmapDirectory),
3552                                 xpmkind[kind], ss);
3553                         if (appData.debugMode) {
3554                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3555                         }
3556                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3557                                                 &(xpmPieceBitmap2[kind][piece]),
3558                                                 NULL, &attr);
3559                     }
3560                     if (r != 0) {
3561                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3562                                 r, buf);
3563                         exit(1);
3564                     }
3565                 }
3566                 if(piece <= (int) WhiteKing)
3567                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3568             }
3569         }
3570         /* Load light and dark squares */
3571         /* If the LSQ and DSQ pieces don't exist, we will
3572            draw them with solid squares. */
3573         fprintf(stderr, _("light square "));
3574         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3575         if (access(buf, 0) != 0) {
3576             useImageSqs = 0;
3577         } else {
3578             useImageSqs = 1;
3579             if (appData.debugMode)
3580               fprintf(stderr, _("(File:%s:) "), buf);
3581
3582             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3583                                        &xpmLightSquare, NULL, &attr)) != 0) {
3584                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3585                 exit(1);
3586             }
3587             fprintf(stderr, _("dark square "));
3588             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3589                     ExpandPathName(appData.pixmapDirectory), ss);
3590             if (appData.debugMode) {
3591                 fprintf(stderr, _("(File:%s:) "), buf);
3592             }
3593             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3594                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3595                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3596                 exit(1);
3597             }
3598         }
3599         xpmJailSquare = xpmLightSquare;
3600         fprintf(stderr, _("Done.\n"));
3601     }
3602     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3603                                       buffering bug */
3604 }
3605 #endif /* HAVE_LIBXPM */
3606
3607 #if HAVE_LIBXPM
3608 /* No built-in bitmaps */
3609 void CreatePieces()
3610 {
3611     int piece, kind;
3612     char buf[MSG_SIZ];
3613     u_int ss = squareSize;
3614
3615     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3616                                      buffering bug */
3617
3618     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3619         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3620           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3621                    pieceBitmapNames[piece],
3622                    ss, kind == SOLID ? 's' : 'o');
3623           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3624           if(piece <= (int)WhiteKing)
3625             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3626         }
3627     }
3628
3629     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3630                                       buffering bug */
3631 }
3632 #else
3633 /* With built-in bitmaps */
3634 void CreatePieces()
3635 {
3636     BuiltInBits* bib = builtInBits;
3637     int piece, kind;
3638     char buf[MSG_SIZ];
3639     u_int ss = squareSize;
3640
3641     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3642                                      buffering bug */
3643
3644     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3645
3646     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3647         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3648           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3649                    pieceBitmapNames[piece],
3650                    ss, kind == SOLID ? 's' : 'o');
3651           ReadBitmap(&pieceBitmap2[kind][piece], buf,
3652                      bib->bits[kind][piece], ss, ss);
3653           if(piece <= (int)WhiteKing)
3654             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3655         }
3656     }
3657
3658     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3659                                       buffering bug */
3660 }
3661 #endif
3662
3663 void ReadBitmap(pm, name, bits, wreq, hreq)
3664      Pixmap *pm;
3665      String name;
3666      unsigned char bits[];
3667      u_int wreq, hreq;
3668 {
3669     int x_hot, y_hot;
3670     u_int w, h;
3671     int errcode;
3672     char msg[MSG_SIZ], fullname[MSG_SIZ];
3673
3674     if (*appData.bitmapDirectory != NULLCHAR) {
3675       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3676       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3677       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3678       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3679                                 &w, &h, pm, &x_hot, &y_hot);
3680       fprintf(stderr, "load %s\n", name);
3681         if (errcode != BitmapSuccess) {
3682             switch (errcode) {
3683               case BitmapOpenFailed:
3684                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3685                 break;
3686               case BitmapFileInvalid:
3687                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3688                 break;
3689               case BitmapNoMemory:
3690                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3691                         fullname);
3692                 break;
3693               default:
3694                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3695                         errcode, fullname);
3696                 break;
3697             }
3698             fprintf(stderr, _("%s: %s...using built-in\n"),
3699                     programName, msg);
3700         } else if (w != wreq || h != hreq) {
3701             fprintf(stderr,
3702                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3703                     programName, fullname, w, h, wreq, hreq);
3704         } else {
3705             return;
3706         }
3707     }
3708     if (bits != NULL) {
3709         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3710                                     wreq, hreq);
3711     }
3712 }
3713
3714 void CreateGrid()
3715 {
3716     int i, j;
3717
3718     if (lineGap == 0) return;
3719
3720     /* [HR] Split this into 2 loops for non-square boards. */
3721
3722     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3723         gridSegments[i].x1 = 0;
3724         gridSegments[i].x2 =
3725           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3726         gridSegments[i].y1 = gridSegments[i].y2
3727           = lineGap / 2 + (i * (squareSize + lineGap));
3728     }
3729
3730     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3731         gridSegments[j + i].y1 = 0;
3732         gridSegments[j + i].y2 =
3733           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3734         gridSegments[j + i].x1 = gridSegments[j + i].x2
3735           = lineGap / 2 + (j * (squareSize + lineGap));
3736     }
3737 }
3738
3739 static void MenuBarSelect(w, addr, index)
3740      Widget w;
3741      caddr_t addr;
3742      caddr_t index;
3743 {
3744     XtActionProc proc = (XtActionProc) addr;
3745
3746     (proc)(NULL, NULL, NULL, NULL);
3747 }
3748
3749 void CreateMenuBarPopup(parent, name, mb)
3750      Widget parent;
3751      String name;
3752      Menu *mb;
3753 {
3754     int j;
3755     Widget menu, entry;
3756     MenuItem *mi;
3757     Arg args[16];
3758
3759     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3760                               parent, NULL, 0);
3761     j = 0;
3762     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3763     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3764     mi = mb->mi;
3765     while (mi->string != NULL) {
3766         if (strcmp(mi->string, "----") == 0) {
3767             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3768                                           menu, args, j);
3769         } else {
3770           XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
3771             entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
3772                                           menu, args, j+1);
3773             XtAddCallback(entry, XtNcallback,
3774                           (XtCallbackProc) MenuBarSelect,
3775                           (caddr_t) mi->proc);
3776         }
3777         mi++;
3778     }
3779 }
3780
3781 Widget CreateMenuBar(mb)
3782      Menu *mb;
3783 {
3784     int j;
3785     Widget anchor, menuBar;
3786     Arg args[16];
3787     char menuName[MSG_SIZ];
3788
3789     j = 0;
3790     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3791     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3792     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3793     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3794                              formWidget, args, j);
3795
3796     while (mb->name != NULL) {
3797         safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3798         strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
3799         j = 0;
3800         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3801         if (tinyLayout) {
3802             char shortName[2];
3803             shortName[0] = mb->name[0];
3804             shortName[1] = NULLCHAR;
3805             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3806         }
3807       else {
3808           XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
3809       }
3810
3811         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3812         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3813                                        menuBar, args, j);
3814         CreateMenuBarPopup(menuBar, menuName, mb);
3815         mb++;
3816     }
3817     return menuBar;
3818 }
3819
3820 Widget CreateButtonBar(mi)
3821      MenuItem *mi;
3822 {
3823     int j;
3824     Widget button, buttonBar;
3825     Arg args[16];
3826
3827     j = 0;
3828     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3829     if (tinyLayout) {
3830         XtSetArg(args[j], XtNhSpace, 0); j++;
3831     }
3832     XtSetArg(args[j], XtNborderWidth, 0); j++;
3833     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3834     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3835                                formWidget, args, j);
3836
3837     while (mi->string != NULL) {
3838         j = 0;
3839         if (tinyLayout) {
3840             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3841             XtSetArg(args[j], XtNborderWidth, 0); j++;
3842         }
3843       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3844         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3845                                        buttonBar, args, j);
3846         XtAddCallback(button, XtNcallback,
3847                       (XtCallbackProc) MenuBarSelect,
3848                       (caddr_t) mi->proc);
3849         mi++;
3850     }
3851     return buttonBar;
3852 }
3853
3854 Widget
3855 CreatePieceMenu(name, color)
3856      char *name;
3857      int color;
3858 {
3859     int i;
3860     Widget entry, menu;
3861     Arg args[16];
3862     ChessSquare selection;
3863
3864     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3865                               boardWidget, args, 0);
3866
3867     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3868         String item = pieceMenuStrings[color][i];
3869
3870         if (strcmp(item, "----") == 0) {
3871             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3872                                           menu, NULL, 0);
3873         } else {
3874           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3875             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3876                                 menu, args, 1);
3877             selection = pieceMenuTranslation[color][i];
3878             XtAddCallback(entry, XtNcallback,
3879                           (XtCallbackProc) PieceMenuSelect,
3880                           (caddr_t) selection);
3881             if (selection == WhitePawn || selection == BlackPawn) {
3882                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3883                 XtSetValues(menu, args, 1);
3884             }
3885         }
3886     }
3887     return menu;
3888 }
3889
3890 void
3891 CreatePieceMenus()
3892 {
3893     int i;
3894     Widget entry;
3895     Arg args[16];
3896     ChessSquare selection;
3897
3898     whitePieceMenu = CreatePieceMenu("menuW", 0);
3899     blackPieceMenu = CreatePieceMenu("menuB", 1);
3900
3901     XtRegisterGrabAction(PieceMenuPopup, True,
3902                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3903                          GrabModeAsync, GrabModeAsync);
3904
3905     XtSetArg(args[0], XtNlabel, _("Drop"));
3906     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3907                                   boardWidget, args, 1);
3908     for (i = 0; i < DROP_MENU_SIZE; i++) {
3909         String item = dropMenuStrings[i];
3910
3911         if (strcmp(item, "----") == 0) {
3912             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3913                                           dropMenu, NULL, 0);
3914         } else {
3915           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3916             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3917                                 dropMenu, args, 1);
3918             selection = dropMenuTranslation[i];
3919             XtAddCallback(entry, XtNcallback,
3920                           (XtCallbackProc) DropMenuSelect,
3921                           (caddr_t) selection);
3922         }
3923     }
3924 }
3925
3926 void SetupDropMenu()
3927 {
3928     int i, j, count;
3929     char label[32];
3930     Arg args[16];
3931     Widget entry;
3932     char* p;
3933
3934     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3935         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3936         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3937                    dmEnables[i].piece);
3938         XtSetSensitive(entry, p != NULL || !appData.testLegality
3939                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3940                                        && !appData.icsActive));
3941         count = 0;
3942         while (p && *p++ == dmEnables[i].piece) count++;
3943         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3944         j = 0;
3945         XtSetArg(args[j], XtNlabel, label); j++;
3946         XtSetValues(entry, args, j);
3947     }
3948 }
3949
3950 void PieceMenuPopup(w, event, params, num_params)
3951      Widget w;
3952      XEvent *event;
3953      String *params;
3954      Cardinal *num_params;
3955 {
3956     String whichMenu; int menuNr;
3957     if (event->type == ButtonRelease)
3958         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3959     else if (event->type == ButtonPress)
3960         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3961     switch(menuNr) {
3962       case 0: whichMenu = params[0]; break;
3963       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3964       case 2:
3965       case -1: if (errorUp) ErrorPopDown();
3966       default: return;
3967     }
3968     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3969 }
3970
3971 static void PieceMenuSelect(w, piece, junk)
3972      Widget w;
3973      ChessSquare piece;
3974      caddr_t junk;
3975 {
3976     if (pmFromX < 0 || pmFromY < 0) return;
3977     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3978 }
3979
3980 static void DropMenuSelect(w, piece, junk)
3981      Widget w;
3982      ChessSquare piece;
3983      caddr_t junk;
3984 {
3985     if (pmFromX < 0 || pmFromY < 0) return;
3986     DropMenuEvent(piece, pmFromX, pmFromY);
3987 }
3988
3989 void WhiteClock(w, event, prms, nprms)
3990      Widget w;
3991      XEvent *event;
3992      String *prms;
3993      Cardinal *nprms;
3994 {
3995     ClockClick(0);
3996 }
3997
3998 void BlackClock(w, event, prms, nprms)
3999      Widget w;
4000      XEvent *event;
4001      String *prms;
4002      Cardinal *nprms;
4003 {
4004     ClockClick(1);
4005 }
4006
4007
4008 /*
4009  * If the user selects on a border boundary, return -1; if off the board,
4010  *   return -2.  Otherwise map the event coordinate to the square.
4011  */
4012 int EventToSquare(x, limit)
4013      int x;
4014 {
4015     if (x <= 0)
4016       return -2;
4017     if (x < lineGap)
4018       return -1;
4019     x -= lineGap;
4020     if ((x % (squareSize + lineGap)) >= squareSize)
4021       return -1;
4022     x /= (squareSize + lineGap);
4023     if (x >= limit)
4024       return -2;
4025     return x;
4026 }
4027
4028 static void do_flash_delay(msec)
4029      unsigned long msec;
4030 {
4031     TimeDelay(msec);
4032 }
4033
4034 static void drawHighlight(file, rank, gc)
4035      int file, rank;
4036      GC gc;
4037 {
4038     int x, y;
4039
4040     if (lineGap == 0) return;
4041
4042     if (flipView) {
4043         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
4044           (squareSize + lineGap);
4045         y = lineGap/2 + rank * (squareSize + lineGap);
4046     } else {
4047         x = lineGap/2 + file * (squareSize + lineGap);
4048         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4049           (squareSize + lineGap);
4050     }
4051
4052     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4053                    squareSize+lineGap, squareSize+lineGap);
4054 }
4055
4056 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4057 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4058
4059 void
4060 SetHighlights(fromX, fromY, toX, toY)
4061      int fromX, fromY, toX, toY;
4062 {
4063     if (hi1X != fromX || hi1Y != fromY) {
4064         if (hi1X >= 0 && hi1Y >= 0) {
4065             drawHighlight(hi1X, hi1Y, lineGC);
4066         }
4067     } // [HGM] first erase both, then draw new!
4068     if (hi2X != toX || hi2Y != toY) {
4069         if (hi2X >= 0 && hi2Y >= 0) {
4070             drawHighlight(hi2X, hi2Y, lineGC);
4071         }
4072     }
4073     if (hi1X != fromX || hi1Y != fromY) {
4074         if (fromX >= 0 && fromY >= 0) {
4075             drawHighlight(fromX, fromY, highlineGC);
4076         }
4077     }
4078     if (hi2X != toX || hi2Y != toY) {
4079         if (toX >= 0 && toY >= 0) {
4080             drawHighlight(toX, toY, highlineGC);
4081         }
4082     }
4083     hi1X = fromX;
4084     hi1Y = fromY;
4085     hi2X = toX;
4086     hi2Y = toY;
4087 }
4088
4089 void
4090 ClearHighlights()
4091 {
4092     SetHighlights(-1, -1, -1, -1);
4093 }
4094
4095
4096 void
4097 SetPremoveHighlights(fromX, fromY, toX, toY)
4098      int fromX, fromY, toX, toY;
4099 {
4100     if (pm1X != fromX || pm1Y != fromY) {
4101         if (pm1X >= 0 && pm1Y >= 0) {
4102             drawHighlight(pm1X, pm1Y, lineGC);
4103         }
4104         if (fromX >= 0 && fromY >= 0) {
4105             drawHighlight(fromX, fromY, prelineGC);
4106         }
4107     }
4108     if (pm2X != toX || pm2Y != toY) {
4109         if (pm2X >= 0 && pm2Y >= 0) {
4110             drawHighlight(pm2X, pm2Y, lineGC);
4111         }
4112         if (toX >= 0 && toY >= 0) {
4113             drawHighlight(toX, toY, prelineGC);
4114         }
4115     }
4116     pm1X = fromX;
4117     pm1Y = fromY;
4118     pm2X = toX;
4119     pm2Y = toY;
4120 }
4121
4122 void
4123 ClearPremoveHighlights()
4124 {
4125   SetPremoveHighlights(-1, -1, -1, -1);
4126 }
4127
4128 static int CutOutSquare(x, y, x0, y0, kind)
4129      int x, y, *x0, *y0, kind;
4130 {
4131     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4132     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4133     *x0 = 0; *y0 = 0;
4134     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4135     if(textureW[kind] < W*squareSize)
4136         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4137     else
4138         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4139     if(textureH[kind] < H*squareSize)
4140         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4141     else
4142         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4143     return 1;
4144 }
4145
4146 static void BlankSquare(x, y, color, piece, dest, fac)
4147      int x, y, color, fac;
4148      ChessSquare piece;
4149      Drawable dest;
4150 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4151     int x0, y0;
4152     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4153         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4154                   squareSize, squareSize, x*fac, y*fac);
4155     } else
4156     if (useImages && useImageSqs) {
4157         Pixmap pm;
4158         switch (color) {
4159           case 1: /* light */
4160             pm = xpmLightSquare;
4161             break;
4162           case 0: /* dark */
4163             pm = xpmDarkSquare;
4164             break;
4165           case 2: /* neutral */
4166           default:
4167             pm = xpmJailSquare;
4168             break;
4169         }
4170         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4171                   squareSize, squareSize, x*fac, y*fac);
4172     } else {
4173         GC gc;
4174         switch (color) {
4175           case 1: /* light */
4176             gc = lightSquareGC;
4177             break;
4178           case 0: /* dark */
4179             gc = darkSquareGC;
4180             break;
4181           case 2: /* neutral */
4182           default:
4183             gc = jailSquareGC;
4184             break;
4185         }
4186         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4187     }
4188 }
4189
4190 /*
4191    I split out the routines to draw a piece so that I could
4192    make a generic flash routine.
4193 */
4194 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4195      ChessSquare piece;
4196      int square_color, x, y;
4197      Drawable dest;
4198 {
4199     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4200     switch (square_color) {
4201       case 1: /* light */
4202       case 2: /* neutral */
4203       default:
4204         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4205                   ? *pieceToOutline(piece)
4206                   : *pieceToSolid(piece),
4207                   dest, bwPieceGC, 0, 0,
4208                   squareSize, squareSize, x, y);
4209         break;
4210       case 0: /* dark */
4211         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4212                   ? *pieceToSolid(piece)
4213                   : *pieceToOutline(piece),
4214                   dest, wbPieceGC, 0, 0,
4215                   squareSize, squareSize, x, y);
4216         break;
4217     }
4218 }
4219
4220 static void monoDrawPiece(piece, square_color, x, y, dest)
4221      ChessSquare piece;
4222      int square_color, x, y;
4223      Drawable dest;
4224 {
4225     switch (square_color) {
4226       case 1: /* light */
4227       case 2: /* neutral */
4228       default:
4229         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4230                    ? *pieceToOutline(piece)
4231                    : *pieceToSolid(piece),
4232                    dest, bwPieceGC, 0, 0,
4233                    squareSize, squareSize, x, y, 1);
4234         break;
4235       case 0: /* dark */
4236         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4237                    ? *pieceToSolid(piece)
4238                    : *pieceToOutline(piece),
4239                    dest, wbPieceGC, 0, 0,
4240                    squareSize, squareSize, x, y, 1);
4241         break;
4242     }
4243 }
4244
4245 static void colorDrawPiece(piece, square_color, x, y, dest)
4246      ChessSquare piece;
4247      int square_color, x, y;
4248      Drawable dest;
4249 {
4250     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4251     switch (square_color) {
4252       case 1: /* light */
4253         XCopyPlane(xDisplay, *pieceToSolid(piece),
4254                    dest, (int) piece < (int) BlackPawn
4255                    ? wlPieceGC : blPieceGC, 0, 0,
4256                    squareSize, squareSize, x, y, 1);
4257         break;
4258       case 0: /* dark */
4259         XCopyPlane(xDisplay, *pieceToSolid(piece),
4260                    dest, (int) piece < (int) BlackPawn
4261                    ? wdPieceGC : bdPieceGC, 0, 0,
4262                    squareSize, squareSize, x, y, 1);
4263         break;
4264       case 2: /* neutral */
4265       default:
4266         XCopyPlane(xDisplay, *pieceToSolid(piece),
4267                    dest, (int) piece < (int) BlackPawn
4268                    ? wjPieceGC : bjPieceGC, 0, 0,
4269                    squareSize, squareSize, x, y, 1);
4270         break;
4271     }
4272 }
4273
4274 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4275      ChessSquare piece;
4276      int square_color, x, y;
4277      Drawable dest;
4278 {
4279     int kind, p = piece;
4280
4281     switch (square_color) {
4282       case 1: /* light */
4283       case 2: /* neutral */
4284       default:
4285         if ((int)piece < (int) BlackPawn) {
4286             kind = 0;
4287         } else {
4288             kind = 2;
4289             piece -= BlackPawn;
4290         }
4291         break;
4292       case 0: /* dark */
4293         if ((int)piece < (int) BlackPawn) {
4294             kind = 1;
4295         } else {
4296             kind = 3;
4297             piece -= BlackPawn;
4298         }
4299         break;
4300     }
4301     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
4302     if(useTexture & square_color+1) {
4303         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4304         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4305         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4306         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4307         XSetClipMask(xDisplay, wlPieceGC, None);
4308         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4309     } else
4310     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4311               dest, wlPieceGC, 0, 0,
4312               squareSize, squareSize, x, y);
4313 }
4314
4315 typedef void (*DrawFunc)();
4316
4317 DrawFunc ChooseDrawFunc()
4318 {
4319     if (appData.monoMode) {
4320         if (DefaultDepth(xDisplay, xScreen) == 1) {
4321             return monoDrawPiece_1bit;
4322         } else {
4323             return monoDrawPiece;
4324         }
4325     } else {
4326         if (useImages)
4327           return colorDrawPieceImage;
4328         else
4329           return colorDrawPiece;
4330     }
4331 }
4332
4333 /* [HR] determine square color depending on chess variant. */
4334 static int SquareColor(row, column)
4335      int row, column;
4336 {
4337     int square_color;
4338
4339     if (gameInfo.variant == VariantXiangqi) {
4340         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4341             square_color = 1;
4342         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4343             square_color = 0;
4344         } else if (row <= 4) {
4345             square_color = 0;
4346         } else {
4347             square_color = 1;
4348         }
4349     } else {
4350         square_color = ((column + row) % 2) == 1;
4351     }
4352
4353     /* [hgm] holdings: next line makes all holdings squares light */
4354     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4355
4356     return square_color;
4357 }
4358
4359 void DrawSquare(row, column, piece, do_flash)
4360      int row, column, do_flash;
4361      ChessSquare piece;
4362 {
4363     int square_color, x, y, direction, font_ascent, font_descent;
4364     int i;
4365     char string[2];
4366     XCharStruct overall;
4367     DrawFunc drawfunc;
4368     int flash_delay;
4369
4370     /* Calculate delay in milliseconds (2-delays per complete flash) */
4371     flash_delay = 500 / appData.flashRate;
4372
4373     if (flipView) {
4374         x = lineGap + ((BOARD_WIDTH-1)-column) *
4375           (squareSize + lineGap);
4376         y = lineGap + row * (squareSize + lineGap);
4377     } else {
4378         x = lineGap + column * (squareSize + lineGap);
4379         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4380           (squareSize + lineGap);
4381     }
4382
4383     if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4384
4385     square_color = SquareColor(row, column);
4386
4387     if ( // [HGM] holdings: blank out area between board and holdings
4388                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4389               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4390                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4391                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
4392
4393                         // [HGM] print piece counts next to holdings
4394                         string[1] = NULLCHAR;
4395                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4396                             string[0] = '0' + piece;
4397                             XTextExtents(countFontStruct, string, 1, &direction,
4398                                  &font_ascent, &font_descent, &overall);
4399                             if (appData.monoMode) {
4400                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4401                                                  x + squareSize - overall.width - 2,
4402                                                  y + font_ascent + 1, string, 1);
4403                             } else {
4404                                 XDrawString(xDisplay, xBoardWindow, countGC,
4405                                             x + squareSize - overall.width - 2,
4406                                             y + font_ascent + 1, string, 1);
4407                             }
4408                         }
4409                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4410                             string[0] = '0' + piece;
4411                             XTextExtents(countFontStruct, string, 1, &direction,
4412                                          &font_ascent, &font_descent, &overall);
4413                             if (appData.monoMode) {
4414                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4415                                                  x + 2, y + font_ascent + 1, string, 1);
4416                             } else {
4417                                 XDrawString(xDisplay, xBoardWindow, countGC,
4418                                             x + 2, y + font_ascent + 1, string, 1);
4419                             }
4420                         }
4421     } else {
4422             if (piece == EmptySquare || appData.blindfold) {
4423                         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4424             } else {
4425                         drawfunc = ChooseDrawFunc();
4426
4427                         if (do_flash && appData.flashCount > 0) {
4428                             for (i=0; i<appData.flashCount; ++i) {
4429                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4430                                         XSync(xDisplay, False);
4431                                         do_flash_delay(flash_delay);
4432
4433                                         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
4434                                         XSync(xDisplay, False);
4435                                         do_flash_delay(flash_delay);
4436                             }
4437                         }
4438                         drawfunc(piece, square_color, x, y, xBoardWindow);
4439         }
4440         }
4441
4442     string[1] = NULLCHAR;
4443     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4444                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4445         string[0] = 'a' + column - BOARD_LEFT;
4446         XTextExtents(coordFontStruct, string, 1, &direction,
4447                      &font_ascent, &font_descent, &overall);
4448         if (appData.monoMode) {
4449             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4450                              x + squareSize - overall.width - 2,
4451                              y + squareSize - font_descent - 1, string, 1);
4452         } else {
4453             XDrawString(xDisplay, xBoardWindow, coordGC,
4454                         x + squareSize - overall.width - 2,
4455                         y + squareSize - font_descent - 1, string, 1);
4456         }
4457     }
4458     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4459         string[0] = ONE + row;
4460         XTextExtents(coordFontStruct, string, 1, &direction,
4461                      &font_ascent, &font_descent, &overall);
4462         if (appData.monoMode) {
4463             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4464                              x + 2, y + font_ascent + 1, string, 1);
4465         } else {
4466             XDrawString(xDisplay, xBoardWindow, coordGC,
4467                         x + 2, y + font_ascent + 1, string, 1);
4468         }
4469     }
4470     if(!partnerUp && marker[row][column]) {
4471         XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4472                 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4473     }
4474 }
4475
4476
4477 /* Why is this needed on some versions of X? */
4478 void EventProc(widget, unused, event)
4479      Widget widget;
4480      caddr_t unused;
4481      XEvent *event;
4482 {
4483     if (!XtIsRealized(widget))
4484       return;
4485
4486     switch (event->type) {
4487       case Expose:
4488         if (event->xexpose.count > 0) return;  /* no clipping is done */
4489         XDrawPosition(widget, True, NULL);
4490         if(twoBoards) { // [HGM] dual: draw other board in other orientation
4491             flipView = !flipView; partnerUp = !partnerUp;
4492             XDrawPosition(widget, True, NULL);
4493             flipView = !flipView; partnerUp = !partnerUp;
4494         }
4495         break;
4496       case MotionNotify:
4497         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
4498       default:
4499         return;
4500     }
4501 }
4502 /* end why */
4503
4504 void DrawPosition(fullRedraw, board)
4505      /*Boolean*/int fullRedraw;
4506      Board board;
4507 {
4508     XDrawPosition(boardWidget, fullRedraw, board);
4509 }
4510
4511 /* Returns 1 if there are "too many" differences between b1 and b2
4512    (i.e. more than 1 move was made) */
4513 static int too_many_diffs(b1, b2)
4514      Board b1, b2;
4515 {
4516     int i, j;
4517     int c = 0;
4518
4519     for (i=0; i<BOARD_HEIGHT; ++i) {
4520         for (j=0; j<BOARD_WIDTH; ++j) {
4521             if (b1[i][j] != b2[i][j]) {
4522                 if (++c > 4)    /* Castling causes 4 diffs */
4523                   return 1;
4524             }
4525         }
4526     }
4527     return 0;
4528 }
4529
4530 /* Matrix describing castling maneuvers */
4531 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4532 static int castling_matrix[4][5] = {
4533     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4534     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4535     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4536     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4537 };
4538
4539 /* Checks whether castling occurred. If it did, *rrow and *rcol
4540    are set to the destination (row,col) of the rook that moved.
4541
4542    Returns 1 if castling occurred, 0 if not.
4543
4544    Note: Only handles a max of 1 castling move, so be sure
4545    to call too_many_diffs() first.
4546    */
4547 static int check_castle_draw(newb, oldb, rrow, rcol)
4548      Board newb, oldb;
4549      int *rrow, *rcol;
4550 {
4551     int i, *r, j;
4552     int match;
4553
4554     /* For each type of castling... */
4555     for (i=0; i<4; ++i) {
4556         r = castling_matrix[i];
4557
4558         /* Check the 4 squares involved in the castling move */
4559         match = 0;
4560         for (j=1; j<=4; ++j) {
4561             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4562                 match = 1;
4563                 break;
4564             }
4565         }
4566
4567         if (!match) {
4568             /* All 4 changed, so it must be a castling move */
4569             *rrow = r[0];
4570             *rcol = r[3];
4571             return 1;
4572         }
4573     }
4574     return 0;
4575 }
4576
4577 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4578 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4579 {
4580       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4581 }
4582
4583 void DrawSeekBackground( int left, int top, int right, int bottom )
4584 {
4585     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4586 }
4587
4588 void DrawSeekText(char *buf, int x, int y)
4589 {
4590     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4591 }
4592
4593 void DrawSeekDot(int x, int y, int colorNr)
4594 {
4595     int square = colorNr & 0x80;
4596     GC color;
4597     colorNr &= 0x7F;
4598     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4599     if(square)
4600         XFillRectangle(xDisplay, xBoardWindow, color,
4601                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4602     else
4603         XFillArc(xDisplay, xBoardWindow, color,
4604                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4605 }
4606
4607 static int damage[2][BOARD_RANKS][BOARD_FILES];
4608
4609 /*
4610  * event handler for redrawing the board
4611  */
4612 void XDrawPosition(w, repaint, board)
4613      Widget w;
4614      /*Boolean*/int repaint;
4615      Board board;
4616 {
4617     int i, j, do_flash;
4618     static int lastFlipView = 0;
4619     static int lastBoardValid[2] = {0, 0};
4620     static Board lastBoard[2];
4621     Arg args[16];
4622     int rrow, rcol;
4623     int nr = twoBoards*partnerUp;
4624
4625     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4626
4627     if (board == NULL) {
4628         if (!lastBoardValid[nr]) return;
4629         board = lastBoard[nr];
4630     }
4631     if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4632         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4633         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
4634                     args, 1);
4635     }
4636
4637     /*
4638      * It would be simpler to clear the window with XClearWindow()
4639      * but this causes a very distracting flicker.
4640      */
4641
4642     if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4643
4644         if ( lineGap && IsDrawArrowEnabled())
4645             XDrawSegments(xDisplay, xBoardWindow, lineGC,
4646                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4647
4648         /* If too much changes (begin observing new game, etc.), don't
4649            do flashing */
4650         do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4651
4652         /* Special check for castling so we don't flash both the king
4653            and the rook (just flash the king). */
4654         if (do_flash) {
4655             if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4656                 /* Draw rook with NO flashing. King will be drawn flashing later */
4657                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4658                 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4659             }
4660         }
4661
4662         /* First pass -- Draw (newly) empty squares and repair damage.
4663            This prevents you from having a piece show up twice while it
4664            is flashing on its new square */
4665         for (i = 0; i < BOARD_HEIGHT; i++)
4666           for (j = 0; j < BOARD_WIDTH; j++)
4667             if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4668                 || damage[nr][i][j]) {
4669                 DrawSquare(i, j, board[i][j], 0);
4670                 damage[nr][i][j] = False;
4671             }
4672
4673         /* Second pass -- Draw piece(s) in new position and flash them */
4674         for (i = 0; i < BOARD_HEIGHT; i++)
4675           for (j = 0; j < BOARD_WIDTH; j++)
4676             if (board[i][j] != lastBoard[nr][i][j]) {
4677                 DrawSquare(i, j, board[i][j], do_flash);
4678             }
4679     } else {
4680         if (lineGap > 0)
4681           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4682                         twoBoards & partnerUp ? secondSegments : // [HGM] dual
4683                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4684
4685         for (i = 0; i < BOARD_HEIGHT; i++)
4686           for (j = 0; j < BOARD_WIDTH; j++) {
4687               DrawSquare(i, j, board[i][j], 0);
4688               damage[nr][i][j] = False;
4689           }
4690     }
4691
4692     CopyBoard(lastBoard[nr], board);
4693     lastBoardValid[nr] = 1;
4694   if(nr == 0) { // [HGM] dual: no highlights on second board yet
4695     lastFlipView = flipView;
4696
4697     /* Draw highlights */
4698     if (pm1X >= 0 && pm1Y >= 0) {
4699       drawHighlight(pm1X, pm1Y, prelineGC);
4700     }
4701     if (pm2X >= 0 && pm2Y >= 0) {
4702       drawHighlight(pm2X, pm2Y, prelineGC);
4703     }
4704     if (hi1X >= 0 && hi1Y >= 0) {
4705       drawHighlight(hi1X, hi1Y, highlineGC);
4706     }
4707     if (hi2X >= 0 && hi2Y >= 0) {
4708       drawHighlight(hi2X, hi2Y, highlineGC);
4709     }
4710     DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
4711   }
4712     /* If piece being dragged around board, must redraw that too */
4713     DrawDragPiece();
4714
4715     XSync(xDisplay, False);
4716 }
4717
4718
4719 /*
4720  * event handler for redrawing the board
4721  */
4722 void DrawPositionProc(w, event, prms, nprms)
4723      Widget w;
4724      XEvent *event;
4725      String *prms;
4726      Cardinal *nprms;
4727 {
4728     XDrawPosition(w, True, NULL);
4729 }
4730
4731
4732 /*
4733  * event handler for parsing user moves
4734  */
4735 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4736 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4737 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4738 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
4739 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4740 //       and at the end FinishMove() to perform the move after optional promotion popups.
4741 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4742 void HandleUserMove(w, event, prms, nprms)
4743      Widget w;
4744      XEvent *event;
4745      String *prms;
4746      Cardinal *nprms;
4747 {
4748     if (w != boardWidget || errorExitStatus != -1) return;
4749     if(nprms) shiftKey = !strcmp(prms[0], "1");
4750
4751     if (promotionUp) {
4752         if (event->type == ButtonPress) {
4753             XtPopdown(promotionShell);
4754             XtDestroyWidget(promotionShell);
4755             promotionUp = False;
4756             ClearHighlights();
4757             fromX = fromY = -1;
4758         } else {
4759             return;
4760         }
4761     }
4762
4763     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4764     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
4765     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4766 }
4767
4768 void AnimateUserMove (Widget w, XEvent * event,
4769                       String * params, Cardinal * nParams)
4770 {
4771     DragPieceMove(event->xmotion.x, event->xmotion.y);
4772 }
4773
4774 void HandlePV (Widget w, XEvent * event,
4775                       String * params, Cardinal * nParams)
4776 {   // [HGM] pv: walk PV
4777     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4778 }
4779
4780 Widget CommentCreate(name, text, mutable, callback, lines)
4781      char *name, *text;
4782      int /*Boolean*/ mutable;
4783      XtCallbackProc callback;
4784      int lines;
4785 {
4786     Arg args[16];
4787     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4788     Dimension bw_width;
4789     int j;
4790
4791     j = 0;
4792     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4793     XtGetValues(boardWidget, args, j);
4794
4795     j = 0;
4796     XtSetArg(args[j], XtNresizable, True);  j++;
4797 #if TOPLEVEL
4798     shell =
4799       XtCreatePopupShell(name, topLevelShellWidgetClass,
4800                          shellWidget, args, j);
4801 #else
4802     shell =
4803       XtCreatePopupShell(name, transientShellWidgetClass,
4804                          shellWidget, args, j);
4805 #endif
4806     layout =
4807       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4808                             layoutArgs, XtNumber(layoutArgs));
4809     form =
4810       XtCreateManagedWidget("form", formWidgetClass, layout,
4811                             formArgs, XtNumber(formArgs));
4812
4813     j = 0;
4814     if (mutable) {
4815         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4816         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4817     }
4818     XtSetArg(args[j], XtNstring, text);  j++;
4819     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4820     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4821     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4822     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4823     XtSetArg(args[j], XtNresizable, True);  j++;
4824     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
4825     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4826     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4827     XtSetArg(args[j], XtNautoFill, True);  j++;
4828     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4829     edit =
4830       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4831     XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
4832
4833     if (mutable) {
4834         j = 0;
4835         XtSetArg(args[j], XtNfromVert, edit);  j++;
4836         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4837         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4838         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4839         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4840         b_ok =
4841           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4842         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4843
4844         j = 0;
4845         XtSetArg(args[j], XtNfromVert, edit);  j++;
4846         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4847         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4848         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4849         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4850         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4851         b_cancel =
4852           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4853         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4854
4855         j = 0;
4856         XtSetArg(args[j], XtNfromVert, edit);  j++;
4857         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4858         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4859         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4860         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4861         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4862         b_clear =
4863           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4864         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4865     } else {
4866         j = 0;
4867         XtSetArg(args[j], XtNfromVert, edit);  j++;
4868         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4869         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4870         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4871         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4872         b_close =
4873           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4874         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4875
4876         j = 0;
4877         XtSetArg(args[j], XtNfromVert, edit);  j++;
4878         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4879         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4880         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4881         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4882         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4883         b_edit =
4884           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4885         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4886     }
4887
4888     XtRealizeWidget(shell);
4889
4890     if (commentX == -1) {
4891         int xx, yy;
4892         Window junk;
4893         Dimension pw_height;
4894         Dimension ew_height;
4895
4896         j = 0;
4897         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4898         XtGetValues(edit, args, j);
4899
4900         j = 0;
4901         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4902         XtGetValues(shell, args, j);
4903         commentH = pw_height + (lines - 1) * ew_height;
4904         commentW = bw_width - 16;
4905
4906         XSync(xDisplay, False);
4907 #ifdef NOTDEF
4908         /* This code seems to tickle an X bug if it is executed too soon
4909            after xboard starts up.  The coordinates get transformed as if
4910            the main window was positioned at (0, 0).
4911            */
4912         XtTranslateCoords(shellWidget,
4913                           (bw_width - commentW) / 2, 0 - commentH / 2,
4914                           &commentX, &commentY);
4915 #else  /*!NOTDEF*/
4916         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4917                               RootWindowOfScreen(XtScreen(shellWidget)),
4918                               (bw_width - commentW) / 2, 0 - commentH / 2,
4919                               &xx, &yy, &junk);
4920         commentX = xx;
4921         commentY = yy;
4922 #endif /*!NOTDEF*/
4923         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4924     }
4925
4926     if(wpComment.width > 0) {
4927       commentX = wpComment.x;
4928       commentY = wpComment.y;
4929       commentW = wpComment.width;
4930       commentH = wpComment.height;
4931     }
4932
4933     j = 0;
4934     XtSetArg(args[j], XtNheight, commentH);  j++;
4935     XtSetArg(args[j], XtNwidth, commentW);  j++;
4936     XtSetArg(args[j], XtNx, commentX);  j++;
4937     XtSetArg(args[j], XtNy, commentY);  j++;
4938     XtSetValues(shell, args, j);
4939     XtSetKeyboardFocus(shell, edit);
4940
4941     return shell;
4942 }
4943
4944 static int savedIndex;  /* gross that this is global */
4945
4946 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4947 {
4948         String val;
4949         XawTextPosition index, dummy;
4950         Arg arg;
4951
4952         XawTextGetSelectionPos(w, &index, &dummy);
4953         XtSetArg(arg, XtNstring, &val);
4954         XtGetValues(w, &arg, 1);
4955         ReplaceComment(savedIndex, val);
4956         if(savedIndex != currentMove) ToNrEvent(savedIndex);
4957         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4958 }
4959
4960 void EditCommentPopUp(index, title, text)
4961      int index;
4962      char *title, *text;
4963 {
4964     Widget edit;
4965     Arg args[16];
4966     int j;
4967
4968     savedIndex = index;
4969     if (text == NULL) text = "";
4970
4971     if (editShell == NULL) {
4972         editShell =
4973           CommentCreate(title, text, True, EditCommentCallback, 4);
4974         XtRealizeWidget(editShell);
4975         CatchDeleteWindow(editShell, "EditCommentPopDown");
4976     } else {
4977         edit = XtNameToWidget(editShell, "*form.text");
4978         j = 0;
4979         XtSetArg(args[j], XtNstring, text); j++;
4980         XtSetValues(edit, args, j);
4981         j = 0;
4982         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4983         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4984         XtSetValues(editShell, args, j);
4985     }
4986
4987     XtPopup(editShell, XtGrabNone);
4988
4989     editUp = True;
4990     j = 0;
4991     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4992     XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
4993                 args, j);
4994     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
4995                 args, j);
4996 }
4997
4998 void EditCommentCallback(w, client_data, call_data)
4999      Widget w;
5000      XtPointer client_data, call_data;
5001 {
5002     String name, val;
5003     Arg args[16];
5004     int j;
5005     Widget edit;
5006
5007     j = 0;
5008     XtSetArg(args[j], XtNlabel, &name);  j++;
5009     XtGetValues(w, args, j);
5010
5011     if (strcmp(name, _("ok")) == 0) {
5012         edit = XtNameToWidget(editShell, "*form.text");
5013         j = 0;
5014         XtSetArg(args[j], XtNstring, &val); j++;
5015         XtGetValues(edit, args, j);
5016         ReplaceComment(savedIndex, val);
5017         EditCommentPopDown();
5018     } else if (strcmp(name, _("cancel")) == 0) {
5019         EditCommentPopDown();
5020     } else if (strcmp(name, _("clear")) == 0) {
5021         edit = XtNameToWidget(editShell, "*form.text");
5022         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5023         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5024     }
5025 }
5026
5027 void EditCommentPopDown()
5028 {
5029     Arg args[16];
5030     int j;
5031
5032     if (!editUp) return;
5033     j = 0;
5034     XtSetArg(args[j], XtNx, &commentX); j++;
5035     XtSetArg(args[j], XtNy, &commentY); j++;
5036     XtSetArg(args[j], XtNheight, &commentH); j++;
5037     XtSetArg(args[j], XtNwidth, &commentW); j++;
5038     XtGetValues(editShell, args, j);
5039     XtPopdown(editShell);
5040     editUp = False;
5041     j = 0;
5042     XtSetArg(args[j], XtNleftBitmap, None); j++;
5043     XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
5044                 args, j);
5045     XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
5046                 args, j);
5047 }
5048
5049 void ICSInputBoxPopUp()
5050 {
5051     InputBoxPopup();
5052 }
5053
5054 extern Option boxOptions[];
5055
5056 void ICSInputSendText()
5057 {
5058     Widget edit;
5059     int j;
5060     Arg args[16];
5061     String val;
5062
5063     edit = boxOptions[0].handle;
5064     j = 0;
5065     XtSetArg(args[j], XtNstring, &val); j++;
5066     XtGetValues(edit, args, j);
5067     SaveInHistory(val);
5068     SendMultiLineToICS(val);
5069     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5070     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5071 }
5072
5073 void ICSInputBoxPopDown()
5074 {
5075     PopDown(4);
5076 }
5077
5078 void CommentPopUp(title, text)
5079      char *title, *text;
5080 {
5081     Arg args[16];
5082     int j;
5083     Widget edit;
5084
5085     savedIndex = currentMove; // [HGM] vari
5086     if (commentShell == NULL) {
5087         commentShell =
5088           CommentCreate(title, text, False, CommentCallback, 4);
5089         XtRealizeWidget(commentShell);
5090         CatchDeleteWindow(commentShell, "CommentPopDown");
5091     } else {
5092         edit = XtNameToWidget(commentShell, "*form.text");
5093         j = 0;
5094         XtSetArg(args[j], XtNstring, text); j++;
5095         XtSetValues(edit, args, j);
5096         j = 0;
5097         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5098         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5099         XtSetValues(commentShell, args, j);
5100     }
5101
5102     XtPopup(commentShell, XtGrabNone);
5103     XSync(xDisplay, False);
5104
5105     commentUp = True;
5106 }
5107
5108 void CommentCallback(w, client_data, call_data)
5109      Widget w;
5110      XtPointer client_data, call_data;
5111 {
5112     String name;
5113     Arg args[16];
5114     int j;
5115
5116     j = 0;
5117     XtSetArg(args[j], XtNlabel, &name);  j++;
5118     XtGetValues(w, args, j);
5119
5120     if (strcmp(name, _("close")) == 0) {
5121         CommentPopDown();
5122     } else if (strcmp(name, _("edit")) == 0) {
5123         CommentPopDown();
5124         EditCommentEvent();
5125     }
5126 }
5127
5128
5129 void CommentPopDown()
5130 {
5131     Arg args[16];
5132     int j;
5133
5134     if (!commentUp) return;
5135     j = 0;
5136     XtSetArg(args[j], XtNx, &commentX); j++;
5137     XtSetArg(args[j], XtNy, &commentY); j++;
5138     XtSetArg(args[j], XtNwidth, &commentW); j++;
5139     XtSetArg(args[j], XtNheight, &commentH); j++;
5140     XtGetValues(commentShell, args, j);
5141     XtPopdown(commentShell);
5142     XSync(xDisplay, False);
5143     commentUp = False;
5144 }
5145
5146 void FileNamePopUp(label, def, proc, openMode)
5147      char *label;
5148      char *def;
5149      FileProc proc;
5150      char *openMode;
5151 {
5152     fileProc = proc;            /* I can't see a way not */
5153     fileOpenMode = openMode;    /*   to use globals here */
5154     {   // [HGM] use file-selector dialog stolen from Ghostview
5155         char *name;
5156         int index; // this is not supported yet
5157         FILE *f;
5158         if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5159                            def, openMode, NULL, &name))
5160           (void) (*fileProc)(f, index=0, name);
5161     }
5162 }
5163
5164 void FileNamePopDown()
5165 {
5166     if (!filenameUp) return;
5167     XtPopdown(fileNameShell);
5168     XtDestroyWidget(fileNameShell);
5169     filenameUp = False;
5170     ModeHighlight();
5171 }
5172
5173 void FileNameCallback(w, client_data, call_data)
5174      Widget w;
5175      XtPointer client_data, call_data;
5176 {
5177     String name;
5178     Arg args[16];
5179
5180     XtSetArg(args[0], XtNlabel, &name);
5181     XtGetValues(w, args, 1);
5182
5183     if (strcmp(name, _("cancel")) == 0) {
5184         FileNamePopDown();
5185         return;
5186     }
5187
5188     FileNameAction(w, NULL, NULL, NULL);
5189 }
5190
5191 void FileNameAction(w, event, prms, nprms)
5192      Widget w;
5193      XEvent *event;
5194      String *prms;
5195      Cardinal *nprms;
5196 {
5197     char buf[MSG_SIZ];
5198     String name;
5199     FILE *f;
5200     char *p, *fullname;
5201     int index;
5202
5203     name = XawDialogGetValueString(w = XtParent(w));
5204
5205     if ((name != NULL) && (*name != NULLCHAR)) {
5206         safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5207         XtPopdown(w = XtParent(XtParent(w)));
5208         XtDestroyWidget(w);
5209         filenameUp = False;
5210
5211         p = strrchr(buf, ' ');
5212         if (p == NULL) {
5213             index = 0;
5214         } else {
5215             *p++ = NULLCHAR;
5216             index = atoi(p);
5217         }
5218         fullname = ExpandPathName(buf);
5219         if (!fullname) {
5220             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5221         }
5222         else {
5223             f = fopen(fullname, fileOpenMode);
5224             if (f == NULL) {
5225                 DisplayError(_("Failed to open file"), errno);
5226             } else {
5227                 (void) (*fileProc)(f, index, buf);
5228             }
5229         }
5230         ModeHighlight();
5231         return;
5232     }
5233
5234     XtPopdown(w = XtParent(XtParent(w)));
5235     XtDestroyWidget(w);
5236     filenameUp = False;
5237     ModeHighlight();
5238 }
5239
5240 void PromotionPopUp()
5241 {
5242     Arg args[16];
5243     Widget dialog, layout;
5244     Position x, y;
5245     Dimension bw_width, pw_width;
5246     int j;
5247
5248     j = 0;
5249     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5250     XtGetValues(boardWidget, args, j);
5251
5252     j = 0;
5253     XtSetArg(args[j], XtNresizable, True); j++;
5254     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5255     promotionShell =
5256       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5257                          shellWidget, args, j);
5258     layout =
5259       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5260                             layoutArgs, XtNumber(layoutArgs));
5261
5262     j = 0;
5263     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5264     XtSetArg(args[j], XtNborderWidth, 0); j++;
5265     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5266                                    layout, args, j);
5267
5268   if(gameInfo.variant != VariantShogi) {
5269    if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
5270       XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
5271                          (XtPointer) dialog);
5272       XawDialogAddButton(dialog, _("General"), PromotionCallback,
5273                          (XtPointer) dialog);
5274       XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
5275                          (XtPointer) dialog);
5276       XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
5277                          (XtPointer) dialog);
5278     } else {\r
5279     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5280                        (XtPointer) dialog);
5281     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5282                        (XtPointer) dialog);
5283     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5284                        (XtPointer) dialog);
5285     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5286                        (XtPointer) dialog);
5287     }
5288     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5289         gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
5290         gameInfo.variant == VariantGiveaway) {
5291       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5292                          (XtPointer) dialog);
5293     }
5294     if(gameInfo.variant == VariantCapablanca ||
5295        gameInfo.variant == VariantGothic ||
5296        gameInfo.variant == VariantCapaRandom) {
5297       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5298                          (XtPointer) dialog);
5299       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5300                          (XtPointer) dialog);
5301     }
5302   } else // [HGM] shogi
5303   {
5304       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5305                          (XtPointer) dialog);
5306       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5307                          (XtPointer) dialog);
5308   }
5309     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5310                        (XtPointer) dialog);
5311
5312     XtRealizeWidget(promotionShell);
5313     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5314
5315     j = 0;
5316     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5317     XtGetValues(promotionShell, args, j);
5318
5319     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5320                       lineGap + squareSize/3 +
5321                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5322                        0 : 6*(squareSize + lineGap)), &x, &y);
5323
5324     j = 0;
5325     XtSetArg(args[j], XtNx, x); j++;
5326     XtSetArg(args[j], XtNy, y); j++;
5327     XtSetValues(promotionShell, args, j);
5328
5329     XtPopup(promotionShell, XtGrabNone);
5330
5331     promotionUp = True;
5332 }
5333
5334 void PromotionPopDown()
5335 {
5336     if (!promotionUp) return;
5337     XtPopdown(promotionShell);
5338     XtDestroyWidget(promotionShell);
5339     promotionUp = False;
5340 }
5341
5342 void PromotionCallback(w, client_data, call_data)
5343      Widget w;
5344      XtPointer client_data, call_data;
5345 {
5346     String name;
5347     Arg args[16];
5348     int promoChar;
5349
5350     XtSetArg(args[0], XtNlabel, &name);
5351     XtGetValues(w, args, 1);
5352
5353     PromotionPopDown();
5354
5355     if (fromX == -1) return;
5356
5357     if (strcmp(name, _("cancel")) == 0) {
5358         fromX = fromY = -1;
5359         ClearHighlights();
5360         return;
5361     } else if (strcmp(name, _("Knight")) == 0) {
5362         promoChar = 'n';
5363     } else if (strcmp(name, _("Promote")) == 0) {
5364         promoChar = '+';
5365     } else if (strcmp(name, _("Defer")) == 0) {
5366         promoChar = '=';
5367     } else {
5368         promoChar = ToLower(name[0]);
5369     }
5370
5371     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5372
5373     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5374     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5375     fromX = fromY = -1;
5376 }
5377
5378
5379 void ErrorCallback(w, client_data, call_data)
5380      Widget w;
5381      XtPointer client_data, call_data;
5382 {
5383     errorUp = False;
5384     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5385     XtDestroyWidget(w);
5386     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5387 }
5388
5389
5390 void ErrorPopDown()
5391 {
5392     if (!errorUp) return;
5393     errorUp = False;
5394     XtPopdown(errorShell);
5395     XtDestroyWidget(errorShell);
5396     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5397 }
5398
5399 void ErrorPopUp(title, label, modal)
5400      char *title, *label;
5401      int modal;
5402 {
5403     Arg args[16];
5404     Widget dialog, layout;
5405     Position x, y;
5406     int xx, yy;
5407     Window junk;
5408     Dimension bw_width, pw_width;
5409     Dimension pw_height;
5410     int i;
5411
5412     i = 0;
5413     XtSetArg(args[i], XtNresizable, True);  i++;
5414     XtSetArg(args[i], XtNtitle, title); i++;
5415     errorShell =
5416       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5417                          shellWidget, args, i);
5418     layout =
5419       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5420                             layoutArgs, XtNumber(layoutArgs));
5421
5422     i = 0;
5423     XtSetArg(args[i], XtNlabel, label); i++;
5424     XtSetArg(args[i], XtNborderWidth, 0); i++;
5425     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5426                                    layout, args, i);
5427
5428     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5429
5430     XtRealizeWidget(errorShell);
5431     CatchDeleteWindow(errorShell, "ErrorPopDown");
5432
5433     i = 0;
5434     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5435     XtGetValues(boardWidget, args, i);
5436     i = 0;
5437     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5438     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5439     XtGetValues(errorShell, args, i);
5440
5441 #ifdef NOTDEF
5442     /* This code seems to tickle an X bug if it is executed too soon
5443        after xboard starts up.  The coordinates get transformed as if
5444        the main window was positioned at (0, 0).
5445        */
5446     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5447                       0 - pw_height + squareSize / 3, &x, &y);
5448 #else
5449     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5450                           RootWindowOfScreen(XtScreen(boardWidget)),
5451                           (bw_width - pw_width) / 2,
5452                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5453     x = xx;
5454     y = yy;
5455 #endif
5456     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5457
5458     i = 0;
5459     XtSetArg(args[i], XtNx, x);  i++;
5460     XtSetArg(args[i], XtNy, y);  i++;
5461     XtSetValues(errorShell, args, i);
5462
5463     errorUp = True;
5464     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5465 }
5466
5467 /* Disable all user input other than deleting the window */
5468 static int frozen = 0;
5469 void FreezeUI()
5470 {
5471   if (frozen) return;
5472   /* Grab by a widget that doesn't accept input */
5473   XtAddGrab(messageWidget, TRUE, FALSE);
5474   frozen = 1;
5475 }
5476
5477 /* Undo a FreezeUI */
5478 void ThawUI()
5479 {
5480   if (!frozen) return;
5481   XtRemoveGrab(messageWidget);
5482   frozen = 0;
5483 }
5484
5485 char *ModeToWidgetName(mode)
5486      GameMode mode;
5487 {
5488     switch (mode) {
5489       case BeginningOfGame:
5490         if (appData.icsActive)
5491           return "menuMode.ICS Client";
5492         else if (appData.noChessProgram ||
5493                  *appData.cmailGameName != NULLCHAR)
5494           return "menuMode.Edit Game";
5495         else
5496           return "menuMode.Machine Black";
5497       case MachinePlaysBlack:
5498         return "menuMode.Machine Black";
5499       case MachinePlaysWhite:
5500         return "menuMode.Machine White";
5501       case AnalyzeMode:
5502         return "menuMode.Analysis Mode";
5503       case AnalyzeFile:
5504         return "menuMode.Analyze File";
5505       case TwoMachinesPlay:
5506         return "menuMode.Two Machines";
5507       case EditGame:
5508         return "menuMode.Edit Game";
5509       case PlayFromGameFile:
5510         return "menuFile.Load Game";
5511       case EditPosition:
5512         return "menuMode.Edit Position";
5513       case Training:
5514         return "menuMode.Training";
5515       case IcsPlayingWhite:
5516       case IcsPlayingBlack:
5517       case IcsObserving:
5518       case IcsIdle:
5519       case IcsExamining:
5520         return "menuMode.ICS Client";
5521       default:
5522       case EndOfGame:
5523         return NULL;
5524     }
5525 }
5526
5527 void ModeHighlight()
5528 {
5529     Arg args[16];
5530     static int oldPausing = FALSE;
5531     static GameMode oldmode = (GameMode) -1;
5532     char *wname;
5533
5534     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5535
5536     if (pausing != oldPausing) {
5537         oldPausing = pausing;
5538         if (pausing) {
5539             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5540         } else {
5541             XtSetArg(args[0], XtNleftBitmap, None);
5542         }
5543         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5544                     args, 1);
5545
5546         if (appData.showButtonBar) {
5547           /* Always toggle, don't set.  Previous code messes up when
5548              invoked while the button is pressed, as releasing it
5549              toggles the state again. */
5550           {
5551             Pixel oldbg, oldfg;
5552             XtSetArg(args[0], XtNbackground, &oldbg);
5553             XtSetArg(args[1], XtNforeground, &oldfg);
5554             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5555                         args, 2);
5556             XtSetArg(args[0], XtNbackground, oldfg);
5557             XtSetArg(args[1], XtNforeground, oldbg);
5558           }
5559           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5560         }
5561     }
5562
5563     wname = ModeToWidgetName(oldmode);
5564     if (wname != NULL) {
5565         XtSetArg(args[0], XtNleftBitmap, None);
5566         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5567     }
5568     wname = ModeToWidgetName(gameMode);
5569     if (wname != NULL) {
5570         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5571         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5572     }
5573     oldmode = gameMode;
5574
5575     /* Maybe all the enables should be handled here, not just this one */
5576     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5577                    gameMode == Training || gameMode == PlayFromGameFile);
5578 }
5579
5580
5581 /*
5582  * Button/menu procedures
5583  */
5584 void ResetProc(w, event, prms, nprms)
5585      Widget w;
5586      XEvent *event;
5587      String *prms;
5588      Cardinal *nprms;
5589 {
5590     ResetGameEvent();
5591 }
5592
5593 int LoadGamePopUp(f, gameNumber, title)
5594      FILE *f;
5595      int gameNumber;
5596      char *title;
5597 {
5598     cmailMsgLoaded = FALSE;
5599     if (gameNumber == 0) {
5600         int error = GameListBuild(f);
5601         if (error) {
5602             DisplayError(_("Cannot build game list"), error);
5603         } else if (!ListEmpty(&gameList) &&
5604                    ((ListGame *) gameList.tailPred)->number > 1) {
5605             GameListPopUp(f, title);
5606             return TRUE;
5607         }
5608         GameListDestroy();
5609         gameNumber = 1;
5610     }
5611     return LoadGame(f, gameNumber, title, FALSE);
5612 }
5613
5614 void LoadGameProc(w, event, prms, nprms)
5615      Widget w;
5616      XEvent *event;
5617      String *prms;
5618      Cardinal *nprms;
5619 {
5620     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5621         Reset(FALSE, TRUE);
5622     }
5623     FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5624 }
5625
5626 void LoadNextGameProc(w, event, prms, nprms)
5627      Widget w;
5628      XEvent *event;
5629      String *prms;
5630      Cardinal *nprms;
5631 {
5632     ReloadGame(1);
5633 }
5634
5635 void LoadPrevGameProc(w, event, prms, nprms)
5636      Widget w;
5637      XEvent *event;
5638      String *prms;
5639      Cardinal *nprms;
5640 {
5641     ReloadGame(-1);
5642 }
5643
5644 void ReloadGameProc(w, event, prms, nprms)
5645      Widget w;
5646      XEvent *event;
5647      String *prms;
5648      Cardinal *nprms;
5649 {
5650     ReloadGame(0);
5651 }
5652
5653 void LoadNextPositionProc(w, event, prms, nprms)
5654      Widget w;
5655      XEvent *event;
5656      String *prms;
5657      Cardinal *nprms;
5658 {
5659     ReloadPosition(1);
5660 }
5661
5662 void LoadPrevPositionProc(w, event, prms, nprms)
5663      Widget w;
5664      XEvent *event;
5665      String *prms;
5666      Cardinal *nprms;
5667 {
5668     ReloadPosition(-1);
5669 }
5670
5671 void ReloadPositionProc(w, event, prms, nprms)
5672      Widget w;
5673      XEvent *event;
5674      String *prms;
5675      Cardinal *nprms;
5676 {
5677     ReloadPosition(0);
5678 }
5679
5680 void LoadPositionProc(w, event, prms, nprms)
5681      Widget w;
5682      XEvent *event;
5683      String *prms;
5684      Cardinal *nprms;
5685 {
5686     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5687         Reset(FALSE, TRUE);
5688     }
5689     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5690 }
5691
5692 void SaveGameProc(w, event, prms, nprms)
5693      Widget w;
5694      XEvent *event;
5695      String *prms;
5696      Cardinal *nprms;
5697 {
5698     FileNamePopUp(_("Save game file name?"),
5699                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5700                   SaveGame, "a");
5701 }
5702
5703 void SavePositionProc(w, event, prms, nprms)
5704      Widget w;
5705      XEvent *event;
5706      String *prms;
5707      Cardinal *nprms;
5708 {
5709     FileNamePopUp(_("Save position file name?"),
5710                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5711                   SavePosition, "a");
5712 }
5713
5714 void ReloadCmailMsgProc(w, event, prms, nprms)
5715      Widget w;
5716      XEvent *event;
5717      String *prms;
5718      Cardinal *nprms;
5719 {
5720     ReloadCmailMsgEvent(FALSE);
5721 }
5722
5723 void MailMoveProc(w, event, prms, nprms)
5724      Widget w;
5725      XEvent *event;
5726      String *prms;
5727      Cardinal *nprms;
5728 {
5729     MailMoveEvent();
5730 }
5731
5732 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5733 char *selected_fen_position=NULL;
5734
5735 Boolean
5736 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5737                  Atom *type_return, XtPointer *value_return,
5738                  unsigned long *length_return, int *format_return)
5739 {
5740   char *selection_tmp;
5741
5742   if (!selected_fen_position) return False; /* should never happen */
5743   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5744     /* note: since no XtSelectionDoneProc was registered, Xt will
5745      * automatically call XtFree on the value returned.  So have to
5746      * make a copy of it allocated with XtMalloc */
5747     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5748     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
5749
5750     *value_return=selection_tmp;
5751     *length_return=strlen(selection_tmp);
5752     *type_return=*target;
5753     *format_return = 8; /* bits per byte */
5754     return True;
5755   } else if (*target == XA_TARGETS(xDisplay)) {
5756     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5757     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5758     targets_tmp[1] = XA_STRING;
5759     *value_return = targets_tmp;
5760     *type_return = XA_ATOM;
5761     *length_return = 2;
5762     *format_return = 8 * sizeof(Atom);
5763     if (*format_return > 32) {
5764       *length_return *= *format_return / 32;
5765       *format_return = 32;
5766     }
5767     return True;
5768   } else {
5769     return False;
5770   }
5771 }
5772
5773 /* note: when called from menu all parameters are NULL, so no clue what the
5774  * Widget which was clicked on was, or what the click event was
5775  */
5776 void CopyPositionProc(w, event, prms, nprms)
5777   Widget w;
5778   XEvent *event;
5779   String *prms;
5780   Cardinal *nprms;
5781   {
5782     /*
5783      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5784      * have a notion of a position that is selected but not copied.
5785      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5786      */
5787     if(gameMode == EditPosition) EditPositionDone(TRUE);
5788     if (selected_fen_position) free(selected_fen_position);
5789     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5790     if (!selected_fen_position) return;
5791     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5792                    CurrentTime,
5793                    SendPositionSelection,
5794                    NULL/* lose_ownership_proc */ ,
5795                    NULL/* transfer_done_proc */);
5796     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5797                    CurrentTime,
5798                    SendPositionSelection,
5799                    NULL/* lose_ownership_proc */ ,
5800                    NULL/* transfer_done_proc */);
5801   }
5802
5803 /* function called when the data to Paste is ready */
5804 static void
5805 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5806            Atom *type, XtPointer value, unsigned long *len, int *format)
5807 {
5808   char *fenstr=value;
5809   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5810   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5811   EditPositionPasteFEN(fenstr);
5812   XtFree(value);
5813 }
5814
5815 /* called when Paste Position button is pressed,
5816  * all parameters will be NULL */
5817 void PastePositionProc(w, event, prms, nprms)
5818   Widget w;
5819   XEvent *event;
5820   String *prms;
5821   Cardinal *nprms;
5822 {
5823     XtGetSelectionValue(menuBarWidget,
5824       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5825       /* (XtSelectionCallbackProc) */ PastePositionCB,
5826       NULL, /* client_data passed to PastePositionCB */
5827
5828       /* better to use the time field from the event that triggered the
5829        * call to this function, but that isn't trivial to get
5830        */
5831       CurrentTime
5832     );
5833     return;
5834 }
5835
5836 static Boolean
5837 SendGameSelection(Widget w, Atom *selection, Atom *target,
5838                   Atom *type_return, XtPointer *value_return,
5839                   unsigned long *length_return, int *format_return)
5840 {
5841   char *selection_tmp;
5842
5843   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5844     FILE* f = fopen(gameCopyFilename, "r");
5845     long len;
5846     size_t count;
5847     if (f == NULL) return False;
5848     fseek(f, 0, 2);
5849     len = ftell(f);
5850     rewind(f);
5851     selection_tmp = XtMalloc(len + 1);
5852     count = fread(selection_tmp, 1, len, f);
5853     fclose(f);
5854     if (len != count) {
5855       XtFree(selection_tmp);
5856       return False;
5857     }
5858     selection_tmp[len] = NULLCHAR;
5859     *value_return = selection_tmp;
5860     *length_return = len;
5861     *type_return = *target;
5862     *format_return = 8; /* bits per byte */
5863     return True;
5864   } else if (*target == XA_TARGETS(xDisplay)) {
5865     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5866     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5867     targets_tmp[1] = XA_STRING;
5868     *value_return = targets_tmp;
5869     *type_return = XA_ATOM;
5870     *length_return = 2;
5871     *format_return = 8 * sizeof(Atom);
5872     if (*format_return > 32) {
5873       *length_return *= *format_return / 32;
5874       *format_return = 32;
5875     }
5876     return True;
5877   } else {
5878     return False;
5879   }
5880 }
5881
5882 /* note: when called from menu all parameters are NULL, so no clue what the
5883  * Widget which was clicked on was, or what the click event was
5884  */
5885 void CopyGameProc(w, event, prms, nprms)
5886   Widget w;
5887   XEvent *event;
5888   String *prms;
5889   Cardinal *nprms;
5890 {
5891   int ret;
5892
5893   ret = SaveGameToFile(gameCopyFilename, FALSE);
5894   if (!ret) return;
5895
5896   /*
5897    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5898    * have a notion of a game that is selected but not copied.
5899    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5900    */
5901   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5902                  CurrentTime,
5903                  SendGameSelection,
5904                  NULL/* lose_ownership_proc */ ,
5905                  NULL/* transfer_done_proc */);
5906   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5907                  CurrentTime,
5908                  SendGameSelection,
5909                  NULL/* lose_ownership_proc */ ,
5910                  NULL/* transfer_done_proc */);
5911 }
5912
5913 /* function called when the data to Paste is ready */
5914 static void
5915 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5916             Atom *type, XtPointer value, unsigned long *len, int *format)
5917 {
5918   FILE* f;
5919   if (value == NULL || *len == 0) {
5920     return; /* nothing had been selected to copy */
5921   }
5922   f = fopen(gamePasteFilename, "w");
5923   if (f == NULL) {
5924     DisplayError(_("Can't open temp file"), errno);
5925     return;
5926   }
5927   fwrite(value, 1, *len, f);
5928   fclose(f);
5929   XtFree(value);
5930   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5931 }
5932
5933 /* called when Paste Game button is pressed,
5934  * all parameters will be NULL */
5935 void PasteGameProc(w, event, prms, nprms)
5936   Widget w;
5937   XEvent *event;
5938   String *prms;
5939   Cardinal *nprms;
5940 {
5941     XtGetSelectionValue(menuBarWidget,
5942       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5943       /* (XtSelectionCallbackProc) */ PasteGameCB,
5944       NULL, /* client_data passed to PasteGameCB */
5945
5946       /* better to use the time field from the event that triggered the
5947        * call to this function, but that isn't trivial to get
5948        */
5949       CurrentTime
5950     );
5951     return;
5952 }
5953
5954
5955 void AutoSaveGame()
5956 {
5957     SaveGameProc(NULL, NULL, NULL, NULL);
5958 }
5959
5960
5961 void QuitProc(w, event, prms, nprms)
5962      Widget w;
5963      XEvent *event;
5964      String *prms;
5965      Cardinal *nprms;
5966 {
5967     ExitEvent(0);
5968 }
5969
5970 void PauseProc(w, event, prms, nprms)
5971      Widget w;
5972      XEvent *event;
5973      String *prms;
5974      Cardinal *nprms;
5975 {
5976     PauseEvent();
5977 }
5978
5979
5980 void MachineBlackProc(w, event, prms, nprms)
5981      Widget w;
5982      XEvent *event;
5983      String *prms;
5984      Cardinal *nprms;
5985 {
5986     MachineBlackEvent();
5987 }
5988
5989 void MachineWhiteProc(w, event, prms, nprms)
5990      Widget w;
5991      XEvent *event;
5992      String *prms;
5993      Cardinal *nprms;
5994 {
5995     MachineWhiteEvent();
5996 }
5997
5998 void AnalyzeModeProc(w, event, prms, nprms)
5999      Widget w;
6000      XEvent *event;
6001      String *prms;
6002      Cardinal *nprms;
6003 {
6004     char buf[MSG_SIZ];
6005
6006     if (!first.analysisSupport) {
6007       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6008       DisplayError(buf, 0);
6009       return;
6010     }
6011     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6012     if (appData.icsActive) {
6013         if (gameMode != IcsObserving) {
6014           snprintf(buf, MSG_SIZ, _("You are not observing a game"));
6015             DisplayError(buf, 0);
6016             /* secure check */
6017             if (appData.icsEngineAnalyze) {
6018                 if (appData.debugMode)
6019                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6020                 ExitAnalyzeMode();
6021                 ModeHighlight();
6022             }
6023             return;
6024         }
6025         /* if enable, use want disable icsEngineAnalyze */
6026         if (appData.icsEngineAnalyze) {
6027                 ExitAnalyzeMode();
6028                 ModeHighlight();
6029                 return;
6030         }
6031         appData.icsEngineAnalyze = TRUE;
6032         if (appData.debugMode)
6033             fprintf(debugFP, _("ICS engine analyze starting... \n"));
6034     }
6035 #ifndef OPTIONSDIALOG
6036     if (!appData.showThinking)
6037       ShowThinkingProc(w,event,prms,nprms);
6038 #endif
6039
6040     AnalyzeModeEvent();
6041 }
6042
6043 void AnalyzeFileProc(w, event, prms, nprms)
6044      Widget w;
6045      XEvent *event;
6046      String *prms;
6047      Cardinal *nprms;
6048 {
6049     if (!first.analysisSupport) {
6050       char buf[MSG_SIZ];
6051       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6052       DisplayError(buf, 0);
6053       return;
6054     }
6055     Reset(FALSE, TRUE);
6056 #ifndef OPTIONSDIALOG
6057     if (!appData.showThinking)
6058       ShowThinkingProc(w,event,prms,nprms);
6059 #endif
6060     AnalyzeFileEvent();
6061     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6062     AnalysisPeriodicEvent(1);
6063 }
6064
6065 void TwoMachinesProc(w, event, prms, nprms)
6066      Widget w;
6067      XEvent *event;
6068      String *prms;
6069      Cardinal *nprms;
6070 {
6071     TwoMachinesEvent();
6072 }
6073
6074 void MatchProc(w, event, prms, nprms)
6075      Widget w;
6076      XEvent *event;
6077      String *prms;
6078      Cardinal *nprms;
6079 {
6080     if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
6081     matchMode = 2; // This is back-end, really\r
6082     appData.matchGames = appData.defaultMatchGames;\r
6083     matchGame = 1;\r
6084     first.matchWins = second.matchWins = 0;\r
6085     TwoMachinesEvent();
6086 }
6087
6088 void IcsClientProc(w, event, prms, nprms)
6089      Widget w;
6090      XEvent *event;
6091      String *prms;
6092      Cardinal *nprms;
6093 {
6094     IcsClientEvent();
6095 }
6096
6097 void EditGameProc(w, event, prms, nprms)
6098      Widget w;
6099      XEvent *event;
6100      String *prms;
6101      Cardinal *nprms;
6102 {
6103     EditGameEvent();
6104 }
6105
6106 void EditPositionProc(w, event, prms, nprms)
6107      Widget w;
6108      XEvent *event;
6109      String *prms;
6110      Cardinal *nprms;
6111 {
6112     EditPositionEvent();
6113 }
6114
6115 void TrainingProc(w, event, prms, nprms)
6116      Widget w;
6117      XEvent *event;
6118      String *prms;
6119      Cardinal *nprms;
6120 {
6121     TrainingEvent();
6122 }
6123
6124 void EditCommentProc(w, event, prms, nprms)
6125      Widget w;
6126      XEvent *event;
6127      String *prms;
6128      Cardinal *nprms;
6129 {
6130     if (editUp) {
6131         EditCommentPopDown();
6132     } else {
6133         EditCommentEvent();
6134     }
6135 }
6136
6137 void IcsInputBoxProc(w, event, prms, nprms)
6138      Widget w;
6139      XEvent *event;
6140      String *prms;
6141      Cardinal *nprms;
6142 {
6143     if (!PopDown(4)) ICSInputBoxPopUp();
6144 }
6145
6146 void AcceptProc(w, event, prms, nprms)
6147      Widget w;
6148      XEvent *event;
6149      String *prms;
6150      Cardinal *nprms;
6151 {
6152     AcceptEvent();
6153 }
6154
6155 void DeclineProc(w, event, prms, nprms)
6156      Widget w;
6157      XEvent *event;
6158      String *prms;
6159      Cardinal *nprms;
6160 {
6161     DeclineEvent();
6162 }
6163
6164 void RematchProc(w, event, prms, nprms)
6165      Widget w;
6166      XEvent *event;
6167      String *prms;
6168      Cardinal *nprms;
6169 {
6170     RematchEvent();
6171 }
6172
6173 void CallFlagProc(w, event, prms, nprms)
6174      Widget w;
6175      XEvent *event;
6176      String *prms;
6177      Cardinal *nprms;
6178 {
6179     CallFlagEvent();
6180 }
6181
6182 void DrawProc(w, event, prms, nprms)
6183      Widget w;
6184      XEvent *event;
6185      String *prms;
6186      Cardinal *nprms;
6187 {
6188     DrawEvent();
6189 }
6190
6191 void AbortProc(w, event, prms, nprms)
6192      Widget w;
6193      XEvent *event;
6194      String *prms;
6195      Cardinal *nprms;
6196 {
6197     AbortEvent();
6198 }
6199
6200 void AdjournProc(w, event, prms, nprms)
6201      Widget w;
6202      XEvent *event;
6203      String *prms;
6204      Cardinal *nprms;
6205 {
6206     AdjournEvent();
6207 }
6208
6209 void ResignProc(w, event, prms, nprms)
6210      Widget w;
6211      XEvent *event;
6212      String *prms;
6213      Cardinal *nprms;
6214 {
6215     ResignEvent();
6216 }
6217
6218 void AdjuWhiteProc(w, event, prms, nprms)
6219      Widget w;
6220      XEvent *event;
6221      String *prms;
6222      Cardinal *nprms;
6223 {
6224     UserAdjudicationEvent(+1);
6225 }
6226
6227 void AdjuBlackProc(w, event, prms, nprms)
6228      Widget w;
6229      XEvent *event;
6230      String *prms;
6231      Cardinal *nprms;
6232 {
6233     UserAdjudicationEvent(-1);
6234 }
6235
6236 void AdjuDrawProc(w, event, prms, nprms)
6237      Widget w;
6238      XEvent *event;
6239      String *prms;
6240      Cardinal *nprms;
6241 {
6242     UserAdjudicationEvent(0);
6243 }
6244
6245 void EnterKeyProc(w, event, prms, nprms)
6246      Widget w;
6247      XEvent *event;
6248      String *prms;
6249      Cardinal *nprms;
6250 {
6251     if (shellUp[4] == True)
6252       ICSInputSendText();
6253 }
6254
6255 void UpKeyProc(w, event, prms, nprms)
6256      Widget w;
6257      XEvent *event;
6258      String *prms;
6259      Cardinal *nprms;
6260 {   // [HGM] input: let up-arrow recall previous line from history
6261     Widget edit;
6262     int j;
6263     Arg args[16];
6264     String val;
6265     XawTextBlock t;
6266
6267     if (!shellUp[4]) return;
6268     edit = boxOptions[0].handle;
6269     j = 0;
6270     XtSetArg(args[j], XtNstring, &val); j++;
6271     XtGetValues(edit, args, j);
6272     val = PrevInHistory(val);
6273     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6274     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6275     if(val) {
6276         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6277         XawTextReplace(edit, 0, 0, &t);
6278         XawTextSetInsertionPoint(edit, 9999);
6279     }
6280 }
6281
6282 void DownKeyProc(w, event, prms, nprms)
6283      Widget w;
6284      XEvent *event;
6285      String *prms;
6286      Cardinal *nprms;
6287 {   // [HGM] input: let down-arrow recall next line from history
6288     Widget edit;
6289     String val;
6290     XawTextBlock t;
6291
6292     if (!shellUp[4]) return;
6293     edit = boxOptions[0].handle;
6294     val = NextInHistory();
6295     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6296     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6297     if(val) {
6298         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6299         XawTextReplace(edit, 0, 0, &t);
6300         XawTextSetInsertionPoint(edit, 9999);
6301     }
6302 }
6303
6304 void StopObservingProc(w, event, prms, nprms)
6305      Widget w;
6306      XEvent *event;
6307      String *prms;
6308      Cardinal *nprms;
6309 {
6310     StopObservingEvent();
6311 }
6312
6313 void StopExaminingProc(w, event, prms, nprms)
6314      Widget w;
6315      XEvent *event;
6316      String *prms;
6317      Cardinal *nprms;
6318 {
6319     StopExaminingEvent();
6320 }
6321
6322 void UploadProc(w, event, prms, nprms)
6323      Widget w;
6324      XEvent *event;
6325      String *prms;
6326      Cardinal *nprms;
6327 {
6328     UploadGameEvent();
6329 }
6330
6331
6332 void ForwardProc(w, event, prms, nprms)
6333      Widget w;
6334      XEvent *event;
6335      String *prms;
6336      Cardinal *nprms;
6337 {
6338     ForwardEvent();
6339 }
6340
6341
6342 void BackwardProc(w, event, prms, nprms)
6343      Widget w;
6344      XEvent *event;
6345      String *prms;
6346      Cardinal *nprms;
6347 {
6348     BackwardEvent();
6349 }
6350
6351 void ToStartProc(w, event, prms, nprms)
6352      Widget w;
6353      XEvent *event;
6354      String *prms;
6355      Cardinal *nprms;
6356 {
6357     ToStartEvent();
6358 }
6359
6360 void ToEndProc(w, event, prms, nprms)
6361      Widget w;
6362      XEvent *event;
6363      String *prms;
6364      Cardinal *nprms;
6365 {
6366     ToEndEvent();
6367 }
6368
6369 void RevertProc(w, event, prms, nprms)
6370      Widget w;
6371      XEvent *event;
6372      String *prms;
6373      Cardinal *nprms;
6374 {
6375     RevertEvent(False);
6376 }
6377
6378 void AnnotateProc(w, event, prms, nprms)
6379      Widget w;
6380      XEvent *event;
6381      String *prms;
6382      Cardinal *nprms;
6383 {
6384     RevertEvent(True);
6385 }
6386
6387 void TruncateGameProc(w, event, prms, nprms)
6388      Widget w;
6389      XEvent *event;
6390      String *prms;
6391      Cardinal *nprms;
6392 {
6393     TruncateGameEvent();
6394 }
6395 void RetractMoveProc(w, event, prms, nprms)
6396      Widget w;
6397      XEvent *event;
6398      String *prms;
6399      Cardinal *nprms;
6400 {
6401     RetractMoveEvent();
6402 }
6403
6404 void MoveNowProc(w, event, prms, nprms)
6405      Widget w;
6406      XEvent *event;
6407      String *prms;
6408      Cardinal *nprms;
6409 {
6410     MoveNowEvent();
6411 }
6412
6413 void FlipViewProc(w, event, prms, nprms)
6414      Widget w;
6415      XEvent *event;
6416      String *prms;
6417      Cardinal *nprms;
6418 {
6419     flipView = !flipView;
6420     DrawPosition(True, NULL);
6421 }
6422
6423 void PonderNextMoveProc(w, event, prms, nprms)
6424      Widget w;
6425      XEvent *event;
6426      String *prms;
6427      Cardinal *nprms;
6428 {
6429     Arg args[16];
6430
6431     PonderNextMoveEvent(!appData.ponderNextMove);
6432 #ifndef OPTIONSDIALOG
6433     if (appData.ponderNextMove) {
6434         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6435     } else {
6436         XtSetArg(args[0], XtNleftBitmap, None);
6437     }
6438     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6439                 args, 1);
6440 #endif
6441 }
6442
6443 #ifndef OPTIONSDIALOG
6444 void AlwaysQueenProc(w, event, prms, nprms)
6445      Widget w;
6446      XEvent *event;
6447      String *prms;
6448      Cardinal *nprms;
6449 {
6450     Arg args[16];
6451
6452     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6453
6454     if (appData.alwaysPromoteToQueen) {
6455         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6456     } else {
6457         XtSetArg(args[0], XtNleftBitmap, None);
6458     }
6459     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6460                 args, 1);
6461 }
6462
6463 void AnimateDraggingProc(w, event, prms, nprms)
6464      Widget w;
6465      XEvent *event;
6466      String *prms;
6467      Cardinal *nprms;
6468 {
6469     Arg args[16];
6470
6471     appData.animateDragging = !appData.animateDragging;
6472
6473     if (appData.animateDragging) {
6474         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6475         CreateAnimVars();
6476     } else {
6477         XtSetArg(args[0], XtNleftBitmap, None);
6478     }
6479     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6480                 args, 1);
6481 }
6482
6483 void AnimateMovingProc(w, event, prms, nprms)
6484      Widget w;
6485      XEvent *event;
6486      String *prms;
6487      Cardinal *nprms;
6488 {
6489     Arg args[16];
6490
6491     appData.animate = !appData.animate;
6492
6493     if (appData.animate) {
6494         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6495         CreateAnimVars();
6496     } else {
6497         XtSetArg(args[0], XtNleftBitmap, None);
6498     }
6499     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6500                 args, 1);
6501 }
6502
6503 void AutoflagProc(w, event, prms, nprms)
6504      Widget w;
6505      XEvent *event;
6506      String *prms;
6507      Cardinal *nprms;
6508 {
6509     Arg args[16];
6510
6511     appData.autoCallFlag = !appData.autoCallFlag;
6512
6513     if (appData.autoCallFlag) {
6514         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6515     } else {
6516         XtSetArg(args[0], XtNleftBitmap, None);
6517     }
6518     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6519                 args, 1);
6520 }
6521
6522 void AutoflipProc(w, event, prms, nprms)
6523      Widget w;
6524      XEvent *event;
6525      String *prms;
6526      Cardinal *nprms;
6527 {
6528     Arg args[16];
6529
6530     appData.autoFlipView = !appData.autoFlipView;
6531
6532     if (appData.autoFlipView) {
6533         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6534     } else {
6535         XtSetArg(args[0], XtNleftBitmap, None);
6536     }
6537     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6538                 args, 1);
6539 }
6540
6541 void BlindfoldProc(w, event, prms, nprms)
6542      Widget w;
6543      XEvent *event;
6544      String *prms;
6545      Cardinal *nprms;
6546 {
6547     Arg args[16];
6548
6549     appData.blindfold = !appData.blindfold;
6550
6551     if (appData.blindfold) {
6552         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6553     } else {
6554         XtSetArg(args[0], XtNleftBitmap, None);
6555     }
6556     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6557                 args, 1);
6558
6559     DrawPosition(True, NULL);
6560 }
6561
6562 void TestLegalityProc(w, event, prms, nprms)
6563      Widget w;
6564      XEvent *event;
6565      String *prms;
6566      Cardinal *nprms;
6567 {
6568     Arg args[16];
6569
6570     appData.testLegality = !appData.testLegality;
6571
6572     if (appData.testLegality) {
6573         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6574     } else {
6575         XtSetArg(args[0], XtNleftBitmap, None);
6576     }
6577     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6578                 args, 1);
6579 }
6580
6581
6582 void FlashMovesProc(w, event, prms, nprms)
6583      Widget w;
6584      XEvent *event;
6585      String *prms;
6586      Cardinal *nprms;
6587 {
6588     Arg args[16];
6589
6590     if (appData.flashCount == 0) {
6591         appData.flashCount = 3;
6592     } else {
6593         appData.flashCount = -appData.flashCount;
6594     }
6595
6596     if (appData.flashCount > 0) {
6597         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6598     } else {
6599         XtSetArg(args[0], XtNleftBitmap, None);
6600     }
6601     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6602                 args, 1);
6603 }
6604
6605 #if HIGHDRAG
6606 void HighlightDraggingProc(w, event, prms, nprms)
6607      Widget w;
6608      XEvent *event;
6609      String *prms;
6610      Cardinal *nprms;
6611 {
6612     Arg args[16];
6613
6614     appData.highlightDragging = !appData.highlightDragging;
6615
6616     if (appData.highlightDragging) {
6617         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6618     } else {
6619         XtSetArg(args[0], XtNleftBitmap, None);
6620     }
6621     XtSetValues(XtNameToWidget(menuBarWidget,
6622                                "menuOptions.Highlight Dragging"), args, 1);
6623 }
6624 #endif
6625
6626 void HighlightLastMoveProc(w, event, prms, nprms)
6627      Widget w;
6628      XEvent *event;
6629      String *prms;
6630      Cardinal *nprms;
6631 {
6632     Arg args[16];
6633
6634     appData.highlightLastMove = !appData.highlightLastMove;
6635
6636     if (appData.highlightLastMove) {
6637         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6638     } else {
6639         XtSetArg(args[0], XtNleftBitmap, None);
6640     }
6641     XtSetValues(XtNameToWidget(menuBarWidget,
6642                                "menuOptions.Highlight Last Move"), args, 1);
6643 }
6644
6645 void HighlightArrowProc(w, event, prms, nprms)
6646      Widget w;
6647      XEvent *event;
6648      String *prms;
6649      Cardinal *nprms;
6650 {
6651     Arg args[16];
6652
6653     appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6654
6655     if (appData.highlightMoveWithArrow) {
6656         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6657     } else {
6658         XtSetArg(args[0], XtNleftBitmap, None);
6659     }
6660     XtSetValues(XtNameToWidget(menuBarWidget,
6661                                "menuOptions.Arrow"), args, 1);
6662 }
6663
6664 #if 0
6665 void IcsAlarmProc(w, event, prms, nprms)
6666      Widget w;
6667      XEvent *event;
6668      String *prms;
6669      Cardinal *nprms;
6670 {
6671     Arg args[16];
6672
6673     appData.icsAlarm = !appData.icsAlarm;
6674
6675     if (appData.icsAlarm) {
6676         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6677     } else {
6678         XtSetArg(args[0], XtNleftBitmap, None);
6679     }
6680     XtSetValues(XtNameToWidget(menuBarWidget,
6681                                "menuOptions.ICS Alarm"), args, 1);
6682 }
6683 #endif
6684
6685 void MoveSoundProc(w, event, prms, nprms)
6686      Widget w;
6687      XEvent *event;
6688      String *prms;
6689      Cardinal *nprms;
6690 {
6691     Arg args[16];
6692
6693     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6694
6695     if (appData.ringBellAfterMoves) {
6696         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6697     } else {
6698         XtSetArg(args[0], XtNleftBitmap, None);
6699     }
6700     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6701                 args, 1);
6702 }
6703
6704 void OneClickProc(w, event, prms, nprms)
6705      Widget w;
6706      XEvent *event;
6707      String *prms;
6708      Cardinal *nprms;
6709 {
6710     Arg args[16];
6711
6712     appData.oneClick = !appData.oneClick;
6713
6714     if (appData.oneClick) {
6715         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6716     } else {
6717         XtSetArg(args[0], XtNleftBitmap, None);
6718     }
6719     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6720                 args, 1);
6721 }
6722
6723 void PeriodicUpdatesProc(w, event, prms, nprms)
6724      Widget w;
6725      XEvent *event;
6726      String *prms;
6727      Cardinal *nprms;
6728 {
6729     Arg args[16];
6730
6731     PeriodicUpdatesEvent(!appData.periodicUpdates);
6732
6733     if (appData.periodicUpdates) {
6734         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6735     } else {
6736         XtSetArg(args[0], XtNleftBitmap, None);
6737     }
6738     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6739                 args, 1);
6740 }
6741
6742 void PopupExitMessageProc(w, event, prms, nprms)
6743      Widget w;
6744      XEvent *event;
6745      String *prms;
6746      Cardinal *nprms;
6747 {
6748     Arg args[16];
6749
6750     appData.popupExitMessage = !appData.popupExitMessage;
6751
6752     if (appData.popupExitMessage) {
6753         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6754     } else {
6755         XtSetArg(args[0], XtNleftBitmap, None);
6756     }
6757     XtSetValues(XtNameToWidget(menuBarWidget,
6758                                "menuOptions.Popup Exit Message"), args, 1);
6759 }
6760
6761 void PopupMoveErrorsProc(w, event, prms, nprms)
6762      Widget w;
6763      XEvent *event;
6764      String *prms;
6765      Cardinal *nprms;
6766 {
6767     Arg args[16];
6768
6769     appData.popupMoveErrors = !appData.popupMoveErrors;
6770
6771     if (appData.popupMoveErrors) {
6772         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6773     } else {
6774         XtSetArg(args[0], XtNleftBitmap, None);
6775     }
6776     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6777                 args, 1);
6778 }
6779
6780 #if 0
6781 void PremoveProc(w, event, prms, nprms)
6782      Widget w;
6783      XEvent *event;
6784      String *prms;
6785      Cardinal *nprms;
6786 {
6787     Arg args[16];
6788
6789     appData.premove = !appData.premove;
6790
6791     if (appData.premove) {
6792         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6793     } else {
6794         XtSetArg(args[0], XtNleftBitmap, None);
6795     }
6796     XtSetValues(XtNameToWidget(menuBarWidget,
6797                                "menuOptions.Premove"), args, 1);
6798 }
6799 #endif
6800
6801 void ShowCoordsProc(w, event, prms, nprms)
6802      Widget w;
6803      XEvent *event;
6804      String *prms;
6805      Cardinal *nprms;
6806 {
6807     Arg args[16];
6808
6809     appData.showCoords = !appData.showCoords;
6810
6811     if (appData.showCoords) {
6812         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6813     } else {
6814         XtSetArg(args[0], XtNleftBitmap, None);
6815     }
6816     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6817                 args, 1);
6818
6819     DrawPosition(True, NULL);
6820 }
6821
6822 void ShowThinkingProc(w, event, prms, nprms)
6823      Widget w;
6824      XEvent *event;
6825      String *prms;
6826      Cardinal *nprms;
6827 {
6828     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6829     ShowThinkingEvent();
6830 }
6831
6832 void HideThinkingProc(w, event, prms, nprms)
6833      Widget w;
6834      XEvent *event;
6835      String *prms;
6836      Cardinal *nprms;
6837 {
6838     Arg args[16];
6839
6840     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6841     ShowThinkingEvent();
6842
6843     if (appData.hideThinkingFromHuman) {
6844         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6845     } else {
6846         XtSetArg(args[0], XtNleftBitmap, None);
6847     }
6848     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6849                 args, 1);
6850 }
6851 #endif
6852
6853 void SaveOnExitProc(w, event, prms, nprms)
6854      Widget w;
6855      XEvent *event;
6856      String *prms;
6857      Cardinal *nprms;
6858 {
6859     Arg args[16];
6860
6861     saveSettingsOnExit = !saveSettingsOnExit;
6862
6863     if (saveSettingsOnExit) {
6864         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6865     } else {
6866         XtSetArg(args[0], XtNleftBitmap, None);
6867     }
6868     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6869                 args, 1);
6870 }
6871
6872 void SaveSettingsProc(w, event, prms, nprms)
6873      Widget w;
6874      XEvent *event;
6875      String *prms;
6876      Cardinal *nprms;
6877 {
6878      SaveSettings(settingsFileName);
6879 }
6880
6881 void InfoProc(w, event, prms, nprms)
6882      Widget w;
6883      XEvent *event;
6884      String *prms;
6885      Cardinal *nprms;
6886 {
6887     char buf[MSG_SIZ];
6888     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6889             INFODIR, INFOFILE);
6890     system(buf);
6891 }
6892
6893 void ManProc(w, event, prms, nprms)
6894      Widget w;
6895      XEvent *event;
6896      String *prms;
6897      Cardinal *nprms;
6898 {
6899     char buf[MSG_SIZ];
6900     String name;
6901     if (nprms && *nprms > 0)
6902       name = prms[0];
6903     else
6904       name = "xboard";
6905     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6906     system(buf);
6907 }
6908
6909 void HintProc(w, event, prms, nprms)
6910      Widget w;
6911      XEvent *event;
6912      String *prms;
6913      Cardinal *nprms;
6914 {
6915     HintEvent();
6916 }
6917
6918 void BookProc(w, event, prms, nprms)
6919      Widget w;
6920      XEvent *event;
6921      String *prms;
6922      Cardinal *nprms;
6923 {
6924     BookEvent();
6925 }
6926
6927 void AboutProc(w, event, prms, nprms)
6928      Widget w;
6929      XEvent *event;
6930      String *prms;
6931      Cardinal *nprms;
6932 {
6933     char buf[MSG_SIZ];
6934 #if ZIPPY
6935     char *zippy = " (with Zippy code)";
6936 #else
6937     char *zippy = "";
6938 #endif
6939     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
6940             programVersion, zippy,
6941             "Copyright 1991 Digital Equipment Corporation",
6942             "Enhancements Copyright 1992-2009 Free Software Foundation",
6943             "Enhancements Copyright 2005 Alessandro Scotti",
6944             PACKAGE, " is free software and carries NO WARRANTY;",
6945             "see the file COPYING for more information.");
6946     ErrorPopUp(_("About XBoard"), buf, FALSE);
6947 }
6948
6949 void DebugProc(w, event, prms, nprms)
6950      Widget w;
6951      XEvent *event;
6952      String *prms;
6953      Cardinal *nprms;
6954 {
6955     appData.debugMode = !appData.debugMode;
6956 }
6957
6958 void AboutGameProc(w, event, prms, nprms)
6959      Widget w;
6960      XEvent *event;
6961      String *prms;
6962      Cardinal *nprms;
6963 {
6964     AboutGameEvent();
6965 }
6966
6967 void NothingProc(w, event, prms, nprms)
6968      Widget w;
6969      XEvent *event;
6970      String *prms;
6971      Cardinal *nprms;
6972 {
6973     return;
6974 }
6975
6976 void Iconify(w, event, prms, nprms)
6977      Widget w;
6978      XEvent *event;
6979      String *prms;
6980      Cardinal *nprms;
6981 {
6982     Arg args[16];
6983
6984     fromX = fromY = -1;
6985     XtSetArg(args[0], XtNiconic, True);
6986     XtSetValues(shellWidget, args, 1);
6987 }
6988
6989 void DisplayMessage(message, extMessage)
6990      char *message, *extMessage;
6991 {
6992   /* display a message in the message widget */
6993
6994   char buf[MSG_SIZ];
6995   Arg arg;
6996
6997   if (extMessage)
6998     {
6999       if (*message)
7000         {
7001           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
7002           message = buf;
7003         }
7004       else
7005         {
7006           message = extMessage;
7007         };
7008     };
7009
7010   /* need to test if messageWidget already exists, since this function
7011      can also be called during the startup, if for example a Xresource
7012      is not set up correctly */
7013   if(messageWidget)
7014     {
7015       XtSetArg(arg, XtNlabel, message);
7016       XtSetValues(messageWidget, &arg, 1);
7017     };
7018
7019   return;
7020 }
7021
7022 void DisplayTitle(text)
7023      char *text;
7024 {
7025     Arg args[16];
7026     int i;
7027     char title[MSG_SIZ];
7028     char icon[MSG_SIZ];
7029
7030     if (text == NULL) text = "";
7031
7032     if (appData.titleInWindow) {
7033         i = 0;
7034         XtSetArg(args[i], XtNlabel, text);   i++;
7035         XtSetValues(titleWidget, args, i);
7036     }
7037
7038     if (*text != NULLCHAR) {
7039       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
7040       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
7041     } else if (appData.icsActive) {
7042         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7043         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7044     } else if (appData.cmailGameName[0] != NULLCHAR) {
7045         snprintf(icon, sizeof(icon), "%s", "CMail");
7046         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7047 #ifdef GOTHIC
7048     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7049     } else if (gameInfo.variant == VariantGothic) {
7050       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
7051       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
7052 #endif
7053 #ifdef FALCON
7054     } else if (gameInfo.variant == VariantFalcon) {
7055       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7056       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
7057 #endif
7058     } else if (appData.noChessProgram) {
7059       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7060       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
7061     } else {
7062       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
7063         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7064     }
7065     i = 0;
7066     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
7067     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
7068     XtSetValues(shellWidget, args, i);
7069 }
7070
7071
7072 void
7073 DisplayError(message, error)
7074      String message;
7075      int error;
7076 {
7077     char buf[MSG_SIZ];
7078
7079     if (error == 0) {
7080         if (appData.debugMode || appData.matchMode) {
7081             fprintf(stderr, "%s: %s\n", programName, message);
7082         }
7083     } else {
7084         if (appData.debugMode || appData.matchMode) {
7085             fprintf(stderr, "%s: %s: %s\n",
7086                     programName, message, strerror(error));
7087         }
7088         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7089         message = buf;
7090     }
7091     ErrorPopUp(_("Error"), message, FALSE);
7092 }
7093
7094
7095 void DisplayMoveError(message)
7096      String message;
7097 {
7098     fromX = fromY = -1;
7099     ClearHighlights();
7100     DrawPosition(FALSE, NULL);
7101     if (appData.debugMode || appData.matchMode) {
7102         fprintf(stderr, "%s: %s\n", programName, message);
7103     }
7104     if (appData.popupMoveErrors) {
7105         ErrorPopUp(_("Error"), message, FALSE);
7106     } else {
7107         DisplayMessage(message, "");
7108     }
7109 }
7110
7111
7112 void DisplayFatalError(message, error, status)
7113      String message;
7114      int error, status;
7115 {
7116     char buf[MSG_SIZ];
7117
7118     errorExitStatus = status;
7119     if (error == 0) {
7120         fprintf(stderr, "%s: %s\n", programName, message);
7121     } else {
7122         fprintf(stderr, "%s: %s: %s\n",
7123                 programName, message, strerror(error));
7124         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7125         message = buf;
7126     }
7127     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7128       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7129     } else {
7130       ExitEvent(status);
7131     }
7132 }
7133
7134 void DisplayInformation(message)
7135      String message;
7136 {
7137     ErrorPopDown();
7138     ErrorPopUp(_("Information"), message, TRUE);
7139 }
7140
7141 void DisplayNote(message)
7142      String message;
7143 {
7144     ErrorPopDown();
7145     ErrorPopUp(_("Note"), message, FALSE);
7146 }
7147
7148 static int
7149 NullXErrorCheck(dpy, error_event)
7150      Display *dpy;
7151      XErrorEvent *error_event;
7152 {
7153     return 0;
7154 }
7155
7156 void DisplayIcsInteractionTitle(message)
7157      String message;
7158 {
7159   if (oldICSInteractionTitle == NULL) {
7160     /* Magic to find the old window title, adapted from vim */
7161     char *wina = getenv("WINDOWID");
7162     if (wina != NULL) {
7163       Window win = (Window) atoi(wina);
7164       Window root, parent, *children;
7165       unsigned int nchildren;
7166       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7167       for (;;) {
7168         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7169         if (!XQueryTree(xDisplay, win, &root, &parent,
7170                         &children, &nchildren)) break;
7171         if (children) XFree((void *)children);
7172         if (parent == root || parent == 0) break;
7173         win = parent;
7174       }
7175       XSetErrorHandler(oldHandler);
7176     }
7177     if (oldICSInteractionTitle == NULL) {
7178       oldICSInteractionTitle = "xterm";
7179     }
7180   }
7181   printf("\033]0;%s\007", message);
7182   fflush(stdout);
7183 }
7184
7185 char pendingReplyPrefix[MSG_SIZ];
7186 ProcRef pendingReplyPR;
7187
7188 void AskQuestionProc(w, event, prms, nprms)
7189      Widget w;
7190      XEvent *event;
7191      String *prms;
7192      Cardinal *nprms;
7193 {
7194     if (*nprms != 4) {
7195         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7196                 *nprms);
7197         return;
7198     }
7199     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7200 }
7201
7202 void AskQuestionPopDown()
7203 {
7204     if (!askQuestionUp) return;
7205     XtPopdown(askQuestionShell);
7206     XtDestroyWidget(askQuestionShell);
7207     askQuestionUp = False;
7208 }
7209
7210 void AskQuestionReplyAction(w, event, prms, nprms)
7211      Widget w;
7212      XEvent *event;
7213      String *prms;
7214      Cardinal *nprms;
7215 {
7216     char buf[MSG_SIZ];
7217     int err;
7218     String reply;
7219
7220     reply = XawDialogGetValueString(w = XtParent(w));
7221     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7222     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7223     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7224     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
7225     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7226     AskQuestionPopDown();
7227
7228     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7229 }
7230
7231 void AskQuestionCallback(w, client_data, call_data)
7232      Widget w;
7233      XtPointer client_data, call_data;
7234 {
7235     String name;
7236     Arg args[16];
7237
7238     XtSetArg(args[0], XtNlabel, &name);
7239     XtGetValues(w, args, 1);
7240
7241     if (strcmp(name, _("cancel")) == 0) {
7242         AskQuestionPopDown();
7243     } else {
7244         AskQuestionReplyAction(w, NULL, NULL, NULL);
7245     }
7246 }
7247
7248 void AskQuestion(title, question, replyPrefix, pr)
7249      char *title, *question, *replyPrefix;
7250      ProcRef pr;
7251 {
7252     Arg args[16];
7253     Widget popup, layout, dialog, edit;
7254     Window root, child;
7255     int x, y, i;
7256     int win_x, win_y;
7257     unsigned int mask;
7258
7259     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7260     pendingReplyPR = pr;
7261
7262     i = 0;
7263     XtSetArg(args[i], XtNresizable, True); i++;
7264     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7265     askQuestionShell = popup =
7266       XtCreatePopupShell(title, transientShellWidgetClass,
7267                          shellWidget, args, i);
7268
7269     layout =
7270       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7271                             layoutArgs, XtNumber(layoutArgs));
7272
7273     i = 0;
7274     XtSetArg(args[i], XtNlabel, question); i++;
7275     XtSetArg(args[i], XtNvalue, ""); i++;
7276     XtSetArg(args[i], XtNborderWidth, 0); i++;
7277     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7278                                    layout, args, i);
7279
7280     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7281                        (XtPointer) dialog);
7282     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7283                        (XtPointer) dialog);
7284
7285     XtRealizeWidget(popup);
7286     CatchDeleteWindow(popup, "AskQuestionPopDown");
7287
7288     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7289                   &x, &y, &win_x, &win_y, &mask);
7290
7291     XtSetArg(args[0], XtNx, x - 10);
7292     XtSetArg(args[1], XtNy, y - 30);
7293     XtSetValues(popup, args, 2);
7294
7295     XtPopup(popup, XtGrabExclusive);
7296     askQuestionUp = True;
7297
7298     edit = XtNameToWidget(dialog, "*value");
7299     XtSetKeyboardFocus(popup, edit);
7300 }
7301
7302
7303 void
7304 PlaySound(name)
7305      char *name;
7306 {
7307   if (*name == NULLCHAR) {
7308     return;
7309   } else if (strcmp(name, "$") == 0) {
7310     putc(BELLCHAR, stderr);
7311   } else {
7312     char buf[2048];
7313     char *prefix = "", *sep = "";
7314     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7315     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7316     system(buf);
7317   }
7318 }
7319
7320 void
7321 RingBell()
7322 {
7323   PlaySound(appData.soundMove);
7324 }
7325
7326 void
7327 PlayIcsWinSound()
7328 {
7329   PlaySound(appData.soundIcsWin);
7330 }
7331
7332 void
7333 PlayIcsLossSound()
7334 {
7335   PlaySound(appData.soundIcsLoss);
7336 }
7337
7338 void
7339 PlayIcsDrawSound()
7340 {
7341   PlaySound(appData.soundIcsDraw);
7342 }
7343
7344 void
7345 PlayIcsUnfinishedSound()
7346 {
7347   PlaySound(appData.soundIcsUnfinished);
7348 }
7349
7350 void
7351 PlayAlarmSound()
7352 {
7353   PlaySound(appData.soundIcsAlarm);
7354 }
7355
7356 void
7357 EchoOn()
7358 {
7359     system("stty echo");
7360 }
7361
7362 void
7363 EchoOff()
7364 {
7365     system("stty -echo");
7366 }
7367
7368 void
7369 Colorize(cc, continuation)
7370      ColorClass cc;
7371      int continuation;
7372 {
7373     char buf[MSG_SIZ];
7374     int count, outCount, error;
7375
7376     if (textColors[(int)cc].bg > 0) {
7377         if (textColors[(int)cc].fg > 0) {
7378           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7379                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7380         } else {
7381           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7382                    textColors[(int)cc].bg);
7383         }
7384     } else {
7385         if (textColors[(int)cc].fg > 0) {
7386           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7387                     textColors[(int)cc].fg);
7388         } else {
7389           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7390         }
7391     }
7392     count = strlen(buf);
7393     outCount = OutputToProcess(NoProc, buf, count, &error);
7394     if (outCount < count) {
7395         DisplayFatalError(_("Error writing to display"), error, 1);
7396     }
7397
7398     if (continuation) return;
7399     switch (cc) {
7400     case ColorShout:
7401       PlaySound(appData.soundShout);
7402       break;
7403     case ColorSShout:
7404       PlaySound(appData.soundSShout);
7405       break;
7406     case ColorChannel1:
7407       PlaySound(appData.soundChannel1);
7408       break;
7409     case ColorChannel:
7410       PlaySound(appData.soundChannel);
7411       break;
7412     case ColorKibitz:
7413       PlaySound(appData.soundKibitz);
7414       break;
7415     case ColorTell:
7416       PlaySound(appData.soundTell);
7417       break;
7418     case ColorChallenge:
7419       PlaySound(appData.soundChallenge);
7420       break;
7421     case ColorRequest:
7422       PlaySound(appData.soundRequest);
7423       break;
7424     case ColorSeek:
7425       PlaySound(appData.soundSeek);
7426       break;
7427     case ColorNormal:
7428     case ColorNone:
7429     default:
7430       break;
7431     }
7432 }
7433
7434 char *UserName()
7435 {
7436     return getpwuid(getuid())->pw_name;
7437 }
7438
7439 static char *
7440 ExpandPathName(path)
7441      char *path;
7442 {
7443     static char static_buf[4*MSG_SIZ];
7444     char *d, *s, buf[4*MSG_SIZ];
7445     struct passwd *pwd;
7446
7447     s = path;
7448     d = static_buf;
7449
7450     while (*s && isspace(*s))
7451       ++s;
7452
7453     if (!*s) {
7454         *d = 0;
7455         return static_buf;
7456     }
7457
7458     if (*s == '~') {
7459         if (*(s+1) == '/') {
7460           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7461           strcat(d, s+1);
7462         }
7463         else {
7464           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7465           { char *p; if(p = strchr(buf, '/')) *p = 0; }
7466           pwd = getpwnam(buf);
7467           if (!pwd)
7468             {
7469               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7470                       buf, path);
7471               return NULL;
7472             }
7473           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7474           strcat(d, strchr(s+1, '/'));
7475         }
7476     }
7477     else
7478       safeStrCpy(d, s, 4*MSG_SIZ );
7479
7480     return static_buf;
7481 }
7482
7483 char *HostName()
7484 {
7485     static char host_name[MSG_SIZ];
7486
7487 #if HAVE_GETHOSTNAME
7488     gethostname(host_name, MSG_SIZ);
7489     return host_name;
7490 #else  /* not HAVE_GETHOSTNAME */
7491 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7492     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7493     return host_name;
7494 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7495     return "localhost";
7496 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7497 #endif /* not HAVE_GETHOSTNAME */
7498 }
7499
7500 XtIntervalId delayedEventTimerXID = 0;
7501 DelayedEventCallback delayedEventCallback = 0;
7502
7503 void
7504 FireDelayedEvent()
7505 {
7506     delayedEventTimerXID = 0;
7507     delayedEventCallback();
7508 }
7509
7510 void
7511 ScheduleDelayedEvent(cb, millisec)
7512      DelayedEventCallback cb; long millisec;
7513 {
7514     if(delayedEventTimerXID && delayedEventCallback == cb)
7515         // [HGM] alive: replace, rather than add or flush identical event
7516         XtRemoveTimeOut(delayedEventTimerXID);
7517     delayedEventCallback = cb;
7518     delayedEventTimerXID =
7519       XtAppAddTimeOut(appContext, millisec,
7520                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7521 }
7522
7523 DelayedEventCallback
7524 GetDelayedEvent()
7525 {
7526   if (delayedEventTimerXID) {
7527     return delayedEventCallback;
7528   } else {
7529     return NULL;
7530   }
7531 }
7532
7533 void
7534 CancelDelayedEvent()
7535 {
7536   if (delayedEventTimerXID) {
7537     XtRemoveTimeOut(delayedEventTimerXID);
7538     delayedEventTimerXID = 0;
7539   }
7540 }
7541
7542 XtIntervalId loadGameTimerXID = 0;
7543
7544 int LoadGameTimerRunning()
7545 {
7546     return loadGameTimerXID != 0;
7547 }
7548
7549 int StopLoadGameTimer()
7550 {
7551     if (loadGameTimerXID != 0) {
7552         XtRemoveTimeOut(loadGameTimerXID);
7553         loadGameTimerXID = 0;
7554         return TRUE;
7555     } else {
7556         return FALSE;
7557     }
7558 }
7559
7560 void
7561 LoadGameTimerCallback(arg, id)
7562      XtPointer arg;
7563      XtIntervalId *id;
7564 {
7565     loadGameTimerXID = 0;
7566     AutoPlayGameLoop();
7567 }
7568
7569 void
7570 StartLoadGameTimer(millisec)
7571      long millisec;
7572 {
7573     loadGameTimerXID =
7574       XtAppAddTimeOut(appContext, millisec,
7575                       (XtTimerCallbackProc) LoadGameTimerCallback,
7576                       (XtPointer) 0);
7577 }
7578
7579 XtIntervalId analysisClockXID = 0;
7580
7581 void
7582 AnalysisClockCallback(arg, id)
7583      XtPointer arg;
7584      XtIntervalId *id;
7585 {
7586     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7587          || appData.icsEngineAnalyze) { // [DM]
7588         AnalysisPeriodicEvent(0);
7589         StartAnalysisClock();
7590     }
7591 }
7592
7593 void
7594 StartAnalysisClock()
7595 {
7596     analysisClockXID =
7597       XtAppAddTimeOut(appContext, 2000,
7598                       (XtTimerCallbackProc) AnalysisClockCallback,
7599                       (XtPointer) 0);
7600 }
7601
7602 XtIntervalId clockTimerXID = 0;
7603
7604 int ClockTimerRunning()
7605 {
7606     return clockTimerXID != 0;
7607 }
7608
7609 int StopClockTimer()
7610 {
7611     if (clockTimerXID != 0) {
7612         XtRemoveTimeOut(clockTimerXID);
7613         clockTimerXID = 0;
7614         return TRUE;
7615     } else {
7616         return FALSE;
7617     }
7618 }
7619
7620 void
7621 ClockTimerCallback(arg, id)
7622      XtPointer arg;
7623      XtIntervalId *id;
7624 {
7625     clockTimerXID = 0;
7626     DecrementClocks();
7627 }
7628
7629 void
7630 StartClockTimer(millisec)
7631      long millisec;
7632 {
7633     clockTimerXID =
7634       XtAppAddTimeOut(appContext, millisec,
7635                       (XtTimerCallbackProc) ClockTimerCallback,
7636                       (XtPointer) 0);
7637 }
7638
7639 void
7640 DisplayTimerLabel(w, color, timer, highlight)
7641      Widget w;
7642      char *color;
7643      long timer;
7644      int highlight;
7645 {
7646     char buf[MSG_SIZ];
7647     Arg args[16];
7648
7649     /* check for low time warning */
7650     Pixel foregroundOrWarningColor = timerForegroundPixel;
7651
7652     if (timer > 0 &&
7653         appData.lowTimeWarning &&
7654         (timer / 1000) < appData.icsAlarmTime)
7655       foregroundOrWarningColor = lowTimeWarningColor;
7656
7657     if (appData.clockMode) {
7658       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7659       XtSetArg(args[0], XtNlabel, buf);
7660     } else {
7661       snprintf(buf, MSG_SIZ, "%s  ", color);
7662       XtSetArg(args[0], XtNlabel, buf);
7663     }
7664
7665     if (highlight) {
7666
7667         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7668         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7669     } else {
7670         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7671         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7672     }
7673
7674     XtSetValues(w, args, 3);
7675 }
7676
7677 void
7678 DisplayWhiteClock(timeRemaining, highlight)
7679      long timeRemaining;
7680      int highlight;
7681 {
7682     Arg args[16];
7683
7684     if(appData.noGUI) return;
7685     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7686     if (highlight && iconPixmap == bIconPixmap) {
7687         iconPixmap = wIconPixmap;
7688         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7689         XtSetValues(shellWidget, args, 1);
7690     }
7691 }
7692
7693 void
7694 DisplayBlackClock(timeRemaining, highlight)
7695      long timeRemaining;
7696      int highlight;
7697 {
7698     Arg args[16];
7699
7700     if(appData.noGUI) return;
7701     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7702     if (highlight && iconPixmap == wIconPixmap) {
7703         iconPixmap = bIconPixmap;
7704         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7705         XtSetValues(shellWidget, args, 1);
7706     }
7707 }
7708
7709 #define CPNone 0
7710 #define CPReal 1
7711 #define CPComm 2
7712 #define CPSock 3
7713 #define CPLoop 4
7714 typedef int CPKind;
7715
7716 typedef struct {
7717     CPKind kind;
7718     int pid;
7719     int fdTo, fdFrom;
7720 } ChildProc;
7721
7722
7723 int StartChildProcess(cmdLine, dir, pr)
7724      char *cmdLine;
7725      char *dir;
7726      ProcRef *pr;
7727 {
7728     char *argv[64], *p;
7729     int i, pid;
7730     int to_prog[2], from_prog[2];
7731     ChildProc *cp;
7732     char buf[MSG_SIZ];
7733
7734     if (appData.debugMode) {
7735         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7736     }
7737
7738     /* We do NOT feed the cmdLine to the shell; we just
7739        parse it into blank-separated arguments in the
7740        most simple-minded way possible.
7741        */
7742     i = 0;
7743     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7744     p = buf;
7745     for (;;) {
7746         while(*p == ' ') p++;
7747         argv[i++] = p;
7748         if(*p == '"' || *p == '\'')
7749              p = strchr(++argv[i-1], *p);
7750         else p = strchr(p, ' ');
7751         if (p == NULL) break;
7752         *p++ = NULLCHAR;
7753     }
7754     argv[i] = NULL;
7755
7756     SetUpChildIO(to_prog, from_prog);
7757
7758     if ((pid = fork()) == 0) {
7759         /* Child process */
7760         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7761         close(to_prog[1]);     // first close the unused pipe ends
7762         close(from_prog[0]);
7763         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7764         dup2(from_prog[1], 1);
7765         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7766         close(from_prog[1]);                   // and closing again loses one of the pipes!
7767         if(fileno(stderr) >= 2) // better safe than sorry...
7768                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7769
7770         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7771             perror(dir);
7772             exit(1);
7773         }
7774
7775         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7776
7777         execvp(argv[0], argv);
7778
7779         /* If we get here, exec failed */
7780         perror(argv[0]);
7781         exit(1);
7782     }
7783
7784     /* Parent process */
7785     close(to_prog[0]);
7786     close(from_prog[1]);
7787
7788     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7789     cp->kind = CPReal;
7790     cp->pid = pid;
7791     cp->fdFrom = from_prog[0];
7792     cp->fdTo = to_prog[1];
7793     *pr = (ProcRef) cp;
7794     return 0;
7795 }
7796
7797 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7798 static RETSIGTYPE AlarmCallBack(int n)
7799 {
7800     return;
7801 }
7802
7803 void
7804 DestroyChildProcess(pr, signalType)
7805      ProcRef pr;
7806      int signalType;
7807 {
7808     ChildProc *cp = (ChildProc *) pr;
7809
7810     if (cp->kind != CPReal) return;
7811     cp->kind = CPNone;
7812     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7813         signal(SIGALRM, AlarmCallBack);
7814         alarm(3);
7815         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7816             kill(cp->pid, SIGKILL); // kill it forcefully
7817             wait((int *) 0);        // and wait again
7818         }
7819     } else {
7820         if (signalType) {
7821             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7822         }
7823         /* Process is exiting either because of the kill or because of
7824            a quit command sent by the backend; either way, wait for it to die.
7825         */
7826         wait((int *) 0);
7827     }
7828     close(cp->fdFrom);
7829     close(cp->fdTo);
7830 }
7831
7832 void
7833 InterruptChildProcess(pr)
7834      ProcRef pr;
7835 {
7836     ChildProc *cp = (ChildProc *) pr;
7837
7838     if (cp->kind != CPReal) return;
7839     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7840 }
7841
7842 int OpenTelnet(host, port, pr)
7843      char *host;
7844      char *port;
7845      ProcRef *pr;
7846 {
7847     char cmdLine[MSG_SIZ];
7848
7849     if (port[0] == NULLCHAR) {
7850       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7851     } else {
7852       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7853     }
7854     return StartChildProcess(cmdLine, "", pr);
7855 }
7856
7857 int OpenTCP(host, port, pr)
7858      char *host;
7859      char *port;
7860      ProcRef *pr;
7861 {
7862 #if OMIT_SOCKETS
7863     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7864 #else  /* !OMIT_SOCKETS */
7865     int s;
7866     struct sockaddr_in sa;
7867     struct hostent     *hp;
7868     unsigned short uport;
7869     ChildProc *cp;
7870
7871     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7872         return errno;
7873     }
7874
7875     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7876     sa.sin_family = AF_INET;
7877     sa.sin_addr.s_addr = INADDR_ANY;
7878     uport = (unsigned short) 0;
7879     sa.sin_port = htons(uport);
7880     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7881         return errno;
7882     }
7883
7884     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7885     if (!(hp = gethostbyname(host))) {
7886         int b0, b1, b2, b3;
7887         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7888             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7889             hp->h_addrtype = AF_INET;
7890             hp->h_length = 4;
7891             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7892             hp->h_addr_list[0] = (char *) malloc(4);
7893             hp->h_addr_list[0][0] = b0;
7894             hp->h_addr_list[0][1] = b1;
7895             hp->h_addr_list[0][2] = b2;
7896             hp->h_addr_list[0][3] = b3;
7897         } else {
7898             return ENOENT;
7899         }
7900     }
7901     sa.sin_family = hp->h_addrtype;
7902     uport = (unsigned short) atoi(port);
7903     sa.sin_port = htons(uport);
7904     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7905
7906     if (connect(s, (struct sockaddr *) &sa,
7907                 sizeof(struct sockaddr_in)) < 0) {
7908         return errno;
7909     }
7910
7911     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7912     cp->kind = CPSock;
7913     cp->pid = 0;
7914     cp->fdFrom = s;
7915     cp->fdTo = s;
7916     *pr = (ProcRef) cp;
7917
7918 #endif /* !OMIT_SOCKETS */
7919
7920     return 0;
7921 }
7922
7923 int OpenCommPort(name, pr)
7924      char *name;
7925      ProcRef *pr;
7926 {
7927     int fd;
7928     ChildProc *cp;
7929
7930     fd = open(name, 2, 0);
7931     if (fd < 0) return errno;
7932
7933     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7934     cp->kind = CPComm;
7935     cp->pid = 0;
7936     cp->fdFrom = fd;
7937     cp->fdTo = fd;
7938     *pr = (ProcRef) cp;
7939
7940     return 0;
7941 }
7942
7943 int OpenLoopback(pr)
7944      ProcRef *pr;
7945 {
7946     ChildProc *cp;
7947     int to[2], from[2];
7948
7949     SetUpChildIO(to, from);
7950
7951     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7952     cp->kind = CPLoop;
7953     cp->pid = 0;
7954     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7955     cp->fdTo = to[1];
7956     *pr = (ProcRef) cp;
7957
7958     return 0;
7959 }
7960
7961 int OpenRcmd(host, user, cmd, pr)
7962      char *host, *user, *cmd;
7963      ProcRef *pr;
7964 {
7965     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7966     return -1;
7967 }
7968
7969 #define INPUT_SOURCE_BUF_SIZE 8192
7970
7971 typedef struct {
7972     CPKind kind;
7973     int fd;
7974     int lineByLine;
7975     char *unused;
7976     InputCallback func;
7977     XtInputId xid;
7978     char buf[INPUT_SOURCE_BUF_SIZE];
7979     VOIDSTAR closure;
7980 } InputSource;
7981
7982 void
7983 DoInputCallback(closure, source, xid)
7984      caddr_t closure;
7985      int *source;
7986      XtInputId *xid;
7987 {
7988     InputSource *is = (InputSource *) closure;
7989     int count;
7990     int error;
7991     char *p, *q;
7992
7993     if (is->lineByLine) {
7994         count = read(is->fd, is->unused,
7995                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7996         if (count <= 0) {
7997             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7998             return;
7999         }
8000         is->unused += count;
8001         p = is->buf;
8002         while (p < is->unused) {
8003             q = memchr(p, '\n', is->unused - p);
8004             if (q == NULL) break;
8005             q++;
8006             (is->func)(is, is->closure, p, q - p, 0);
8007             p = q;
8008         }
8009         q = is->buf;
8010         while (p < is->unused) {
8011             *q++ = *p++;
8012         }
8013         is->unused = q;
8014     } else {
8015         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8016         if (count == -1)
8017           error = errno;
8018         else
8019           error = 0;
8020         (is->func)(is, is->closure, is->buf, count, error);
8021     }
8022 }
8023
8024 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8025      ProcRef pr;
8026      int lineByLine;
8027      InputCallback func;
8028      VOIDSTAR closure;
8029 {
8030     InputSource *is;
8031     ChildProc *cp = (ChildProc *) pr;
8032
8033     is = (InputSource *) calloc(1, sizeof(InputSource));
8034     is->lineByLine = lineByLine;
8035     is->func = func;
8036     if (pr == NoProc) {
8037         is->kind = CPReal;
8038         is->fd = fileno(stdin);
8039     } else {
8040         is->kind = cp->kind;
8041         is->fd = cp->fdFrom;
8042     }
8043     if (lineByLine) {
8044         is->unused = is->buf;
8045     }
8046
8047     is->xid = XtAppAddInput(appContext, is->fd,
8048                             (XtPointer) (XtInputReadMask),
8049                             (XtInputCallbackProc) DoInputCallback,
8050                             (XtPointer) is);
8051     is->closure = closure;
8052     return (InputSourceRef) is;
8053 }
8054
8055 void
8056 RemoveInputSource(isr)
8057      InputSourceRef isr;
8058 {
8059     InputSource *is = (InputSource *) isr;
8060
8061     if (is->xid == 0) return;
8062     XtRemoveInput(is->xid);
8063     is->xid = 0;
8064 }
8065
8066 int OutputToProcess(pr, message, count, outError)
8067      ProcRef pr;
8068      char *message;
8069      int count;
8070      int *outError;
8071 {
8072     static int line = 0;
8073     ChildProc *cp = (ChildProc *) pr;
8074     int outCount;
8075
8076     if (pr == NoProc)
8077     {
8078         if (appData.noJoin || !appData.useInternalWrap)
8079             outCount = fwrite(message, 1, count, stdout);
8080         else
8081         {
8082             int width = get_term_width();
8083             int len = wrap(NULL, message, count, width, &line);
8084             char *msg = malloc(len);
8085             int dbgchk;
8086
8087             if (!msg)
8088                 outCount = fwrite(message, 1, count, stdout);
8089             else
8090             {
8091                 dbgchk = wrap(msg, message, count, width, &line);
8092                 if (dbgchk != len && appData.debugMode)
8093                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8094                 outCount = fwrite(msg, 1, dbgchk, stdout);
8095                 free(msg);
8096             }
8097         }
8098     }
8099     else
8100       outCount = write(cp->fdTo, message, count);
8101
8102     if (outCount == -1)
8103       *outError = errno;
8104     else
8105       *outError = 0;
8106
8107     return outCount;
8108 }
8109
8110 /* Output message to process, with "ms" milliseconds of delay
8111    between each character. This is needed when sending the logon
8112    script to ICC, which for some reason doesn't like the
8113    instantaneous send. */
8114 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8115      ProcRef pr;
8116      char *message;
8117      int count;
8118      int *outError;
8119      long msdelay;
8120 {
8121     ChildProc *cp = (ChildProc *) pr;
8122     int outCount = 0;
8123     int r;
8124
8125     while (count--) {
8126         r = write(cp->fdTo, message++, 1);
8127         if (r == -1) {
8128             *outError = errno;
8129             return outCount;
8130         }
8131         ++outCount;
8132         if (msdelay >= 0)
8133           TimeDelay(msdelay);
8134     }
8135
8136     return outCount;
8137 }
8138
8139 /****   Animation code by Hugh Fisher, DCS, ANU.
8140
8141         Known problem: if a window overlapping the board is
8142         moved away while a piece is being animated underneath,
8143         the newly exposed area won't be updated properly.
8144         I can live with this.
8145
8146         Known problem: if you look carefully at the animation
8147         of pieces in mono mode, they are being drawn as solid
8148         shapes without interior detail while moving. Fixing
8149         this would be a major complication for minimal return.
8150 ****/
8151
8152 /*      Masks for XPM pieces. Black and white pieces can have
8153         different shapes, but in the interest of retaining my
8154         sanity pieces must have the same outline on both light
8155         and dark squares, and all pieces must use the same
8156         background square colors/images.                */
8157
8158 static int xpmDone = 0;
8159
8160 static void
8161 CreateAnimMasks (pieceDepth)
8162      int pieceDepth;
8163 {
8164   ChessSquare   piece;
8165   Pixmap        buf;
8166   GC            bufGC, maskGC;
8167   int           kind, n;
8168   unsigned long plane;
8169   XGCValues     values;
8170
8171   /* Need a bitmap just to get a GC with right depth */
8172   buf = XCreatePixmap(xDisplay, xBoardWindow,
8173                         8, 8, 1);
8174   values.foreground = 1;
8175   values.background = 0;
8176   /* Don't use XtGetGC, not read only */
8177   maskGC = XCreateGC(xDisplay, buf,
8178                     GCForeground | GCBackground, &values);
8179   XFreePixmap(xDisplay, buf);
8180
8181   buf = XCreatePixmap(xDisplay, xBoardWindow,
8182                       squareSize, squareSize, pieceDepth);
8183   values.foreground = XBlackPixel(xDisplay, xScreen);
8184   values.background = XWhitePixel(xDisplay, xScreen);
8185   bufGC = XCreateGC(xDisplay, buf,
8186                     GCForeground | GCBackground, &values);
8187
8188   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8189     /* Begin with empty mask */
8190     if(!xpmDone) // [HGM] pieces: keep using existing
8191     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8192                                  squareSize, squareSize, 1);
8193     XSetFunction(xDisplay, maskGC, GXclear);
8194     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8195                    0, 0, squareSize, squareSize);
8196
8197     /* Take a copy of the piece */
8198     if (White(piece))
8199       kind = 0;
8200     else
8201       kind = 2;
8202     XSetFunction(xDisplay, bufGC, GXcopy);
8203     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8204               buf, bufGC,
8205               0, 0, squareSize, squareSize, 0, 0);
8206
8207     /* XOR the background (light) over the piece */
8208     XSetFunction(xDisplay, bufGC, GXxor);
8209     if (useImageSqs)
8210       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8211                 0, 0, squareSize, squareSize, 0, 0);
8212     else {
8213       XSetForeground(xDisplay, bufGC, lightSquareColor);
8214       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8215     }
8216
8217     /* We now have an inverted piece image with the background
8218        erased. Construct mask by just selecting all the non-zero
8219        pixels - no need to reconstruct the original image.      */
8220     XSetFunction(xDisplay, maskGC, GXor);
8221     plane = 1;
8222     /* Might be quicker to download an XImage and create bitmap
8223        data from it rather than this N copies per piece, but it
8224        only takes a fraction of a second and there is a much
8225        longer delay for loading the pieces.             */
8226     for (n = 0; n < pieceDepth; n ++) {
8227       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8228                  0, 0, squareSize, squareSize,
8229                  0, 0, plane);
8230       plane = plane << 1;
8231     }
8232   }
8233   /* Clean up */
8234   XFreePixmap(xDisplay, buf);
8235   XFreeGC(xDisplay, bufGC);
8236   XFreeGC(xDisplay, maskGC);
8237 }
8238
8239 static void
8240 InitAnimState (anim, info)
8241   AnimState * anim;
8242   XWindowAttributes * info;
8243 {
8244   XtGCMask  mask;
8245   XGCValues values;
8246
8247   /* Each buffer is square size, same depth as window */
8248   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8249                         squareSize, squareSize, info->depth);
8250   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8251                         squareSize, squareSize, info->depth);
8252
8253   /* Create a plain GC for blitting */
8254   mask = GCForeground | GCBackground | GCFunction |
8255          GCPlaneMask | GCGraphicsExposures;
8256   values.foreground = XBlackPixel(xDisplay, xScreen);
8257   values.background = XWhitePixel(xDisplay, xScreen);
8258   values.function   = GXcopy;
8259   values.plane_mask = AllPlanes;
8260   values.graphics_exposures = False;
8261   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8262
8263   /* Piece will be copied from an existing context at
8264      the start of each new animation/drag. */
8265   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8266
8267   /* Outline will be a read-only copy of an existing */
8268   anim->outlineGC = None;
8269 }
8270
8271 static void
8272 CreateAnimVars ()
8273 {
8274   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8275   XWindowAttributes info;
8276
8277   if (xpmDone && gameInfo.variant == old) return;
8278   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8279   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8280
8281   InitAnimState(&game, &info);
8282   InitAnimState(&player, &info);
8283
8284   /* For XPM pieces, we need bitmaps to use as masks. */
8285   if (useImages)
8286     CreateAnimMasks(info.depth);
8287    xpmDone = 1;
8288 }
8289
8290 #ifndef HAVE_USLEEP
8291
8292 static Boolean frameWaiting;
8293
8294 static RETSIGTYPE FrameAlarm (sig)
8295      int sig;
8296 {
8297   frameWaiting = False;
8298   /* In case System-V style signals.  Needed?? */
8299   signal(SIGALRM, FrameAlarm);
8300 }
8301
8302 static void
8303 FrameDelay (time)
8304      int time;
8305 {
8306   struct itimerval delay;
8307
8308   XSync(xDisplay, False);
8309
8310   if (time > 0) {
8311     frameWaiting = True;
8312     signal(SIGALRM, FrameAlarm);
8313     delay.it_interval.tv_sec =
8314       delay.it_value.tv_sec = time / 1000;
8315     delay.it_interval.tv_usec =
8316       delay.it_value.tv_usec = (time % 1000) * 1000;
8317     setitimer(ITIMER_REAL, &delay, NULL);
8318     while (frameWaiting) pause();
8319     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8320     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8321     setitimer(ITIMER_REAL, &delay, NULL);
8322   }
8323 }
8324
8325 #else
8326
8327 static void
8328 FrameDelay (time)
8329      int time;
8330 {
8331   XSync(xDisplay, False);
8332   if (time > 0)
8333     usleep(time * 1000);
8334 }
8335
8336 #endif
8337
8338 /*      Convert board position to corner of screen rect and color       */
8339
8340 static void
8341 ScreenSquare(column, row, pt, color)
8342      int column; int row; XPoint * pt; int * color;
8343 {
8344   if (flipView) {
8345     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8346     pt->y = lineGap + row * (squareSize + lineGap);
8347   } else {
8348     pt->x = lineGap + column * (squareSize + lineGap);
8349     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8350   }
8351   *color = SquareColor(row, column);
8352 }
8353
8354 /*      Convert window coords to square                 */
8355
8356 static void
8357 BoardSquare(x, y, column, row)
8358      int x; int y; int * column; int * row;
8359 {
8360   *column = EventToSquare(x, BOARD_WIDTH);
8361   if (flipView && *column >= 0)
8362     *column = BOARD_WIDTH - 1 - *column;
8363   *row = EventToSquare(y, BOARD_HEIGHT);
8364   if (!flipView && *row >= 0)
8365     *row = BOARD_HEIGHT - 1 - *row;
8366 }
8367
8368 /*   Utilities  */
8369
8370 #undef Max  /* just in case */
8371 #undef Min
8372 #define Max(a, b) ((a) > (b) ? (a) : (b))
8373 #define Min(a, b) ((a) < (b) ? (a) : (b))
8374
8375 static void
8376 SetRect(rect, x, y, width, height)
8377      XRectangle * rect; int x; int y; int width; int height;
8378 {
8379   rect->x = x;
8380   rect->y = y;
8381   rect->width  = width;
8382   rect->height = height;
8383 }
8384
8385 /*      Test if two frames overlap. If they do, return
8386         intersection rect within old and location of
8387         that rect within new. */
8388
8389 static Boolean
8390 Intersect(old, new, size, area, pt)
8391      XPoint * old; XPoint * new;
8392      int size; XRectangle * area; XPoint * pt;
8393 {
8394   if (old->x > new->x + size || new->x > old->x + size ||
8395       old->y > new->y + size || new->y > old->y + size) {
8396     return False;
8397   } else {
8398     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8399             size - abs(old->x - new->x), size - abs(old->y - new->y));
8400     pt->x = Max(old->x - new->x, 0);
8401     pt->y = Max(old->y - new->y, 0);
8402     return True;
8403   }
8404 }
8405
8406 /*      For two overlapping frames, return the rect(s)
8407         in the old that do not intersect with the new.   */
8408
8409 static void
8410 CalcUpdateRects(old, new, size, update, nUpdates)
8411      XPoint * old; XPoint * new; int size;
8412      XRectangle update[]; int * nUpdates;
8413 {
8414   int        count;
8415
8416   /* If old = new (shouldn't happen) then nothing to draw */
8417   if (old->x == new->x && old->y == new->y) {
8418     *nUpdates = 0;
8419     return;
8420   }
8421   /* Work out what bits overlap. Since we know the rects
8422      are the same size we don't need a full intersect calc. */
8423   count = 0;
8424   /* Top or bottom edge? */
8425   if (new->y > old->y) {
8426     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8427     count ++;
8428   } else if (old->y > new->y) {
8429     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8430                               size, old->y - new->y);
8431     count ++;
8432   }
8433   /* Left or right edge - don't overlap any update calculated above. */
8434   if (new->x > old->x) {
8435     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8436                               new->x - old->x, size - abs(new->y - old->y));
8437     count ++;
8438   } else if (old->x > new->x) {
8439     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8440                               old->x - new->x, size - abs(new->y - old->y));
8441     count ++;
8442   }
8443   /* Done */
8444   *nUpdates = count;
8445 }
8446
8447 /*      Generate a series of frame coords from start->mid->finish.
8448         The movement rate doubles until the half way point is
8449         reached, then halves back down to the final destination,
8450         which gives a nice slow in/out effect. The algorithmn
8451         may seem to generate too many intermediates for short
8452         moves, but remember that the purpose is to attract the
8453         viewers attention to the piece about to be moved and
8454         then to where it ends up. Too few frames would be less
8455         noticeable.                                             */
8456
8457 static void
8458 Tween(start, mid, finish, factor, frames, nFrames)
8459      XPoint * start; XPoint * mid;
8460      XPoint * finish; int factor;
8461      XPoint frames[]; int * nFrames;
8462 {
8463   int fraction, n, count;
8464
8465   count = 0;
8466
8467   /* Slow in, stepping 1/16th, then 1/8th, ... */
8468   fraction = 1;
8469   for (n = 0; n < factor; n++)
8470     fraction *= 2;
8471   for (n = 0; n < factor; n++) {
8472     frames[count].x = start->x + (mid->x - start->x) / fraction;
8473     frames[count].y = start->y + (mid->y - start->y) / fraction;
8474     count ++;
8475     fraction = fraction / 2;
8476   }
8477
8478   /* Midpoint */
8479   frames[count] = *mid;
8480   count ++;
8481
8482   /* Slow out, stepping 1/2, then 1/4, ... */
8483   fraction = 2;
8484   for (n = 0; n < factor; n++) {
8485     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8486     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8487     count ++;
8488     fraction = fraction * 2;
8489   }
8490   *nFrames = count;
8491 }
8492
8493 /*      Draw a piece on the screen without disturbing what's there      */
8494
8495 static void
8496 SelectGCMask(piece, clip, outline, mask)
8497      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8498 {
8499   GC source;
8500
8501   /* Bitmap for piece being moved. */
8502   if (appData.monoMode) {
8503       *mask = *pieceToSolid(piece);
8504   } else if (useImages) {
8505 #if HAVE_LIBXPM
8506       *mask = xpmMask[piece];
8507 #else
8508       *mask = ximMaskPm[piece];
8509 #endif
8510   } else {
8511       *mask = *pieceToSolid(piece);
8512   }
8513
8514   /* GC for piece being moved. Square color doesn't matter, but
8515      since it gets modified we make a copy of the original. */
8516   if (White(piece)) {
8517     if (appData.monoMode)
8518       source = bwPieceGC;
8519     else
8520       source = wlPieceGC;
8521   } else {
8522     if (appData.monoMode)
8523       source = wbPieceGC;
8524     else
8525       source = blPieceGC;
8526   }
8527   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8528
8529   /* Outline only used in mono mode and is not modified */
8530   if (White(piece))
8531     *outline = bwPieceGC;
8532   else
8533     *outline = wbPieceGC;
8534 }
8535
8536 static void
8537 OverlayPiece(piece, clip, outline,  dest)
8538      ChessSquare piece; GC clip; GC outline; Drawable dest;
8539 {
8540   int   kind;
8541
8542   if (!useImages) {
8543     /* Draw solid rectangle which will be clipped to shape of piece */
8544     XFillRectangle(xDisplay, dest, clip,
8545                    0, 0, squareSize, squareSize);
8546     if (appData.monoMode)
8547       /* Also draw outline in contrasting color for black
8548          on black / white on white cases                */
8549       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8550                  0, 0, squareSize, squareSize, 0, 0, 1);
8551   } else {
8552     /* Copy the piece */
8553     if (White(piece))
8554       kind = 0;
8555     else
8556       kind = 2;
8557     if(appData.upsideDown && flipView) kind ^= 2;
8558     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8559               dest, clip,
8560               0, 0, squareSize, squareSize,
8561               0, 0);
8562   }
8563 }
8564
8565 /* Animate the movement of a single piece */
8566
8567 static void
8568 BeginAnimation(anim, piece, startColor, start)
8569      AnimState *anim;
8570      ChessSquare piece;
8571      int startColor;
8572      XPoint * start;
8573 {
8574   Pixmap mask;
8575
8576   if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8577   /* The old buffer is initialised with the start square (empty) */
8578   BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8579   anim->prevFrame = *start;
8580
8581   /* The piece will be drawn using its own bitmap as a matte    */
8582   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8583   XSetClipMask(xDisplay, anim->pieceGC, mask);
8584 }
8585
8586 static void
8587 AnimationFrame(anim, frame, piece)
8588      AnimState *anim;
8589      XPoint *frame;
8590      ChessSquare piece;
8591 {
8592   XRectangle updates[4];
8593   XRectangle overlap;
8594   XPoint     pt;
8595   int        count, i;
8596
8597   /* Save what we are about to draw into the new buffer */
8598   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8599             frame->x, frame->y, squareSize, squareSize,
8600             0, 0);
8601
8602   /* Erase bits of the previous frame */
8603   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8604     /* Where the new frame overlapped the previous,
8605        the contents in newBuf are wrong. */
8606     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8607               overlap.x, overlap.y,
8608               overlap.width, overlap.height,
8609               pt.x, pt.y);
8610     /* Repaint the areas in the old that don't overlap new */
8611     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8612     for (i = 0; i < count; i++)
8613       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8614                 updates[i].x - anim->prevFrame.x,
8615                 updates[i].y - anim->prevFrame.y,
8616                 updates[i].width, updates[i].height,
8617                 updates[i].x, updates[i].y);
8618   } else {
8619     /* Easy when no overlap */
8620     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8621                   0, 0, squareSize, squareSize,
8622                   anim->prevFrame.x, anim->prevFrame.y);
8623   }
8624
8625   /* Save this frame for next time round */
8626   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8627                 0, 0, squareSize, squareSize,
8628                 0, 0);
8629   anim->prevFrame = *frame;
8630
8631   /* Draw piece over original screen contents, not current,
8632      and copy entire rect. Wipes out overlapping piece images. */
8633   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8634   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8635                 0, 0, squareSize, squareSize,
8636                 frame->x, frame->y);
8637 }
8638
8639 static void
8640 EndAnimation (anim, finish)
8641      AnimState *anim;
8642      XPoint *finish;
8643 {
8644   XRectangle updates[4];
8645   XRectangle overlap;
8646   XPoint     pt;
8647   int        count, i;
8648
8649   /* The main code will redraw the final square, so we
8650      only need to erase the bits that don't overlap.    */
8651   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8652     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8653     for (i = 0; i < count; i++)
8654       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8655                 updates[i].x - anim->prevFrame.x,
8656                 updates[i].y - anim->prevFrame.y,
8657                 updates[i].width, updates[i].height,
8658                 updates[i].x, updates[i].y);
8659   } else {
8660     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8661                 0, 0, squareSize, squareSize,
8662                 anim->prevFrame.x, anim->prevFrame.y);
8663   }
8664 }
8665
8666 static void
8667 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8668      AnimState *anim;
8669      ChessSquare piece; int startColor;
8670      XPoint * start; XPoint * finish;
8671      XPoint frames[]; int nFrames;
8672 {
8673   int n;
8674
8675   BeginAnimation(anim, piece, startColor, start);
8676   for (n = 0; n < nFrames; n++) {
8677     AnimationFrame(anim, &(frames[n]), piece);
8678     FrameDelay(appData.animSpeed);
8679   }
8680   EndAnimation(anim, finish);
8681 }
8682
8683 void
8684 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8685 {
8686     int i, x, y;
8687     ChessSquare piece = board[fromY][toY];
8688     board[fromY][toY] = EmptySquare;
8689     DrawPosition(FALSE, board);
8690     if (flipView) {
8691         x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8692         y = lineGap + toY * (squareSize + lineGap);
8693     } else {
8694         x = lineGap + toX * (squareSize + lineGap);
8695         y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8696     }
8697     for(i=1; i<4*kFactor; i++) {
8698         int r = squareSize * 9 * i/(20*kFactor - 5);
8699         XFillArc(xDisplay, xBoardWindow, highlineGC,
8700                 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8701         FrameDelay(appData.animSpeed);
8702     }
8703     board[fromY][toY] = piece;
8704 }
8705
8706 /* Main control logic for deciding what to animate and how */
8707
8708 void
8709 AnimateMove(board, fromX, fromY, toX, toY)
8710      Board board;
8711      int fromX;
8712      int fromY;
8713      int toX;
8714      int toY;
8715 {
8716   ChessSquare piece;
8717   int hop;
8718   XPoint      start, finish, mid;
8719   XPoint      frames[kFactor * 2 + 1];
8720   int         nFrames, startColor, endColor;
8721
8722   /* Are we animating? */
8723   if (!appData.animate || appData.blindfold)
8724     return;
8725
8726   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8727      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8728         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8729
8730   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8731   piece = board[fromY][fromX];
8732   if (piece >= EmptySquare) return;
8733
8734 #if DONT_HOP
8735   hop = FALSE;
8736 #else
8737   hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8738 #endif
8739
8740   if (appData.debugMode) {
8741       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8742                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8743              piece, fromX, fromY, toX, toY);  }
8744
8745   ScreenSquare(fromX, fromY, &start, &startColor);
8746   ScreenSquare(toX, toY, &finish, &endColor);
8747
8748   if (hop) {
8749     /* Knight: make straight movement then diagonal */
8750     if (abs(toY - fromY) < abs(toX - fromX)) {
8751        mid.x = start.x + (finish.x - start.x) / 2;
8752        mid.y = start.y;
8753      } else {
8754        mid.x = start.x;
8755        mid.y = start.y + (finish.y - start.y) / 2;
8756      }
8757   } else {
8758     mid.x = start.x + (finish.x - start.x) / 2;
8759     mid.y = start.y + (finish.y - start.y) / 2;
8760   }
8761
8762   /* Don't use as many frames for very short moves */
8763   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8764     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8765   else
8766     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8767   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8768   if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8769     int i,j;
8770     for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8771       if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8772   }
8773
8774   /* Be sure end square is redrawn */
8775   damage[0][toY][toX] = True;
8776 }
8777
8778 void
8779 DragPieceBegin(x, y)
8780      int x; int y;
8781 {
8782     int  boardX, boardY, color;
8783     XPoint corner;
8784
8785     /* Are we animating? */
8786     if (!appData.animateDragging || appData.blindfold)
8787       return;
8788
8789     /* Figure out which square we start in and the
8790        mouse position relative to top left corner. */
8791     BoardSquare(x, y, &boardX, &boardY);
8792     player.startBoardX = boardX;
8793     player.startBoardY = boardY;
8794     ScreenSquare(boardX, boardY, &corner, &color);
8795     player.startSquare  = corner;
8796     player.startColor   = color;
8797     /* As soon as we start dragging, the piece will jump slightly to
8798        be centered over the mouse pointer. */
8799     player.mouseDelta.x = squareSize/2;
8800     player.mouseDelta.y = squareSize/2;
8801     /* Initialise animation */
8802     player.dragPiece = PieceForSquare(boardX, boardY);
8803     /* Sanity check */
8804     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8805         player.dragActive = True;
8806         BeginAnimation(&player, player.dragPiece, color, &corner);
8807         /* Mark this square as needing to be redrawn. Note that
8808            we don't remove the piece though, since logically (ie
8809            as seen by opponent) the move hasn't been made yet. */
8810            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8811               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8812            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8813                      corner.x, corner.y, squareSize, squareSize,
8814                      0, 0); // [HGM] zh: unstack in stead of grab
8815            if(gatingPiece != EmptySquare) {
8816                /* Kludge alert: When gating we want the introduced
8817                   piece to appear on the from square. To generate an
8818                   image of it, we draw it on the board, copy the image,
8819                   and draw the original piece again. */
8820                ChessSquare piece = boards[currentMove][boardY][boardX];
8821                DrawSquare(boardY, boardX, gatingPiece, 0);
8822                XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8823                      corner.x, corner.y, squareSize, squareSize, 0, 0);
8824                DrawSquare(boardY, boardX, piece, 0);
8825            }
8826         damage[0][boardY][boardX] = True;
8827     } else {
8828         player.dragActive = False;
8829     }
8830 }
8831
8832 static void
8833 DragPieceMove(x, y)
8834      int x; int y;
8835 {
8836     XPoint corner;
8837
8838     /* Are we animating? */
8839     if (!appData.animateDragging || appData.blindfold)
8840       return;
8841
8842     /* Sanity check */
8843     if (! player.dragActive)
8844       return;
8845     /* Move piece, maintaining same relative position
8846        of mouse within square    */
8847     corner.x = x - player.mouseDelta.x;
8848     corner.y = y - player.mouseDelta.y;
8849     AnimationFrame(&player, &corner, player.dragPiece);
8850 #if HIGHDRAG*0
8851     if (appData.highlightDragging) {
8852         int boardX, boardY;
8853         BoardSquare(x, y, &boardX, &boardY);
8854         SetHighlights(fromX, fromY, boardX, boardY);
8855     }
8856 #endif
8857 }
8858
8859 void
8860 DragPieceEnd(x, y)
8861      int x; int y;
8862 {
8863     int boardX, boardY, color;
8864     XPoint corner;
8865
8866     /* Are we animating? */
8867     if (!appData.animateDragging || appData.blindfold)
8868       return;
8869
8870     /* Sanity check */
8871     if (! player.dragActive)
8872       return;
8873     /* Last frame in sequence is square piece is
8874        placed on, which may not match mouse exactly. */
8875     BoardSquare(x, y, &boardX, &boardY);
8876     ScreenSquare(boardX, boardY, &corner, &color);
8877     EndAnimation(&player, &corner);
8878
8879     /* Be sure end square is redrawn */
8880     damage[0][boardY][boardX] = True;
8881
8882     /* This prevents weird things happening with fast successive
8883        clicks which on my Sun at least can cause motion events
8884        without corresponding press/release. */
8885     player.dragActive = False;
8886 }
8887
8888 /* Handle expose event while piece being dragged */
8889
8890 static void
8891 DrawDragPiece ()
8892 {
8893   if (!player.dragActive || appData.blindfold)
8894     return;
8895
8896   /* What we're doing: logically, the move hasn't been made yet,
8897      so the piece is still in it's original square. But visually
8898      it's being dragged around the board. So we erase the square
8899      that the piece is on and draw it at the last known drag point. */
8900   BlankSquare(player.startSquare.x, player.startSquare.y,
8901                 player.startColor, EmptySquare, xBoardWindow, 1);
8902   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8903   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8904 }
8905
8906 #include <sys/ioctl.h>
8907 int get_term_width()
8908 {
8909     int fd, default_width;
8910
8911     fd = STDIN_FILENO;
8912     default_width = 79; // this is FICS default anyway...
8913
8914 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8915     struct ttysize win;
8916     if (!ioctl(fd, TIOCGSIZE, &win))
8917         default_width = win.ts_cols;
8918 #elif defined(TIOCGWINSZ)
8919     struct winsize win;
8920     if (!ioctl(fd, TIOCGWINSZ, &win))
8921         default_width = win.ws_col;
8922 #endif
8923     return default_width;
8924 }
8925
8926 void
8927 update_ics_width()
8928 {
8929   static int old_width = 0;
8930   int new_width = get_term_width();
8931
8932   if (old_width != new_width)
8933     ics_printf("set width %d\n", new_width);
8934   old_width = new_width;
8935 }
8936
8937 void NotifyFrontendLogin()
8938 {
8939     update_ics_width();
8940 }
8941
8942 /* [AS] Arrow highlighting support */
8943
8944 static double A_WIDTH = 5; /* Width of arrow body */
8945
8946 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
8947 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
8948
8949 static double Sqr( double x )
8950 {
8951     return x*x;
8952 }
8953
8954 static int Round( double x )
8955 {
8956     return (int) (x + 0.5);
8957 }
8958
8959 void SquareToPos(int rank, int file, int *x, int *y)
8960 {
8961     if (flipView) {
8962         *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8963         *y = lineGap + rank * (squareSize + lineGap);
8964     } else {
8965         *x = lineGap + file * (squareSize + lineGap);
8966         *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8967     }
8968 }
8969
8970 /* Draw an arrow between two points using current settings */
8971 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8972 {
8973     XPoint arrow[7];
8974     double dx, dy, j, k, x, y;
8975
8976     if( d_x == s_x ) {
8977         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8978
8979         arrow[0].x = s_x + A_WIDTH + 0.5;
8980         arrow[0].y = s_y;
8981
8982         arrow[1].x = s_x + A_WIDTH + 0.5;
8983         arrow[1].y = d_y - h;
8984
8985         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8986         arrow[2].y = d_y - h;
8987
8988         arrow[3].x = d_x;
8989         arrow[3].y = d_y;
8990
8991         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8992         arrow[5].y = d_y - h;
8993
8994         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8995         arrow[4].y = d_y - h;
8996
8997         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8998         arrow[6].y = s_y;
8999     }
9000     else if( d_y == s_y ) {
9001         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
9002
9003         arrow[0].x = s_x;
9004         arrow[0].y = s_y + A_WIDTH + 0.5;
9005
9006         arrow[1].x = d_x - w;
9007         arrow[1].y = s_y + A_WIDTH + 0.5;
9008
9009         arrow[2].x = d_x - w;
9010         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9011
9012         arrow[3].x = d_x;
9013         arrow[3].y = d_y;
9014
9015         arrow[5].x = d_x - w;
9016         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
9017
9018         arrow[4].x = d_x - w;
9019         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
9020
9021         arrow[6].x = s_x;
9022         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
9023     }
9024     else {
9025         /* [AS] Needed a lot of paper for this! :-) */
9026         dy = (double) (d_y - s_y) / (double) (d_x - s_x);
9027         dx = (double) (s_x - d_x) / (double) (s_y - d_y);
9028
9029         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
9030
9031         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
9032
9033         x = s_x;
9034         y = s_y;
9035
9036         arrow[0].x = Round(x - j);
9037         arrow[0].y = Round(y + j*dx);
9038
9039         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
9040         arrow[1].y = Round(arrow[0].y - 2*j*dx);
9041
9042         if( d_x > s_x ) {
9043             x = (double) d_x - k;
9044             y = (double) d_y - k*dy;
9045         }
9046         else {
9047             x = (double) d_x + k;
9048             y = (double) d_y + k*dy;
9049         }
9050
9051         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
9052
9053         arrow[6].x = Round(x - j);
9054         arrow[6].y = Round(y + j*dx);
9055
9056         arrow[2].x = Round(arrow[6].x + 2*j);
9057         arrow[2].y = Round(arrow[6].y - 2*j*dx);
9058
9059         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
9060         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
9061
9062         arrow[4].x = d_x;
9063         arrow[4].y = d_y;
9064
9065         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9066         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9067     }
9068
9069     XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9070 //    Polygon( hdc, arrow, 7 );
9071 }
9072
9073 /* [AS] Draw an arrow between two squares */
9074 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9075 {
9076     int s_x, s_y, d_x, d_y, hor, vert, i;
9077
9078     if( s_col == d_col && s_row == d_row ) {
9079         return;
9080     }
9081
9082     /* Get source and destination points */
9083     SquareToPos( s_row, s_col, &s_x, &s_y);
9084     SquareToPos( d_row, d_col, &d_x, &d_y);
9085
9086     if( d_y > s_y ) {
9087         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9088     }
9089     else if( d_y < s_y ) {
9090         d_y += squareSize / 2 + squareSize / 4;
9091     }
9092     else {
9093         d_y += squareSize / 2;
9094     }
9095
9096     if( d_x > s_x ) {
9097         d_x += squareSize / 2 - squareSize / 4;
9098     }
9099     else if( d_x < s_x ) {
9100         d_x += squareSize / 2 + squareSize / 4;
9101     }
9102     else {
9103         d_x += squareSize / 2;
9104     }
9105
9106     s_x += squareSize / 2;
9107     s_y += squareSize / 2;
9108
9109     /* Adjust width */
9110     A_WIDTH = squareSize / 14.; //[HGM] make float
9111
9112     DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9113
9114     hor = 64*s_col + 32; vert = 64*s_row + 32;
9115     for(i=0; i<= 64; i++) {
9116             damage[0][vert+6>>6][hor+6>>6] = True;
9117             damage[0][vert-6>>6][hor+6>>6] = True;
9118             damage[0][vert+6>>6][hor-6>>6] = True;
9119             damage[0][vert-6>>6][hor-6>>6] = True;
9120             hor += d_col - s_col; vert += d_row - s_row;
9121     }
9122 }
9123
9124 Boolean IsDrawArrowEnabled()
9125 {
9126     return appData.highlightMoveWithArrow && squareSize >= 32;
9127 }
9128
9129 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9130 {
9131     if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9132         DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9133 }