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