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