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