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