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