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