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