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