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