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