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