Make GenericPopUp() more readable by using more named flags instead of numerals
[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     *format_return = 8 * sizeof(Atom);
5647     if (*format_return > 32) {
5648       *length_return *= *format_return / 32;
5649       *format_return = 32;
5650     }
5651     return True;
5652   } else {
5653     return False;
5654   }
5655 }
5656
5657 /* note: when called from menu all parameters are NULL, so no clue what the
5658  * Widget which was clicked on was, or what the click event was
5659  */
5660 void CopyPositionProc(w, event, prms, nprms)
5661   Widget w;
5662   XEvent *event;
5663   String *prms;
5664   Cardinal *nprms;
5665   {
5666     /*
5667      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5668      * have a notion of a position that is selected but not copied.
5669      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5670      */
5671     if(gameMode == EditPosition) EditPositionDone(TRUE);
5672     if (selected_fen_position) free(selected_fen_position);
5673     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5674     if (!selected_fen_position) return;
5675     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5676                    CurrentTime,
5677                    SendPositionSelection,
5678                    NULL/* lose_ownership_proc */ ,
5679                    NULL/* transfer_done_proc */);
5680     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5681                    CurrentTime,
5682                    SendPositionSelection,
5683                    NULL/* lose_ownership_proc */ ,
5684                    NULL/* transfer_done_proc */);
5685   }
5686
5687 /* function called when the data to Paste is ready */
5688 static void
5689 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5690            Atom *type, XtPointer value, unsigned long *len, int *format)
5691 {
5692   char *fenstr=value;
5693   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5694   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5695   EditPositionPasteFEN(fenstr);
5696   XtFree(value);
5697 }
5698
5699 /* called when Paste Position button is pressed,
5700  * all parameters will be NULL */
5701 void PastePositionProc(w, event, prms, nprms)
5702   Widget w;
5703   XEvent *event;
5704   String *prms;
5705   Cardinal *nprms;
5706 {
5707     XtGetSelectionValue(menuBarWidget,
5708       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5709       /* (XtSelectionCallbackProc) */ PastePositionCB,
5710       NULL, /* client_data passed to PastePositionCB */
5711
5712       /* better to use the time field from the event that triggered the
5713        * call to this function, but that isn't trivial to get
5714        */
5715       CurrentTime
5716     );
5717     return;
5718 }
5719
5720 static Boolean
5721 SendGameSelection(Widget w, Atom *selection, Atom *target,
5722                   Atom *type_return, XtPointer *value_return,
5723                   unsigned long *length_return, int *format_return)
5724 {
5725   char *selection_tmp;
5726
5727   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5728     FILE* f = fopen(gameCopyFilename, "r");
5729     long len;
5730     size_t count;
5731     if (f == NULL) return False;
5732     fseek(f, 0, 2);
5733     len = ftell(f);
5734     rewind(f);
5735     selection_tmp = XtMalloc(len + 1);
5736     count = fread(selection_tmp, 1, len, f);
5737     fclose(f);
5738     if (len != count) {
5739       XtFree(selection_tmp);
5740       return False;
5741     }
5742     selection_tmp[len] = NULLCHAR;
5743     *value_return = selection_tmp;
5744     *length_return = len;
5745     *type_return = *target;
5746     *format_return = 8; /* bits per byte */
5747     return True;
5748   } else if (*target == XA_TARGETS(xDisplay)) {
5749     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5750     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5751     targets_tmp[1] = XA_STRING;
5752     *value_return = targets_tmp;
5753     *type_return = XA_ATOM;
5754     *length_return = 2;
5755     *format_return = 8 * sizeof(Atom);
5756     if (*format_return > 32) {
5757       *length_return *= *format_return / 32;
5758       *format_return = 32;
5759     }
5760     return True;
5761   } else {
5762     return False;
5763   }
5764 }
5765
5766 void CopySomething()
5767 {
5768   /*
5769    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5770    * have a notion of a game that is selected but not copied.
5771    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5772    */
5773   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5774                  CurrentTime,
5775                  SendGameSelection,
5776                  NULL/* lose_ownership_proc */ ,
5777                  NULL/* transfer_done_proc */);
5778   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5779                  CurrentTime,
5780                  SendGameSelection,
5781                  NULL/* lose_ownership_proc */ ,
5782                  NULL/* transfer_done_proc */);
5783 }
5784
5785 /* note: when called from menu all parameters are NULL, so no clue what the
5786  * Widget which was clicked on was, or what the click event was
5787  */
5788 void CopyGameProc(w, event, prms, nprms)
5789   Widget w;
5790   XEvent *event;
5791   String *prms;
5792   Cardinal *nprms;
5793 {
5794   int ret;
5795
5796   ret = SaveGameToFile(gameCopyFilename, FALSE);
5797   if (!ret) return;
5798
5799   CopySomething();
5800 }
5801
5802 void CopyGameListProc(w, event, prms, nprms)
5803   Widget w;
5804   XEvent *event;
5805   String *prms;
5806   Cardinal *nprms;
5807 {
5808   if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
5809   CopySomething();
5810 }
5811
5812 /* function called when the data to Paste is ready */
5813 static void
5814 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5815             Atom *type, XtPointer value, unsigned long *len, int *format)
5816 {
5817   FILE* f;
5818   if (value == NULL || *len == 0) {
5819     return; /* nothing had been selected to copy */
5820   }
5821   f = fopen(gamePasteFilename, "w");
5822   if (f == NULL) {
5823     DisplayError(_("Can't open temp file"), errno);
5824     return;
5825   }
5826   fwrite(value, 1, *len, f);
5827   fclose(f);
5828   XtFree(value);
5829   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5830 }
5831
5832 /* called when Paste Game button is pressed,
5833  * all parameters will be NULL */
5834 void PasteGameProc(w, event, prms, nprms)
5835   Widget w;
5836   XEvent *event;
5837   String *prms;
5838   Cardinal *nprms;
5839 {
5840     XtGetSelectionValue(menuBarWidget,
5841       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5842       /* (XtSelectionCallbackProc) */ PasteGameCB,
5843       NULL, /* client_data passed to PasteGameCB */
5844
5845       /* better to use the time field from the event that triggered the
5846        * call to this function, but that isn't trivial to get
5847        */
5848       CurrentTime
5849     );
5850     return;
5851 }
5852
5853
5854 void AutoSaveGame()
5855 {
5856     SaveGameProc(NULL, NULL, NULL, NULL);
5857 }
5858
5859
5860 void QuitProc(w, event, prms, nprms)
5861      Widget w;
5862      XEvent *event;
5863      String *prms;
5864      Cardinal *nprms;
5865 {
5866     ExitEvent(0);
5867 }
5868
5869 void PauseProc(w, event, prms, nprms)
5870      Widget w;
5871      XEvent *event;
5872      String *prms;
5873      Cardinal *nprms;
5874 {
5875     PauseEvent();
5876 }
5877
5878
5879 void MachineBlackProc(w, event, prms, nprms)
5880      Widget w;
5881      XEvent *event;
5882      String *prms;
5883      Cardinal *nprms;
5884 {
5885     MachineBlackEvent();
5886 }
5887
5888 void MachineWhiteProc(w, event, prms, nprms)
5889      Widget w;
5890      XEvent *event;
5891      String *prms;
5892      Cardinal *nprms;
5893 {
5894     MachineWhiteEvent();
5895 }
5896
5897 void AnalyzeModeProc(w, event, prms, nprms)
5898      Widget w;
5899      XEvent *event;
5900      String *prms;
5901      Cardinal *nprms;
5902 {
5903     char buf[MSG_SIZ];
5904
5905     if (!first.analysisSupport) {
5906       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5907       DisplayError(buf, 0);
5908       return;
5909     }
5910     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
5911     if (appData.icsActive) {
5912         if (gameMode != IcsObserving) {
5913           snprintf(buf, MSG_SIZ, _("You are not observing a game"));
5914             DisplayError(buf, 0);
5915             /* secure check */
5916             if (appData.icsEngineAnalyze) {
5917                 if (appData.debugMode)
5918                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
5919                 ExitAnalyzeMode();
5920                 ModeHighlight();
5921             }
5922             return;
5923         }
5924         /* if enable, use want disable icsEngineAnalyze */
5925         if (appData.icsEngineAnalyze) {
5926                 ExitAnalyzeMode();
5927                 ModeHighlight();
5928                 return;
5929         }
5930         appData.icsEngineAnalyze = TRUE;
5931         if (appData.debugMode)
5932             fprintf(debugFP, _("ICS engine analyze starting... \n"));
5933     }
5934 #ifndef OPTIONSDIALOG
5935     if (!appData.showThinking)
5936       ShowThinkingProc(w,event,prms,nprms);
5937 #endif
5938
5939     AnalyzeModeEvent();
5940 }
5941
5942 void AnalyzeFileProc(w, event, prms, nprms)
5943      Widget w;
5944      XEvent *event;
5945      String *prms;
5946      Cardinal *nprms;
5947 {
5948     if (!first.analysisSupport) {
5949       char buf[MSG_SIZ];
5950       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
5951       DisplayError(buf, 0);
5952       return;
5953     }
5954 //    Reset(FALSE, TRUE);
5955 #ifndef OPTIONSDIALOG
5956     if (!appData.showThinking)
5957       ShowThinkingProc(w,event,prms,nprms);
5958 #endif
5959     AnalyzeFileEvent();
5960 //    FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
5961     AnalysisPeriodicEvent(1);
5962 }
5963
5964 void TwoMachinesProc(w, event, prms, nprms)
5965      Widget w;
5966      XEvent *event;
5967      String *prms;
5968      Cardinal *nprms;
5969 {
5970     TwoMachinesEvent();
5971 }
5972
5973 void MatchProc(w, event, prms, nprms)
5974      Widget w;
5975      XEvent *event;
5976      String *prms;
5977      Cardinal *nprms;
5978 {
5979     MatchEvent(2);
5980 }
5981
5982 void IcsClientProc(w, event, prms, nprms)
5983      Widget w;
5984      XEvent *event;
5985      String *prms;
5986      Cardinal *nprms;
5987 {
5988     IcsClientEvent();
5989 }
5990
5991 void EditGameProc(w, event, prms, nprms)
5992      Widget w;
5993      XEvent *event;
5994      String *prms;
5995      Cardinal *nprms;
5996 {
5997     EditGameEvent();
5998 }
5999
6000 void EditPositionProc(w, event, prms, nprms)
6001      Widget w;
6002      XEvent *event;
6003      String *prms;
6004      Cardinal *nprms;
6005 {
6006     EditPositionEvent();
6007 }
6008
6009 void TrainingProc(w, event, prms, nprms)
6010      Widget w;
6011      XEvent *event;
6012      String *prms;
6013      Cardinal *nprms;
6014 {
6015     TrainingEvent();
6016 }
6017
6018 void EditCommentProc(w, event, prms, nprms)
6019      Widget w;
6020      XEvent *event;
6021      String *prms;
6022      Cardinal *nprms;
6023 {
6024     Arg args[5];
6025     int j;
6026     if (PopDown(1)) { // popdown succesful
6027         j = 0;
6028         XtSetArg(args[j], XtNleftBitmap, None); j++;
6029         XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
6030         XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
6031     } else // was not up
6032         EditCommentEvent();
6033 }
6034
6035 void IcsInputBoxProc(w, event, prms, nprms)
6036      Widget w;
6037      XEvent *event;
6038      String *prms;
6039      Cardinal *nprms;
6040 {
6041     if (!PopDown(4)) ICSInputBoxPopUp();
6042 }
6043
6044 void AcceptProc(w, event, prms, nprms)
6045      Widget w;
6046      XEvent *event;
6047      String *prms;
6048      Cardinal *nprms;
6049 {
6050     AcceptEvent();
6051 }
6052
6053 void DeclineProc(w, event, prms, nprms)
6054      Widget w;
6055      XEvent *event;
6056      String *prms;
6057      Cardinal *nprms;
6058 {
6059     DeclineEvent();
6060 }
6061
6062 void RematchProc(w, event, prms, nprms)
6063      Widget w;
6064      XEvent *event;
6065      String *prms;
6066      Cardinal *nprms;
6067 {
6068     RematchEvent();
6069 }
6070
6071 void CallFlagProc(w, event, prms, nprms)
6072      Widget w;
6073      XEvent *event;
6074      String *prms;
6075      Cardinal *nprms;
6076 {
6077     CallFlagEvent();
6078 }
6079
6080 void DrawProc(w, event, prms, nprms)
6081      Widget w;
6082      XEvent *event;
6083      String *prms;
6084      Cardinal *nprms;
6085 {
6086     DrawEvent();
6087 }
6088
6089 void AbortProc(w, event, prms, nprms)
6090      Widget w;
6091      XEvent *event;
6092      String *prms;
6093      Cardinal *nprms;
6094 {
6095     AbortEvent();
6096 }
6097
6098 void AdjournProc(w, event, prms, nprms)
6099      Widget w;
6100      XEvent *event;
6101      String *prms;
6102      Cardinal *nprms;
6103 {
6104     AdjournEvent();
6105 }
6106
6107 void ResignProc(w, event, prms, nprms)
6108      Widget w;
6109      XEvent *event;
6110      String *prms;
6111      Cardinal *nprms;
6112 {
6113     ResignEvent();
6114 }
6115
6116 void AdjuWhiteProc(w, event, prms, nprms)
6117      Widget w;
6118      XEvent *event;
6119      String *prms;
6120      Cardinal *nprms;
6121 {
6122     UserAdjudicationEvent(+1);
6123 }
6124
6125 void AdjuBlackProc(w, event, prms, nprms)
6126      Widget w;
6127      XEvent *event;
6128      String *prms;
6129      Cardinal *nprms;
6130 {
6131     UserAdjudicationEvent(-1);
6132 }
6133
6134 void AdjuDrawProc(w, event, prms, nprms)
6135      Widget w;
6136      XEvent *event;
6137      String *prms;
6138      Cardinal *nprms;
6139 {
6140     UserAdjudicationEvent(0);
6141 }
6142
6143 void EnterKeyProc(w, event, prms, nprms)
6144      Widget w;
6145      XEvent *event;
6146      String *prms;
6147      Cardinal *nprms;
6148 {
6149     if (shellUp[4] == True)
6150       ICSInputSendText();
6151 }
6152
6153 void UpKeyProc(w, event, prms, nprms)
6154      Widget w;
6155      XEvent *event;
6156      String *prms;
6157      Cardinal *nprms;
6158 {   // [HGM] input: let up-arrow recall previous line from history
6159     Widget edit;
6160     int j;
6161     Arg args[16];
6162     String val;
6163     XawTextBlock t;
6164
6165     if (!shellUp[4]) return;
6166     edit = boxOptions[0].handle;
6167     j = 0;
6168     XtSetArg(args[j], XtNstring, &val); j++;
6169     XtGetValues(edit, args, j);
6170     val = PrevInHistory(val);
6171     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6172     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6173     if(val) {
6174         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6175         XawTextReplace(edit, 0, 0, &t);
6176         XawTextSetInsertionPoint(edit, 9999);
6177     }
6178 }
6179
6180 void DownKeyProc(w, event, prms, nprms)
6181      Widget w;
6182      XEvent *event;
6183      String *prms;
6184      Cardinal *nprms;
6185 {   // [HGM] input: let down-arrow recall next line from history
6186     Widget edit;
6187     String val;
6188     XawTextBlock t;
6189
6190     if (!shellUp[4]) return;
6191     edit = boxOptions[0].handle;
6192     val = NextInHistory();
6193     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6194     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6195     if(val) {
6196         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6197         XawTextReplace(edit, 0, 0, &t);
6198         XawTextSetInsertionPoint(edit, 9999);
6199     }
6200 }
6201
6202 void StopObservingProc(w, event, prms, nprms)
6203      Widget w;
6204      XEvent *event;
6205      String *prms;
6206      Cardinal *nprms;
6207 {
6208     StopObservingEvent();
6209 }
6210
6211 void StopExaminingProc(w, event, prms, nprms)
6212      Widget w;
6213      XEvent *event;
6214      String *prms;
6215      Cardinal *nprms;
6216 {
6217     StopExaminingEvent();
6218 }
6219
6220 void UploadProc(w, event, prms, nprms)
6221      Widget w;
6222      XEvent *event;
6223      String *prms;
6224      Cardinal *nprms;
6225 {
6226     UploadGameEvent();
6227 }
6228
6229
6230 void ForwardProc(w, event, prms, nprms)
6231      Widget w;
6232      XEvent *event;
6233      String *prms;
6234      Cardinal *nprms;
6235 {
6236     ForwardEvent();
6237 }
6238
6239
6240 void BackwardProc(w, event, prms, nprms)
6241      Widget w;
6242      XEvent *event;
6243      String *prms;
6244      Cardinal *nprms;
6245 {
6246     BackwardEvent();
6247 }
6248
6249 void TempBackwardProc(w, event, prms, nprms)
6250      Widget w;
6251      XEvent *event;
6252      String *prms;
6253      Cardinal *nprms;
6254 {
6255         if (!TempBackwardActive) {
6256                 TempBackwardActive = True;
6257                 BackwardEvent();
6258         }
6259 }
6260
6261 void TempForwardProc(w, event, prms, nprms)
6262      Widget w;
6263      XEvent *event;
6264      String *prms;
6265      Cardinal *nprms;
6266 {
6267         /* Check to see if triggered by a key release event for a repeating key.
6268          * If so the next queued event will be a key press of the same key at the same time */
6269         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
6270                 XEvent next;
6271                 XPeekEvent(xDisplay, &next);
6272                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
6273                         next.xkey.keycode == event->xkey.keycode)
6274                                 return;
6275         }
6276     ForwardEvent();
6277         TempBackwardActive = False;
6278 }
6279
6280 void ToStartProc(w, event, prms, nprms)
6281      Widget w;
6282      XEvent *event;
6283      String *prms;
6284      Cardinal *nprms;
6285 {
6286     ToStartEvent();
6287 }
6288
6289 void ToEndProc(w, event, prms, nprms)
6290      Widget w;
6291      XEvent *event;
6292      String *prms;
6293      Cardinal *nprms;
6294 {
6295     ToEndEvent();
6296 }
6297
6298 void RevertProc(w, event, prms, nprms)
6299      Widget w;
6300      XEvent *event;
6301      String *prms;
6302      Cardinal *nprms;
6303 {
6304     RevertEvent(False);
6305 }
6306
6307 void AnnotateProc(w, event, prms, nprms)
6308      Widget w;
6309      XEvent *event;
6310      String *prms;
6311      Cardinal *nprms;
6312 {
6313     RevertEvent(True);
6314 }
6315
6316 void TruncateGameProc(w, event, prms, nprms)
6317      Widget w;
6318      XEvent *event;
6319      String *prms;
6320      Cardinal *nprms;
6321 {
6322     TruncateGameEvent();
6323 }
6324 void RetractMoveProc(w, event, prms, nprms)
6325      Widget w;
6326      XEvent *event;
6327      String *prms;
6328      Cardinal *nprms;
6329 {
6330     RetractMoveEvent();
6331 }
6332
6333 void MoveNowProc(w, event, prms, nprms)
6334      Widget w;
6335      XEvent *event;
6336      String *prms;
6337      Cardinal *nprms;
6338 {
6339     MoveNowEvent();
6340 }
6341
6342 void FlipViewProc(w, event, prms, nprms)
6343      Widget w;
6344      XEvent *event;
6345      String *prms;
6346      Cardinal *nprms;
6347 {
6348     flipView = !flipView;
6349     DrawPosition(True, NULL);
6350 }
6351
6352 void PonderNextMoveProc(w, event, prms, nprms)
6353      Widget w;
6354      XEvent *event;
6355      String *prms;
6356      Cardinal *nprms;
6357 {
6358     Arg args[16];
6359
6360     PonderNextMoveEvent(!appData.ponderNextMove);
6361 #ifndef OPTIONSDIALOG
6362     if (appData.ponderNextMove) {
6363         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6364     } else {
6365         XtSetArg(args[0], XtNleftBitmap, None);
6366     }
6367     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6368                 args, 1);
6369 #endif
6370 }
6371
6372 #ifndef OPTIONSDIALOG
6373 void AlwaysQueenProc(w, event, prms, nprms)
6374      Widget w;
6375      XEvent *event;
6376      String *prms;
6377      Cardinal *nprms;
6378 {
6379     Arg args[16];
6380
6381     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6382
6383     if (appData.alwaysPromoteToQueen) {
6384         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6385     } else {
6386         XtSetArg(args[0], XtNleftBitmap, None);
6387     }
6388     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6389                 args, 1);
6390 }
6391
6392 void AnimateDraggingProc(w, event, prms, nprms)
6393      Widget w;
6394      XEvent *event;
6395      String *prms;
6396      Cardinal *nprms;
6397 {
6398     Arg args[16];
6399
6400     appData.animateDragging = !appData.animateDragging;
6401
6402     if (appData.animateDragging) {
6403         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6404         CreateAnimVars();
6405     } else {
6406         XtSetArg(args[0], XtNleftBitmap, None);
6407     }
6408     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6409                 args, 1);
6410 }
6411
6412 void AnimateMovingProc(w, event, prms, nprms)
6413      Widget w;
6414      XEvent *event;
6415      String *prms;
6416      Cardinal *nprms;
6417 {
6418     Arg args[16];
6419
6420     appData.animate = !appData.animate;
6421
6422     if (appData.animate) {
6423         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6424         CreateAnimVars();
6425     } else {
6426         XtSetArg(args[0], XtNleftBitmap, None);
6427     }
6428     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6429                 args, 1);
6430 }
6431
6432 void AutoflagProc(w, event, prms, nprms)
6433      Widget w;
6434      XEvent *event;
6435      String *prms;
6436      Cardinal *nprms;
6437 {
6438     Arg args[16];
6439
6440     appData.autoCallFlag = !appData.autoCallFlag;
6441
6442     if (appData.autoCallFlag) {
6443         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6444     } else {
6445         XtSetArg(args[0], XtNleftBitmap, None);
6446     }
6447     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6448                 args, 1);
6449 }
6450
6451 void AutoflipProc(w, event, prms, nprms)
6452      Widget w;
6453      XEvent *event;
6454      String *prms;
6455      Cardinal *nprms;
6456 {
6457     Arg args[16];
6458
6459     appData.autoFlipView = !appData.autoFlipView;
6460
6461     if (appData.autoFlipView) {
6462         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6463     } else {
6464         XtSetArg(args[0], XtNleftBitmap, None);
6465     }
6466     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6467                 args, 1);
6468 }
6469
6470 void BlindfoldProc(w, event, prms, nprms)
6471      Widget w;
6472      XEvent *event;
6473      String *prms;
6474      Cardinal *nprms;
6475 {
6476     Arg args[16];
6477
6478     appData.blindfold = !appData.blindfold;
6479
6480     if (appData.blindfold) {
6481         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6482     } else {
6483         XtSetArg(args[0], XtNleftBitmap, None);
6484     }
6485     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6486                 args, 1);
6487
6488     DrawPosition(True, NULL);
6489 }
6490
6491 void TestLegalityProc(w, event, prms, nprms)
6492      Widget w;
6493      XEvent *event;
6494      String *prms;
6495      Cardinal *nprms;
6496 {
6497     Arg args[16];
6498
6499     appData.testLegality = !appData.testLegality;
6500
6501     if (appData.testLegality) {
6502         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6503     } else {
6504         XtSetArg(args[0], XtNleftBitmap, None);
6505     }
6506     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6507                 args, 1);
6508 }
6509
6510
6511 void FlashMovesProc(w, event, prms, nprms)
6512      Widget w;
6513      XEvent *event;
6514      String *prms;
6515      Cardinal *nprms;
6516 {
6517     Arg args[16];
6518
6519     if (appData.flashCount == 0) {
6520         appData.flashCount = 3;
6521     } else {
6522         appData.flashCount = -appData.flashCount;
6523     }
6524
6525     if (appData.flashCount > 0) {
6526         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6527     } else {
6528         XtSetArg(args[0], XtNleftBitmap, None);
6529     }
6530     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6531                 args, 1);
6532 }
6533
6534 #if HIGHDRAG
6535 void HighlightDraggingProc(w, event, prms, nprms)
6536      Widget w;
6537      XEvent *event;
6538      String *prms;
6539      Cardinal *nprms;
6540 {
6541     Arg args[16];
6542
6543     appData.highlightDragging = !appData.highlightDragging;
6544
6545     if (appData.highlightDragging) {
6546         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6547     } else {
6548         XtSetArg(args[0], XtNleftBitmap, None);
6549     }
6550     XtSetValues(XtNameToWidget(menuBarWidget,
6551                                "menuOptions.Highlight Dragging"), args, 1);
6552 }
6553 #endif
6554
6555 void HighlightLastMoveProc(w, event, prms, nprms)
6556      Widget w;
6557      XEvent *event;
6558      String *prms;
6559      Cardinal *nprms;
6560 {
6561     Arg args[16];
6562
6563     appData.highlightLastMove = !appData.highlightLastMove;
6564
6565     if (appData.highlightLastMove) {
6566         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6567     } else {
6568         XtSetArg(args[0], XtNleftBitmap, None);
6569     }
6570     XtSetValues(XtNameToWidget(menuBarWidget,
6571                                "menuOptions.Highlight Last Move"), args, 1);
6572 }
6573
6574 void HighlightArrowProc(w, event, prms, nprms)
6575      Widget w;
6576      XEvent *event;
6577      String *prms;
6578      Cardinal *nprms;
6579 {
6580     Arg args[16];
6581
6582     appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
6583
6584     if (appData.highlightMoveWithArrow) {
6585         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6586     } else {
6587         XtSetArg(args[0], XtNleftBitmap, None);
6588     }
6589     XtSetValues(XtNameToWidget(menuBarWidget,
6590                                "menuOptions.Arrow"), args, 1);
6591 }
6592
6593 #if 0
6594 void IcsAlarmProc(w, event, prms, nprms)
6595      Widget w;
6596      XEvent *event;
6597      String *prms;
6598      Cardinal *nprms;
6599 {
6600     Arg args[16];
6601
6602     appData.icsAlarm = !appData.icsAlarm;
6603
6604     if (appData.icsAlarm) {
6605         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6606     } else {
6607         XtSetArg(args[0], XtNleftBitmap, None);
6608     }
6609     XtSetValues(XtNameToWidget(menuBarWidget,
6610                                "menuOptions.ICS Alarm"), args, 1);
6611 }
6612 #endif
6613
6614 void MoveSoundProc(w, event, prms, nprms)
6615      Widget w;
6616      XEvent *event;
6617      String *prms;
6618      Cardinal *nprms;
6619 {
6620     Arg args[16];
6621
6622     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6623
6624     if (appData.ringBellAfterMoves) {
6625         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6626     } else {
6627         XtSetArg(args[0], XtNleftBitmap, None);
6628     }
6629     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6630                 args, 1);
6631 }
6632
6633 void OneClickProc(w, event, prms, nprms)
6634      Widget w;
6635      XEvent *event;
6636      String *prms;
6637      Cardinal *nprms;
6638 {
6639     Arg args[16];
6640
6641     appData.oneClick = !appData.oneClick;
6642
6643     if (appData.oneClick) {
6644         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6645     } else {
6646         XtSetArg(args[0], XtNleftBitmap, None);
6647     }
6648     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
6649                 args, 1);
6650 }
6651
6652 void PeriodicUpdatesProc(w, event, prms, nprms)
6653      Widget w;
6654      XEvent *event;
6655      String *prms;
6656      Cardinal *nprms;
6657 {
6658     Arg args[16];
6659
6660     PeriodicUpdatesEvent(!appData.periodicUpdates);
6661
6662     if (appData.periodicUpdates) {
6663         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6664     } else {
6665         XtSetArg(args[0], XtNleftBitmap, None);
6666     }
6667     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6668                 args, 1);
6669 }
6670
6671 void PopupExitMessageProc(w, event, prms, nprms)
6672      Widget w;
6673      XEvent *event;
6674      String *prms;
6675      Cardinal *nprms;
6676 {
6677     Arg args[16];
6678
6679     appData.popupExitMessage = !appData.popupExitMessage;
6680
6681     if (appData.popupExitMessage) {
6682         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6683     } else {
6684         XtSetArg(args[0], XtNleftBitmap, None);
6685     }
6686     XtSetValues(XtNameToWidget(menuBarWidget,
6687                                "menuOptions.Popup Exit Message"), args, 1);
6688 }
6689
6690 void PopupMoveErrorsProc(w, event, prms, nprms)
6691      Widget w;
6692      XEvent *event;
6693      String *prms;
6694      Cardinal *nprms;
6695 {
6696     Arg args[16];
6697
6698     appData.popupMoveErrors = !appData.popupMoveErrors;
6699
6700     if (appData.popupMoveErrors) {
6701         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6702     } else {
6703         XtSetArg(args[0], XtNleftBitmap, None);
6704     }
6705     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6706                 args, 1);
6707 }
6708
6709 #if 0
6710 void PremoveProc(w, event, prms, nprms)
6711      Widget w;
6712      XEvent *event;
6713      String *prms;
6714      Cardinal *nprms;
6715 {
6716     Arg args[16];
6717
6718     appData.premove = !appData.premove;
6719
6720     if (appData.premove) {
6721         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6722     } else {
6723         XtSetArg(args[0], XtNleftBitmap, None);
6724     }
6725     XtSetValues(XtNameToWidget(menuBarWidget,
6726                                "menuOptions.Premove"), args, 1);
6727 }
6728 #endif
6729
6730 void ShowCoordsProc(w, event, prms, nprms)
6731      Widget w;
6732      XEvent *event;
6733      String *prms;
6734      Cardinal *nprms;
6735 {
6736     Arg args[16];
6737
6738     appData.showCoords = !appData.showCoords;
6739
6740     if (appData.showCoords) {
6741         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6742     } else {
6743         XtSetArg(args[0], XtNleftBitmap, None);
6744     }
6745     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6746                 args, 1);
6747
6748     DrawPosition(True, NULL);
6749 }
6750
6751 void ShowThinkingProc(w, event, prms, nprms)
6752      Widget w;
6753      XEvent *event;
6754      String *prms;
6755      Cardinal *nprms;
6756 {
6757     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6758     ShowThinkingEvent();
6759 }
6760
6761 void HideThinkingProc(w, event, prms, nprms)
6762      Widget w;
6763      XEvent *event;
6764      String *prms;
6765      Cardinal *nprms;
6766 {
6767     Arg args[16];
6768
6769     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6770     ShowThinkingEvent();
6771
6772     if (appData.hideThinkingFromHuman) {
6773         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6774     } else {
6775         XtSetArg(args[0], XtNleftBitmap, None);
6776     }
6777     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6778                 args, 1);
6779 }
6780 #endif
6781
6782 void SaveOnExitProc(w, event, prms, nprms)
6783      Widget w;
6784      XEvent *event;
6785      String *prms;
6786      Cardinal *nprms;
6787 {
6788     Arg args[16];
6789
6790     saveSettingsOnExit = !saveSettingsOnExit;
6791
6792     if (saveSettingsOnExit) {
6793         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6794     } else {
6795         XtSetArg(args[0], XtNleftBitmap, None);
6796     }
6797     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6798                 args, 1);
6799 }
6800
6801 void SaveSettingsProc(w, event, prms, nprms)
6802      Widget w;
6803      XEvent *event;
6804      String *prms;
6805      Cardinal *nprms;
6806 {
6807      SaveSettings(settingsFileName);
6808 }
6809
6810 void InfoProc(w, event, prms, nprms)
6811      Widget w;
6812      XEvent *event;
6813      String *prms;
6814      Cardinal *nprms;
6815 {
6816     char buf[MSG_SIZ];
6817     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6818             INFODIR, INFOFILE);
6819     system(buf);
6820 }
6821
6822 void ManProc(w, event, prms, nprms)
6823      Widget w;
6824      XEvent *event;
6825      String *prms;
6826      Cardinal *nprms;
6827 {
6828     char buf[MSG_SIZ];
6829     String name;
6830     if (nprms && *nprms > 0)
6831       name = prms[0];
6832     else
6833       name = "xboard";
6834     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
6835     system(buf);
6836 }
6837
6838 void HintProc(w, event, prms, nprms)
6839      Widget w;
6840      XEvent *event;
6841      String *prms;
6842      Cardinal *nprms;
6843 {
6844     HintEvent();
6845 }
6846
6847 void BookProc(w, event, prms, nprms)
6848      Widget w;
6849      XEvent *event;
6850      String *prms;
6851      Cardinal *nprms;
6852 {
6853     BookEvent();
6854 }
6855
6856 void AboutProc(w, event, prms, nprms)
6857      Widget w;
6858      XEvent *event;
6859      String *prms;
6860      Cardinal *nprms;
6861 {
6862     char buf[MSG_SIZ];
6863 #if ZIPPY
6864     char *zippy = _(" (with Zippy code)");
6865 #else
6866     char *zippy = "";
6867 #endif
6868     snprintf(buf, sizeof(buf), 
6869 _("%s%s\n\n"
6870 "Copyright 1991 Digital Equipment Corporation\n"
6871 "Enhancements Copyright 1992-2009 Free Software Foundation\n"
6872 "Enhancements Copyright 2005 Alessandro Scotti\n\n"
6873 "%s is free software and carries NO WARRANTY;"
6874 "see the file COPYING for more information."),
6875             programVersion, zippy, PACKAGE);
6876     ErrorPopUp(_("About XBoard"), buf, FALSE);
6877 }
6878
6879 void DebugProc(w, event, prms, nprms)
6880      Widget w;
6881      XEvent *event;
6882      String *prms;
6883      Cardinal *nprms;
6884 {
6885     appData.debugMode = !appData.debugMode;
6886 }
6887
6888 void AboutGameProc(w, event, prms, nprms)
6889      Widget w;
6890      XEvent *event;
6891      String *prms;
6892      Cardinal *nprms;
6893 {
6894     AboutGameEvent();
6895 }
6896
6897 void NothingProc(w, event, prms, nprms)
6898      Widget w;
6899      XEvent *event;
6900      String *prms;
6901      Cardinal *nprms;
6902 {
6903     return;
6904 }
6905
6906 void DisplayMessage(message, extMessage)
6907      char *message, *extMessage;
6908 {
6909   /* display a message in the message widget */
6910
6911   char buf[MSG_SIZ];
6912   Arg arg;
6913
6914   if (extMessage)
6915     {
6916       if (*message)
6917         {
6918           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
6919           message = buf;
6920         }
6921       else
6922         {
6923           message = extMessage;
6924         };
6925     };
6926
6927     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
6928
6929   /* need to test if messageWidget already exists, since this function
6930      can also be called during the startup, if for example a Xresource
6931      is not set up correctly */
6932   if(messageWidget)
6933     {
6934       XtSetArg(arg, XtNlabel, message);
6935       XtSetValues(messageWidget, &arg, 1);
6936     };
6937
6938   return;
6939 }
6940
6941 void DisplayTitle(text)
6942      char *text;
6943 {
6944     Arg args[16];
6945     int i;
6946     char title[MSG_SIZ];
6947     char icon[MSG_SIZ];
6948
6949     if (text == NULL) text = "";
6950
6951     if (appData.titleInWindow) {
6952         i = 0;
6953         XtSetArg(args[i], XtNlabel, text);   i++;
6954         XtSetValues(titleWidget, args, i);
6955     }
6956
6957     if (*text != NULLCHAR) {
6958       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
6959       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
6960     } else if (appData.icsActive) {
6961         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
6962         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
6963     } else if (appData.cmailGameName[0] != NULLCHAR) {
6964         snprintf(icon, sizeof(icon), "%s", "CMail");
6965         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
6966 #ifdef GOTHIC
6967     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
6968     } else if (gameInfo.variant == VariantGothic) {
6969       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
6970       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
6971 #endif
6972 #ifdef FALCON
6973     } else if (gameInfo.variant == VariantFalcon) {
6974       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6975       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
6976 #endif
6977     } else if (appData.noChessProgram) {
6978       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
6979       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
6980     } else {
6981       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
6982         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
6983     }
6984     i = 0;
6985     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6986     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6987     XtSetValues(shellWidget, args, i);
6988     XSync(xDisplay, False);
6989 }
6990
6991
6992 void
6993 DisplayError(message, error)
6994      String message;
6995      int error;
6996 {
6997     char buf[MSG_SIZ];
6998
6999     if (error == 0) {
7000         if (appData.debugMode || appData.matchMode) {
7001             fprintf(stderr, "%s: %s\n", programName, message);
7002         }
7003     } else {
7004         if (appData.debugMode || appData.matchMode) {
7005             fprintf(stderr, "%s: %s: %s\n",
7006                     programName, message, strerror(error));
7007         }
7008         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7009         message = buf;
7010     }
7011     ErrorPopUp(_("Error"), message, FALSE);
7012 }
7013
7014
7015 void DisplayMoveError(message)
7016      String message;
7017 {
7018     fromX = fromY = -1;
7019     ClearHighlights();
7020     DrawPosition(FALSE, NULL);
7021     if (appData.debugMode || appData.matchMode) {
7022         fprintf(stderr, "%s: %s\n", programName, message);
7023     }
7024     if (appData.popupMoveErrors) {
7025         ErrorPopUp(_("Error"), message, FALSE);
7026     } else {
7027         DisplayMessage(message, "");
7028     }
7029 }
7030
7031
7032 void DisplayFatalError(message, error, status)
7033      String message;
7034      int error, status;
7035 {
7036     char buf[MSG_SIZ];
7037
7038     errorExitStatus = status;
7039     if (error == 0) {
7040         fprintf(stderr, "%s: %s\n", programName, message);
7041     } else {
7042         fprintf(stderr, "%s: %s: %s\n",
7043                 programName, message, strerror(error));
7044         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7045         message = buf;
7046     }
7047     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7048       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7049     } else {
7050       ExitEvent(status);
7051     }
7052 }
7053
7054 void DisplayInformation(message)
7055      String message;
7056 {
7057     ErrorPopDown();
7058     ErrorPopUp(_("Information"), message, TRUE);
7059 }
7060
7061 void DisplayNote(message)
7062      String message;
7063 {
7064     ErrorPopDown();
7065     ErrorPopUp(_("Note"), message, FALSE);
7066 }
7067
7068 static int
7069 NullXErrorCheck(dpy, error_event)
7070      Display *dpy;
7071      XErrorEvent *error_event;
7072 {
7073     return 0;
7074 }
7075
7076 void DisplayIcsInteractionTitle(message)
7077      String message;
7078 {
7079   if (oldICSInteractionTitle == NULL) {
7080     /* Magic to find the old window title, adapted from vim */
7081     char *wina = getenv("WINDOWID");
7082     if (wina != NULL) {
7083       Window win = (Window) atoi(wina);
7084       Window root, parent, *children;
7085       unsigned int nchildren;
7086       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7087       for (;;) {
7088         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7089         if (!XQueryTree(xDisplay, win, &root, &parent,
7090                         &children, &nchildren)) break;
7091         if (children) XFree((void *)children);
7092         if (parent == root || parent == 0) break;
7093         win = parent;
7094       }
7095       XSetErrorHandler(oldHandler);
7096     }
7097     if (oldICSInteractionTitle == NULL) {
7098       oldICSInteractionTitle = "xterm";
7099     }
7100   }
7101   printf("\033]0;%s\007", message);
7102   fflush(stdout);
7103 }
7104
7105 char pendingReplyPrefix[MSG_SIZ];
7106 ProcRef pendingReplyPR;
7107
7108 void AskQuestionProc(w, event, prms, nprms)
7109      Widget w;
7110      XEvent *event;
7111      String *prms;
7112      Cardinal *nprms;
7113 {
7114     if (*nprms != 4) {
7115         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7116                 *nprms);
7117         return;
7118     }
7119     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7120 }
7121
7122 void AskQuestionPopDown()
7123 {
7124     if (!askQuestionUp) return;
7125     XtPopdown(askQuestionShell);
7126     XtDestroyWidget(askQuestionShell);
7127     askQuestionUp = False;
7128 }
7129
7130 void AskQuestionReplyAction(w, event, prms, nprms)
7131      Widget w;
7132      XEvent *event;
7133      String *prms;
7134      Cardinal *nprms;
7135 {
7136     char buf[MSG_SIZ];
7137     int err;
7138     String reply;
7139
7140     reply = XawDialogGetValueString(w = XtParent(w));
7141     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7142     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7143     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7144     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
7145     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7146     AskQuestionPopDown();
7147
7148     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7149 }
7150
7151 void AskQuestionCallback(w, client_data, call_data)
7152      Widget w;
7153      XtPointer client_data, call_data;
7154 {
7155     String name;
7156     Arg args[16];
7157
7158     XtSetArg(args[0], XtNlabel, &name);
7159     XtGetValues(w, args, 1);
7160
7161     if (strcmp(name, _("cancel")) == 0) {
7162         AskQuestionPopDown();
7163     } else {
7164         AskQuestionReplyAction(w, NULL, NULL, NULL);
7165     }
7166 }
7167
7168 void AskQuestion(title, question, replyPrefix, pr)
7169      char *title, *question, *replyPrefix;
7170      ProcRef pr;
7171 {
7172     Arg args[16];
7173     Widget popup, layout, dialog, edit;
7174     Window root, child;
7175     int x, y, i;
7176     int win_x, win_y;
7177     unsigned int mask;
7178
7179     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7180     pendingReplyPR = pr;
7181
7182     i = 0;
7183     XtSetArg(args[i], XtNresizable, True); i++;
7184     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7185     askQuestionShell = popup =
7186       XtCreatePopupShell(title, transientShellWidgetClass,
7187                          shellWidget, args, i);
7188
7189     layout =
7190       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7191                             layoutArgs, XtNumber(layoutArgs));
7192
7193     i = 0;
7194     XtSetArg(args[i], XtNlabel, question); i++;
7195     XtSetArg(args[i], XtNvalue, ""); i++;
7196     XtSetArg(args[i], XtNborderWidth, 0); i++;
7197     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7198                                    layout, args, i);
7199
7200     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7201                        (XtPointer) dialog);
7202     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7203                        (XtPointer) dialog);
7204
7205     XtRealizeWidget(popup);
7206     CatchDeleteWindow(popup, "AskQuestionPopDown");
7207
7208     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7209                   &x, &y, &win_x, &win_y, &mask);
7210
7211     XtSetArg(args[0], XtNx, x - 10);
7212     XtSetArg(args[1], XtNy, y - 30);
7213     XtSetValues(popup, args, 2);
7214
7215     XtPopup(popup, XtGrabExclusive);
7216     askQuestionUp = True;
7217
7218     edit = XtNameToWidget(dialog, "*value");
7219     XtSetKeyboardFocus(popup, edit);
7220 }
7221
7222
7223 void
7224 PlaySound(name)
7225      char *name;
7226 {
7227   if (*name == NULLCHAR) {
7228     return;
7229   } else if (strcmp(name, "$") == 0) {
7230     putc(BELLCHAR, stderr);
7231   } else {
7232     char buf[2048];
7233     char *prefix = "", *sep = "";
7234     if(appData.soundProgram[0] == NULLCHAR) return;
7235     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
7236     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
7237     system(buf);
7238   }
7239 }
7240
7241 void
7242 RingBell()
7243 {
7244   PlaySound(appData.soundMove);
7245 }
7246
7247 void
7248 PlayIcsWinSound()
7249 {
7250   PlaySound(appData.soundIcsWin);
7251 }
7252
7253 void
7254 PlayIcsLossSound()
7255 {
7256   PlaySound(appData.soundIcsLoss);
7257 }
7258
7259 void
7260 PlayIcsDrawSound()
7261 {
7262   PlaySound(appData.soundIcsDraw);
7263 }
7264
7265 void
7266 PlayIcsUnfinishedSound()
7267 {
7268   PlaySound(appData.soundIcsUnfinished);
7269 }
7270
7271 void
7272 PlayAlarmSound()
7273 {
7274   PlaySound(appData.soundIcsAlarm);
7275 }
7276
7277 void
7278 PlayTellSound()
7279 {
7280   PlaySound(appData.soundTell);
7281 }
7282
7283 void
7284 EchoOn()
7285 {
7286     system("stty echo");
7287     noEcho = False;
7288 }
7289
7290 void
7291 EchoOff()
7292 {
7293     system("stty -echo");
7294     noEcho = True;
7295 }
7296
7297 void
7298 RunCommand(char *buf)
7299 {
7300     system(buf);
7301 }
7302
7303 void
7304 Colorize(cc, continuation)
7305      ColorClass cc;
7306      int continuation;
7307 {
7308     char buf[MSG_SIZ];
7309     int count, outCount, error;
7310
7311     if (textColors[(int)cc].bg > 0) {
7312         if (textColors[(int)cc].fg > 0) {
7313           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7314                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7315         } else {
7316           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7317                    textColors[(int)cc].bg);
7318         }
7319     } else {
7320         if (textColors[(int)cc].fg > 0) {
7321           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7322                     textColors[(int)cc].fg);
7323         } else {
7324           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7325         }
7326     }
7327     count = strlen(buf);
7328     outCount = OutputToProcess(NoProc, buf, count, &error);
7329     if (outCount < count) {
7330         DisplayFatalError(_("Error writing to display"), error, 1);
7331     }
7332
7333     if (continuation) return;
7334     switch (cc) {
7335     case ColorShout:
7336       PlaySound(appData.soundShout);
7337       break;
7338     case ColorSShout:
7339       PlaySound(appData.soundSShout);
7340       break;
7341     case ColorChannel1:
7342       PlaySound(appData.soundChannel1);
7343       break;
7344     case ColorChannel:
7345       PlaySound(appData.soundChannel);
7346       break;
7347     case ColorKibitz:
7348       PlaySound(appData.soundKibitz);
7349       break;
7350     case ColorTell:
7351       PlaySound(appData.soundTell);
7352       break;
7353     case ColorChallenge:
7354       PlaySound(appData.soundChallenge);
7355       break;
7356     case ColorRequest:
7357       PlaySound(appData.soundRequest);
7358       break;
7359     case ColorSeek:
7360       PlaySound(appData.soundSeek);
7361       break;
7362     case ColorNormal:
7363     case ColorNone:
7364     default:
7365       break;
7366     }
7367 }
7368
7369 char *UserName()
7370 {
7371     return getpwuid(getuid())->pw_name;
7372 }
7373
7374 static char *
7375 ExpandPathName(path)
7376      char *path;
7377 {
7378     static char static_buf[4*MSG_SIZ];
7379     char *d, *s, buf[4*MSG_SIZ];
7380     struct passwd *pwd;
7381
7382     s = path;
7383     d = static_buf;
7384
7385     while (*s && isspace(*s))
7386       ++s;
7387
7388     if (!*s) {
7389         *d = 0;
7390         return static_buf;
7391     }
7392
7393     if (*s == '~') {
7394         if (*(s+1) == '/') {
7395           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7396           strcat(d, s+1);
7397         }
7398         else {
7399           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7400           { char *p; if(p = strchr(buf, '/')) *p = 0; }
7401           pwd = getpwnam(buf);
7402           if (!pwd)
7403             {
7404               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7405                       buf, path);
7406               return NULL;
7407             }
7408           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7409           strcat(d, strchr(s+1, '/'));
7410         }
7411     }
7412     else
7413       safeStrCpy(d, s, 4*MSG_SIZ );
7414
7415     return static_buf;
7416 }
7417
7418 char *HostName()
7419 {
7420     static char host_name[MSG_SIZ];
7421
7422 #if HAVE_GETHOSTNAME
7423     gethostname(host_name, MSG_SIZ);
7424     return host_name;
7425 #else  /* not HAVE_GETHOSTNAME */
7426 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7427     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7428     return host_name;
7429 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7430     return "localhost";
7431 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7432 #endif /* not HAVE_GETHOSTNAME */
7433 }
7434
7435 XtIntervalId delayedEventTimerXID = 0;
7436 DelayedEventCallback delayedEventCallback = 0;
7437
7438 void
7439 FireDelayedEvent()
7440 {
7441     delayedEventTimerXID = 0;
7442     delayedEventCallback();
7443 }
7444
7445 void
7446 ScheduleDelayedEvent(cb, millisec)
7447      DelayedEventCallback cb; long millisec;
7448 {
7449     if(delayedEventTimerXID && delayedEventCallback == cb)
7450         // [HGM] alive: replace, rather than add or flush identical event
7451         XtRemoveTimeOut(delayedEventTimerXID);
7452     delayedEventCallback = cb;
7453     delayedEventTimerXID =
7454       XtAppAddTimeOut(appContext, millisec,
7455                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7456 }
7457
7458 DelayedEventCallback
7459 GetDelayedEvent()
7460 {
7461   if (delayedEventTimerXID) {
7462     return delayedEventCallback;
7463   } else {
7464     return NULL;
7465   }
7466 }
7467
7468 void
7469 CancelDelayedEvent()
7470 {
7471   if (delayedEventTimerXID) {
7472     XtRemoveTimeOut(delayedEventTimerXID);
7473     delayedEventTimerXID = 0;
7474   }
7475 }
7476
7477 XtIntervalId loadGameTimerXID = 0;
7478
7479 int LoadGameTimerRunning()
7480 {
7481     return loadGameTimerXID != 0;
7482 }
7483
7484 int StopLoadGameTimer()
7485 {
7486     if (loadGameTimerXID != 0) {
7487         XtRemoveTimeOut(loadGameTimerXID);
7488         loadGameTimerXID = 0;
7489         return TRUE;
7490     } else {
7491         return FALSE;
7492     }
7493 }
7494
7495 void
7496 LoadGameTimerCallback(arg, id)
7497      XtPointer arg;
7498      XtIntervalId *id;
7499 {
7500     loadGameTimerXID = 0;
7501     AutoPlayGameLoop();
7502 }
7503
7504 void
7505 StartLoadGameTimer(millisec)
7506      long millisec;
7507 {
7508     loadGameTimerXID =
7509       XtAppAddTimeOut(appContext, millisec,
7510                       (XtTimerCallbackProc) LoadGameTimerCallback,
7511                       (XtPointer) 0);
7512 }
7513
7514 XtIntervalId analysisClockXID = 0;
7515
7516 void
7517 AnalysisClockCallback(arg, id)
7518      XtPointer arg;
7519      XtIntervalId *id;
7520 {
7521     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7522          || appData.icsEngineAnalyze) { // [DM]
7523         AnalysisPeriodicEvent(0);
7524         StartAnalysisClock();
7525     }
7526 }
7527
7528 void
7529 StartAnalysisClock()
7530 {
7531     analysisClockXID =
7532       XtAppAddTimeOut(appContext, 2000,
7533                       (XtTimerCallbackProc) AnalysisClockCallback,
7534                       (XtPointer) 0);
7535 }
7536
7537 XtIntervalId clockTimerXID = 0;
7538
7539 int ClockTimerRunning()
7540 {
7541     return clockTimerXID != 0;
7542 }
7543
7544 int StopClockTimer()
7545 {
7546     if (clockTimerXID != 0) {
7547         XtRemoveTimeOut(clockTimerXID);
7548         clockTimerXID = 0;
7549         return TRUE;
7550     } else {
7551         return FALSE;
7552     }
7553 }
7554
7555 void
7556 ClockTimerCallback(arg, id)
7557      XtPointer arg;
7558      XtIntervalId *id;
7559 {
7560     clockTimerXID = 0;
7561     DecrementClocks();
7562 }
7563
7564 void
7565 StartClockTimer(millisec)
7566      long millisec;
7567 {
7568     clockTimerXID =
7569       XtAppAddTimeOut(appContext, millisec,
7570                       (XtTimerCallbackProc) ClockTimerCallback,
7571                       (XtPointer) 0);
7572 }
7573
7574 void
7575 DisplayTimerLabel(w, color, timer, highlight)
7576      Widget w;
7577      char *color;
7578      long timer;
7579      int highlight;
7580 {
7581     char buf[MSG_SIZ];
7582     Arg args[16];
7583
7584     /* check for low time warning */
7585     Pixel foregroundOrWarningColor = timerForegroundPixel;
7586
7587     if (timer > 0 &&
7588         appData.lowTimeWarning &&
7589         (timer / 1000) < appData.icsAlarmTime)
7590       foregroundOrWarningColor = lowTimeWarningColor;
7591
7592     if (appData.clockMode) {
7593       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7594       XtSetArg(args[0], XtNlabel, buf);
7595     } else {
7596       snprintf(buf, MSG_SIZ, "%s  ", color);
7597       XtSetArg(args[0], XtNlabel, buf);
7598     }
7599
7600     if (highlight) {
7601
7602         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7603         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7604     } else {
7605         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7606         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7607     }
7608
7609     XtSetValues(w, args, 3);
7610 }
7611
7612 void
7613 DisplayWhiteClock(timeRemaining, highlight)
7614      long timeRemaining;
7615      int highlight;
7616 {
7617     Arg args[16];
7618
7619     if(appData.noGUI) return;
7620     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7621     if (highlight && iconPixmap == bIconPixmap) {
7622         iconPixmap = wIconPixmap;
7623         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7624         XtSetValues(shellWidget, args, 1);
7625     }
7626 }
7627
7628 void
7629 DisplayBlackClock(timeRemaining, highlight)
7630      long timeRemaining;
7631      int highlight;
7632 {
7633     Arg args[16];
7634
7635     if(appData.noGUI) return;
7636     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7637     if (highlight && iconPixmap == wIconPixmap) {
7638         iconPixmap = bIconPixmap;
7639         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7640         XtSetValues(shellWidget, args, 1);
7641     }
7642 }
7643
7644 #define CPNone 0
7645 #define CPReal 1
7646 #define CPComm 2
7647 #define CPSock 3
7648 #define CPLoop 4
7649 typedef int CPKind;
7650
7651 typedef struct {
7652     CPKind kind;
7653     int pid;
7654     int fdTo, fdFrom;
7655 } ChildProc;
7656
7657
7658 int StartChildProcess(cmdLine, dir, pr)
7659      char *cmdLine;
7660      char *dir;
7661      ProcRef *pr;
7662 {
7663     char *argv[64], *p;
7664     int i, pid;
7665     int to_prog[2], from_prog[2];
7666     ChildProc *cp;
7667     char buf[MSG_SIZ];
7668
7669     if (appData.debugMode) {
7670         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7671     }
7672
7673     /* We do NOT feed the cmdLine to the shell; we just
7674        parse it into blank-separated arguments in the
7675        most simple-minded way possible.
7676        */
7677     i = 0;
7678     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7679     p = buf;
7680     for (;;) {
7681         while(*p == ' ') p++;
7682         argv[i++] = p;
7683         if(*p == '"' || *p == '\'')
7684              p = strchr(++argv[i-1], *p);
7685         else p = strchr(p, ' ');
7686         if (p == NULL) break;
7687         *p++ = NULLCHAR;
7688     }
7689     argv[i] = NULL;
7690
7691     SetUpChildIO(to_prog, from_prog);
7692
7693     if ((pid = fork()) == 0) {
7694         /* Child process */
7695         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7696         close(to_prog[1]);     // first close the unused pipe ends
7697         close(from_prog[0]);
7698         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7699         dup2(from_prog[1], 1);
7700         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7701         close(from_prog[1]);                   // and closing again loses one of the pipes!
7702         if(fileno(stderr) >= 2) // better safe than sorry...
7703                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7704
7705         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7706             perror(dir);
7707             exit(1);
7708         }
7709
7710         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7711
7712         execvp(argv[0], argv);
7713
7714         /* If we get here, exec failed */
7715         perror(argv[0]);
7716         exit(1);
7717     }
7718
7719     /* Parent process */
7720     close(to_prog[0]);
7721     close(from_prog[1]);
7722
7723     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7724     cp->kind = CPReal;
7725     cp->pid = pid;
7726     cp->fdFrom = from_prog[0];
7727     cp->fdTo = to_prog[1];
7728     *pr = (ProcRef) cp;
7729     return 0;
7730 }
7731
7732 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7733 static RETSIGTYPE AlarmCallBack(int n)
7734 {
7735     return;
7736 }
7737
7738 void
7739 DestroyChildProcess(pr, signalType)
7740      ProcRef pr;
7741      int signalType;
7742 {
7743     ChildProc *cp = (ChildProc *) pr;
7744
7745     if (cp->kind != CPReal) return;
7746     cp->kind = CPNone;
7747     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7748         signal(SIGALRM, AlarmCallBack);
7749         alarm(3);
7750         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7751             kill(cp->pid, SIGKILL); // kill it forcefully
7752             wait((int *) 0);        // and wait again
7753         }
7754     } else {
7755         if (signalType) {
7756             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7757         }
7758         /* Process is exiting either because of the kill or because of
7759            a quit command sent by the backend; either way, wait for it to die.
7760         */
7761         wait((int *) 0);
7762     }
7763     close(cp->fdFrom);
7764     close(cp->fdTo);
7765 }
7766
7767 void
7768 InterruptChildProcess(pr)
7769      ProcRef pr;
7770 {
7771     ChildProc *cp = (ChildProc *) pr;
7772
7773     if (cp->kind != CPReal) return;
7774     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7775 }
7776
7777 int OpenTelnet(host, port, pr)
7778      char *host;
7779      char *port;
7780      ProcRef *pr;
7781 {
7782     char cmdLine[MSG_SIZ];
7783
7784     if (port[0] == NULLCHAR) {
7785       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7786     } else {
7787       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7788     }
7789     return StartChildProcess(cmdLine, "", pr);
7790 }
7791
7792 int OpenTCP(host, port, pr)
7793      char *host;
7794      char *port;
7795      ProcRef *pr;
7796 {
7797 #if OMIT_SOCKETS
7798     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7799 #else  /* !OMIT_SOCKETS */
7800     struct addrinfo hints;
7801     struct addrinfo *ais, *ai;
7802     int error;
7803     int s=0;
7804     ChildProc *cp;
7805
7806     memset(&hints, 0, sizeof(hints));
7807     hints.ai_family = AF_UNSPEC;
7808     hints.ai_socktype = SOCK_STREAM;
7809
7810     error = getaddrinfo(host, port, &hints, &ais);
7811     if (error != 0) {
7812       /* a getaddrinfo error is not an errno, so can't return it */
7813       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
7814               host, port, gai_strerror(error));
7815       return ENOENT;
7816     }
7817      
7818     for (ai = ais; ai != NULL; ai = ai->ai_next) {
7819       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
7820         error = errno;
7821         continue;
7822       }
7823       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
7824         error = errno;
7825         continue;
7826       }
7827       error = 0;
7828       break;
7829     }
7830     freeaddrinfo(ais);
7831
7832     if (error != 0) {
7833       return error;
7834     }
7835
7836     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7837     cp->kind = CPSock;
7838     cp->pid = 0;
7839     cp->fdFrom = s;
7840     cp->fdTo = s;
7841     *pr = (ProcRef) cp;
7842 #endif /* !OMIT_SOCKETS */
7843
7844     return 0;
7845 }
7846
7847 int OpenCommPort(name, pr)
7848      char *name;
7849      ProcRef *pr;
7850 {
7851     int fd;
7852     ChildProc *cp;
7853
7854     fd = open(name, 2, 0);
7855     if (fd < 0) return errno;
7856
7857     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7858     cp->kind = CPComm;
7859     cp->pid = 0;
7860     cp->fdFrom = fd;
7861     cp->fdTo = fd;
7862     *pr = (ProcRef) cp;
7863
7864     return 0;
7865 }
7866
7867 int OpenLoopback(pr)
7868      ProcRef *pr;
7869 {
7870     ChildProc *cp;
7871     int to[2], from[2];
7872
7873     SetUpChildIO(to, from);
7874
7875     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7876     cp->kind = CPLoop;
7877     cp->pid = 0;
7878     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7879     cp->fdTo = to[1];
7880     *pr = (ProcRef) cp;
7881
7882     return 0;
7883 }
7884
7885 int OpenRcmd(host, user, cmd, pr)
7886      char *host, *user, *cmd;
7887      ProcRef *pr;
7888 {
7889     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
7890     return -1;
7891 }
7892
7893 #define INPUT_SOURCE_BUF_SIZE 8192
7894
7895 typedef struct {
7896     CPKind kind;
7897     int fd;
7898     int lineByLine;
7899     char *unused;
7900     InputCallback func;
7901     XtInputId xid;
7902     char buf[INPUT_SOURCE_BUF_SIZE];
7903     VOIDSTAR closure;
7904 } InputSource;
7905
7906 void
7907 DoInputCallback(closure, source, xid)
7908      caddr_t closure;
7909      int *source;
7910      XtInputId *xid;
7911 {
7912     InputSource *is = (InputSource *) closure;
7913     int count;
7914     int error;
7915     char *p, *q;
7916
7917     if (is->lineByLine) {
7918         count = read(is->fd, is->unused,
7919                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7920         if (count <= 0) {
7921             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7922             return;
7923         }
7924         is->unused += count;
7925         p = is->buf;
7926         while (p < is->unused) {
7927             q = memchr(p, '\n', is->unused - p);
7928             if (q == NULL) break;
7929             q++;
7930             (is->func)(is, is->closure, p, q - p, 0);
7931             p = q;
7932         }
7933         q = is->buf;
7934         while (p < is->unused) {
7935             *q++ = *p++;
7936         }
7937         is->unused = q;
7938     } else {
7939         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7940         if (count == -1)
7941           error = errno;
7942         else
7943           error = 0;
7944         (is->func)(is, is->closure, is->buf, count, error);
7945     }
7946 }
7947
7948 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7949      ProcRef pr;
7950      int lineByLine;
7951      InputCallback func;
7952      VOIDSTAR closure;
7953 {
7954     InputSource *is;
7955     ChildProc *cp = (ChildProc *) pr;
7956
7957     is = (InputSource *) calloc(1, sizeof(InputSource));
7958     is->lineByLine = lineByLine;
7959     is->func = func;
7960     if (pr == NoProc) {
7961         is->kind = CPReal;
7962         is->fd = fileno(stdin);
7963     } else {
7964         is->kind = cp->kind;
7965         is->fd = cp->fdFrom;
7966     }
7967     if (lineByLine) {
7968         is->unused = is->buf;
7969     }
7970
7971     is->xid = XtAppAddInput(appContext, is->fd,
7972                             (XtPointer) (XtInputReadMask),
7973                             (XtInputCallbackProc) DoInputCallback,
7974                             (XtPointer) is);
7975     is->closure = closure;
7976     return (InputSourceRef) is;
7977 }
7978
7979 void
7980 RemoveInputSource(isr)
7981      InputSourceRef isr;
7982 {
7983     InputSource *is = (InputSource *) isr;
7984
7985     if (is->xid == 0) return;
7986     XtRemoveInput(is->xid);
7987     is->xid = 0;
7988 }
7989
7990 int OutputToProcess(pr, message, count, outError)
7991      ProcRef pr;
7992      char *message;
7993      int count;
7994      int *outError;
7995 {
7996     static int line = 0;
7997     ChildProc *cp = (ChildProc *) pr;
7998     int outCount;
7999
8000     if (pr == NoProc)
8001     {
8002         if (appData.noJoin || !appData.useInternalWrap)
8003             outCount = fwrite(message, 1, count, stdout);
8004         else
8005         {
8006             int width = get_term_width();
8007             int len = wrap(NULL, message, count, width, &line);
8008             char *msg = malloc(len);
8009             int dbgchk;
8010
8011             if (!msg)
8012                 outCount = fwrite(message, 1, count, stdout);
8013             else
8014             {
8015                 dbgchk = wrap(msg, message, count, width, &line);
8016                 if (dbgchk != len && appData.debugMode)
8017                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8018                 outCount = fwrite(msg, 1, dbgchk, stdout);
8019                 free(msg);
8020             }
8021         }
8022     }
8023     else
8024       outCount = write(cp->fdTo, message, count);
8025
8026     if (outCount == -1)
8027       *outError = errno;
8028     else
8029       *outError = 0;
8030
8031     return outCount;
8032 }
8033
8034 /* Output message to process, with "ms" milliseconds of delay
8035    between each character. This is needed when sending the logon
8036    script to ICC, which for some reason doesn't like the
8037    instantaneous send. */
8038 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8039      ProcRef pr;
8040      char *message;
8041      int count;
8042      int *outError;
8043      long msdelay;
8044 {
8045     ChildProc *cp = (ChildProc *) pr;
8046     int outCount = 0;
8047     int r;
8048
8049     while (count--) {
8050         r = write(cp->fdTo, message++, 1);
8051         if (r == -1) {
8052             *outError = errno;
8053             return outCount;
8054         }
8055         ++outCount;
8056         if (msdelay >= 0)
8057           TimeDelay(msdelay);
8058     }
8059
8060     return outCount;
8061 }
8062
8063 /****   Animation code by Hugh Fisher, DCS, ANU.
8064
8065         Known problem: if a window overlapping the board is
8066         moved away while a piece is being animated underneath,
8067         the newly exposed area won't be updated properly.
8068         I can live with this.
8069
8070         Known problem: if you look carefully at the animation
8071         of pieces in mono mode, they are being drawn as solid
8072         shapes without interior detail while moving. Fixing
8073         this would be a major complication for minimal return.
8074 ****/
8075
8076 /*      Masks for XPM pieces. Black and white pieces can have
8077         different shapes, but in the interest of retaining my
8078         sanity pieces must have the same outline on both light
8079         and dark squares, and all pieces must use the same
8080         background square colors/images.                */
8081
8082 static int xpmDone = 0;
8083
8084 static void
8085 CreateAnimMasks (pieceDepth)
8086      int pieceDepth;
8087 {
8088   ChessSquare   piece;
8089   Pixmap        buf;
8090   GC            bufGC, maskGC;
8091   int           kind, n;
8092   unsigned long plane;
8093   XGCValues     values;
8094
8095   /* Need a bitmap just to get a GC with right depth */
8096   buf = XCreatePixmap(xDisplay, xBoardWindow,
8097                         8, 8, 1);
8098   values.foreground = 1;
8099   values.background = 0;
8100   /* Don't use XtGetGC, not read only */
8101   maskGC = XCreateGC(xDisplay, buf,
8102                     GCForeground | GCBackground, &values);
8103   XFreePixmap(xDisplay, buf);
8104
8105   buf = XCreatePixmap(xDisplay, xBoardWindow,
8106                       squareSize, squareSize, pieceDepth);
8107   values.foreground = XBlackPixel(xDisplay, xScreen);
8108   values.background = XWhitePixel(xDisplay, xScreen);
8109   bufGC = XCreateGC(xDisplay, buf,
8110                     GCForeground | GCBackground, &values);
8111
8112   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8113     /* Begin with empty mask */
8114     if(!xpmDone) // [HGM] pieces: keep using existing
8115     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8116                                  squareSize, squareSize, 1);
8117     XSetFunction(xDisplay, maskGC, GXclear);
8118     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8119                    0, 0, squareSize, squareSize);
8120
8121     /* Take a copy of the piece */
8122     if (White(piece))
8123       kind = 0;
8124     else
8125       kind = 2;
8126     XSetFunction(xDisplay, bufGC, GXcopy);
8127     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8128               buf, bufGC,
8129               0, 0, squareSize, squareSize, 0, 0);
8130
8131     /* XOR the background (light) over the piece */
8132     XSetFunction(xDisplay, bufGC, GXxor);
8133     if (useImageSqs)
8134       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8135                 0, 0, squareSize, squareSize, 0, 0);
8136     else {
8137       XSetForeground(xDisplay, bufGC, lightSquareColor);
8138       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8139     }
8140
8141     /* We now have an inverted piece image with the background
8142        erased. Construct mask by just selecting all the non-zero
8143        pixels - no need to reconstruct the original image.      */
8144     XSetFunction(xDisplay, maskGC, GXor);
8145     plane = 1;
8146     /* Might be quicker to download an XImage and create bitmap
8147        data from it rather than this N copies per piece, but it
8148        only takes a fraction of a second and there is a much
8149        longer delay for loading the pieces.             */
8150     for (n = 0; n < pieceDepth; n ++) {
8151       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8152                  0, 0, squareSize, squareSize,
8153                  0, 0, plane);
8154       plane = plane << 1;
8155     }
8156   }
8157   /* Clean up */
8158   XFreePixmap(xDisplay, buf);
8159   XFreeGC(xDisplay, bufGC);
8160   XFreeGC(xDisplay, maskGC);
8161 }
8162
8163 static void
8164 InitAnimState (anim, info)
8165   AnimState * anim;
8166   XWindowAttributes * info;
8167 {
8168   XtGCMask  mask;
8169   XGCValues values;
8170
8171   /* Each buffer is square size, same depth as window */
8172   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8173                         squareSize, squareSize, info->depth);
8174   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8175                         squareSize, squareSize, info->depth);
8176
8177   /* Create a plain GC for blitting */
8178   mask = GCForeground | GCBackground | GCFunction |
8179          GCPlaneMask | GCGraphicsExposures;
8180   values.foreground = XBlackPixel(xDisplay, xScreen);
8181   values.background = XWhitePixel(xDisplay, xScreen);
8182   values.function   = GXcopy;
8183   values.plane_mask = AllPlanes;
8184   values.graphics_exposures = False;
8185   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8186
8187   /* Piece will be copied from an existing context at
8188      the start of each new animation/drag. */
8189   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8190
8191   /* Outline will be a read-only copy of an existing */
8192   anim->outlineGC = None;
8193 }
8194
8195 static void
8196 CreateAnimVars ()
8197 {
8198   XWindowAttributes info;
8199
8200   if (xpmDone && gameInfo.variant == oldVariant) return;
8201   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
8202   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8203
8204   InitAnimState(&game, &info);
8205   InitAnimState(&player, &info);
8206
8207   /* For XPM pieces, we need bitmaps to use as masks. */
8208   if (useImages)
8209     CreateAnimMasks(info.depth), xpmDone = 1;
8210 }
8211
8212 #ifndef HAVE_USLEEP
8213
8214 static Boolean frameWaiting;
8215
8216 static RETSIGTYPE FrameAlarm (sig)
8217      int sig;
8218 {
8219   frameWaiting = False;
8220   /* In case System-V style signals.  Needed?? */
8221   signal(SIGALRM, FrameAlarm);
8222 }
8223
8224 static void
8225 FrameDelay (time)
8226      int time;
8227 {
8228   struct itimerval delay;
8229
8230   XSync(xDisplay, False);
8231
8232   if (time > 0) {
8233     frameWaiting = True;
8234     signal(SIGALRM, FrameAlarm);
8235     delay.it_interval.tv_sec =
8236       delay.it_value.tv_sec = time / 1000;
8237     delay.it_interval.tv_usec =
8238       delay.it_value.tv_usec = (time % 1000) * 1000;
8239     setitimer(ITIMER_REAL, &delay, NULL);
8240     while (frameWaiting) pause();
8241     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8242     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8243     setitimer(ITIMER_REAL, &delay, NULL);
8244   }
8245 }
8246
8247 #else
8248
8249 static void
8250 FrameDelay (time)
8251      int time;
8252 {
8253   XSync(xDisplay, False);
8254   if (time > 0)
8255     usleep(time * 1000);
8256 }
8257
8258 #endif
8259
8260 void
8261 DoSleep(int n)
8262 {
8263     FrameDelay(n);
8264 }
8265
8266 /*      Convert board position to corner of screen rect and color       */
8267
8268 static void
8269 ScreenSquare(column, row, pt, color)
8270      int column; int row; XPoint * pt; int * color;
8271 {
8272   if (flipView) {
8273     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8274     pt->y = lineGap + row * (squareSize + lineGap);
8275   } else {
8276     pt->x = lineGap + column * (squareSize + lineGap);
8277     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8278   }
8279   *color = SquareColor(row, column);
8280 }
8281
8282 /*      Convert window coords to square                 */
8283
8284 static void
8285 BoardSquare(x, y, column, row)
8286      int x; int y; int * column; int * row;
8287 {
8288   *column = EventToSquare(x, BOARD_WIDTH);
8289   if (flipView && *column >= 0)
8290     *column = BOARD_WIDTH - 1 - *column;
8291   *row = EventToSquare(y, BOARD_HEIGHT);
8292   if (!flipView && *row >= 0)
8293     *row = BOARD_HEIGHT - 1 - *row;
8294 }
8295
8296 /*   Utilities  */
8297
8298 #undef Max  /* just in case */
8299 #undef Min
8300 #define Max(a, b) ((a) > (b) ? (a) : (b))
8301 #define Min(a, b) ((a) < (b) ? (a) : (b))
8302
8303 static void
8304 SetRect(rect, x, y, width, height)
8305      XRectangle * rect; int x; int y; int width; int height;
8306 {
8307   rect->x = x;
8308   rect->y = y;
8309   rect->width  = width;
8310   rect->height = height;
8311 }
8312
8313 /*      Test if two frames overlap. If they do, return
8314         intersection rect within old and location of
8315         that rect within new. */
8316
8317 static Boolean
8318 Intersect(old, new, size, area, pt)
8319      XPoint * old; XPoint * new;
8320      int size; XRectangle * area; XPoint * pt;
8321 {
8322   if (old->x > new->x + size || new->x > old->x + size ||
8323       old->y > new->y + size || new->y > old->y + size) {
8324     return False;
8325   } else {
8326     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8327             size - abs(old->x - new->x), size - abs(old->y - new->y));
8328     pt->x = Max(old->x - new->x, 0);
8329     pt->y = Max(old->y - new->y, 0);
8330     return True;
8331   }
8332 }
8333
8334 /*      For two overlapping frames, return the rect(s)
8335         in the old that do not intersect with the new.   */
8336
8337 static void
8338 CalcUpdateRects(old, new, size, update, nUpdates)
8339      XPoint * old; XPoint * new; int size;
8340      XRectangle update[]; int * nUpdates;
8341 {
8342   int        count;
8343
8344   /* If old = new (shouldn't happen) then nothing to draw */
8345   if (old->x == new->x && old->y == new->y) {
8346     *nUpdates = 0;
8347     return;
8348   }
8349   /* Work out what bits overlap. Since we know the rects
8350      are the same size we don't need a full intersect calc. */
8351   count = 0;
8352   /* Top or bottom edge? */
8353   if (new->y > old->y) {
8354     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8355     count ++;
8356   } else if (old->y > new->y) {
8357     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8358                               size, old->y - new->y);
8359     count ++;
8360   }
8361   /* Left or right edge - don't overlap any update calculated above. */
8362   if (new->x > old->x) {
8363     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8364                               new->x - old->x, size - abs(new->y - old->y));
8365     count ++;
8366   } else if (old->x > new->x) {
8367     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8368                               old->x - new->x, size - abs(new->y - old->y));
8369     count ++;
8370   }
8371   /* Done */
8372   *nUpdates = count;
8373 }
8374
8375 /*      Generate a series of frame coords from start->mid->finish.
8376         The movement rate doubles until the half way point is
8377         reached, then halves back down to the final destination,
8378         which gives a nice slow in/out effect. The algorithmn
8379         may seem to generate too many intermediates for short
8380         moves, but remember that the purpose is to attract the
8381         viewers attention to the piece about to be moved and
8382         then to where it ends up. Too few frames would be less
8383         noticeable.                                             */
8384
8385 static void
8386 Tween(start, mid, finish, factor, frames, nFrames)
8387      XPoint * start; XPoint * mid;
8388      XPoint * finish; int factor;
8389      XPoint frames[]; int * nFrames;
8390 {
8391   int fraction, n, count;
8392
8393   count = 0;
8394
8395   /* Slow in, stepping 1/16th, then 1/8th, ... */
8396   fraction = 1;
8397   for (n = 0; n < factor; n++)
8398     fraction *= 2;
8399   for (n = 0; n < factor; n++) {
8400     frames[count].x = start->x + (mid->x - start->x) / fraction;
8401     frames[count].y = start->y + (mid->y - start->y) / fraction;
8402     count ++;
8403     fraction = fraction / 2;
8404   }
8405
8406   /* Midpoint */
8407   frames[count] = *mid;
8408   count ++;
8409
8410   /* Slow out, stepping 1/2, then 1/4, ... */
8411   fraction = 2;
8412   for (n = 0; n < factor; n++) {
8413     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8414     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8415     count ++;
8416     fraction = fraction * 2;
8417   }
8418   *nFrames = count;
8419 }
8420
8421 /*      Draw a piece on the screen without disturbing what's there      */
8422
8423 static void
8424 SelectGCMask(piece, clip, outline, mask)
8425      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8426 {
8427   GC source;
8428
8429   /* Bitmap for piece being moved. */
8430   if (appData.monoMode) {
8431       *mask = *pieceToSolid(piece);
8432   } else if (useImages) {
8433 #if HAVE_LIBXPM
8434       *mask = xpmMask[piece];
8435 #else
8436       *mask = ximMaskPm[piece];
8437 #endif
8438   } else {
8439       *mask = *pieceToSolid(piece);
8440   }
8441
8442   /* GC for piece being moved. Square color doesn't matter, but
8443      since it gets modified we make a copy of the original. */
8444   if (White(piece)) {
8445     if (appData.monoMode)
8446       source = bwPieceGC;
8447     else
8448       source = wlPieceGC;
8449   } else {
8450     if (appData.monoMode)
8451       source = wbPieceGC;
8452     else
8453       source = blPieceGC;
8454   }
8455   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8456
8457   /* Outline only used in mono mode and is not modified */
8458   if (White(piece))
8459     *outline = bwPieceGC;
8460   else
8461     *outline = wbPieceGC;
8462 }
8463
8464 static void
8465 OverlayPiece(piece, clip, outline,  dest)
8466      ChessSquare piece; GC clip; GC outline; Drawable dest;
8467 {
8468   int   kind;
8469
8470   if (!useImages) {
8471     /* Draw solid rectangle which will be clipped to shape of piece */
8472     XFillRectangle(xDisplay, dest, clip,
8473                    0, 0, squareSize, squareSize);
8474     if (appData.monoMode)
8475       /* Also draw outline in contrasting color for black
8476          on black / white on white cases                */
8477       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8478                  0, 0, squareSize, squareSize, 0, 0, 1);
8479   } else {
8480     /* Copy the piece */
8481     if (White(piece))
8482       kind = 0;
8483     else
8484       kind = 2;
8485     if(appData.upsideDown && flipView) kind ^= 2;
8486     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8487               dest, clip,
8488               0, 0, squareSize, squareSize,
8489               0, 0);
8490   }
8491 }
8492
8493 /* Animate the movement of a single piece */
8494
8495 static void
8496 BeginAnimation(anim, piece, startColor, start)
8497      AnimState *anim;
8498      ChessSquare piece;
8499      int startColor;
8500      XPoint * start;
8501 {
8502   Pixmap mask;
8503
8504   if(appData.upsideDown && flipView) piece += piece < BlackPawn ? BlackPawn : -BlackPawn;
8505   /* The old buffer is initialised with the start square (empty) */
8506   BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
8507   anim->prevFrame = *start;
8508
8509   /* The piece will be drawn using its own bitmap as a matte    */
8510   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8511   XSetClipMask(xDisplay, anim->pieceGC, mask);
8512 }
8513
8514 static void
8515 AnimationFrame(anim, frame, piece)
8516      AnimState *anim;
8517      XPoint *frame;
8518      ChessSquare piece;
8519 {
8520   XRectangle updates[4];
8521   XRectangle overlap;
8522   XPoint     pt;
8523   int        count, i;
8524
8525   /* Save what we are about to draw into the new buffer */
8526   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8527             frame->x, frame->y, squareSize, squareSize,
8528             0, 0);
8529
8530   /* Erase bits of the previous frame */
8531   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8532     /* Where the new frame overlapped the previous,
8533        the contents in newBuf are wrong. */
8534     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8535               overlap.x, overlap.y,
8536               overlap.width, overlap.height,
8537               pt.x, pt.y);
8538     /* Repaint the areas in the old that don't overlap new */
8539     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8540     for (i = 0; i < count; i++)
8541       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8542                 updates[i].x - anim->prevFrame.x,
8543                 updates[i].y - anim->prevFrame.y,
8544                 updates[i].width, updates[i].height,
8545                 updates[i].x, updates[i].y);
8546   } else {
8547     /* Easy when no overlap */
8548     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8549                   0, 0, squareSize, squareSize,
8550                   anim->prevFrame.x, anim->prevFrame.y);
8551   }
8552
8553   /* Save this frame for next time round */
8554   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8555                 0, 0, squareSize, squareSize,
8556                 0, 0);
8557   anim->prevFrame = *frame;
8558
8559   /* Draw piece over original screen contents, not current,
8560      and copy entire rect. Wipes out overlapping piece images. */
8561   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8562   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8563                 0, 0, squareSize, squareSize,
8564                 frame->x, frame->y);
8565 }
8566
8567 static void
8568 EndAnimation (anim, finish)
8569      AnimState *anim;
8570      XPoint *finish;
8571 {
8572   XRectangle updates[4];
8573   XRectangle overlap;
8574   XPoint     pt;
8575   int        count, i;
8576
8577   /* The main code will redraw the final square, so we
8578      only need to erase the bits that don't overlap.    */
8579   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8580     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8581     for (i = 0; i < count; i++)
8582       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8583                 updates[i].x - anim->prevFrame.x,
8584                 updates[i].y - anim->prevFrame.y,
8585                 updates[i].width, updates[i].height,
8586                 updates[i].x, updates[i].y);
8587   } else {
8588     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8589                 0, 0, squareSize, squareSize,
8590                 anim->prevFrame.x, anim->prevFrame.y);
8591   }
8592 }
8593
8594 static void
8595 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8596      AnimState *anim;
8597      ChessSquare piece; int startColor;
8598      XPoint * start; XPoint * finish;
8599      XPoint frames[]; int nFrames;
8600 {
8601   int n;
8602
8603   BeginAnimation(anim, piece, startColor, start);
8604   for (n = 0; n < nFrames; n++) {
8605     AnimationFrame(anim, &(frames[n]), piece);
8606     FrameDelay(appData.animSpeed);
8607   }
8608   EndAnimation(anim, finish);
8609 }
8610
8611 void
8612 AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
8613 {
8614     int i, x, y;
8615     ChessSquare piece = board[fromY][toY];
8616     board[fromY][toY] = EmptySquare;
8617     DrawPosition(FALSE, board);
8618     if (flipView) {
8619         x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
8620         y = lineGap + toY * (squareSize + lineGap);
8621     } else {
8622         x = lineGap + toX * (squareSize + lineGap);
8623         y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
8624     }
8625     for(i=1; i<4*kFactor; i++) {
8626         int r = squareSize * 9 * i/(20*kFactor - 5);
8627         XFillArc(xDisplay, xBoardWindow, highlineGC,
8628                 x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
8629         FrameDelay(appData.animSpeed);
8630     }
8631     board[fromY][toY] = piece;
8632 }
8633
8634 /* Main control logic for deciding what to animate and how */
8635
8636 void
8637 AnimateMove(board, fromX, fromY, toX, toY)
8638      Board board;
8639      int fromX;
8640      int fromY;
8641      int toX;
8642      int toY;
8643 {
8644   ChessSquare piece;
8645   int hop;
8646   XPoint      start, finish, mid;
8647   XPoint      frames[kFactor * 2 + 1];
8648   int         nFrames, startColor, endColor;
8649
8650   /* Are we animating? */
8651   if (!appData.animate || appData.blindfold)
8652     return;
8653
8654   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8655      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8656         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8657
8658   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8659   piece = board[fromY][fromX];
8660   if (piece >= EmptySquare) return;
8661
8662 #if DONT_HOP
8663   hop = FALSE;
8664 #else
8665   hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
8666 #endif
8667
8668   if (appData.debugMode) {
8669       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8670                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8671              piece, fromX, fromY, toX, toY);  }
8672
8673   ScreenSquare(fromX, fromY, &start, &startColor);
8674   ScreenSquare(toX, toY, &finish, &endColor);
8675
8676   if (hop) {
8677     /* Knight: make straight movement then diagonal */
8678     if (abs(toY - fromY) < abs(toX - fromX)) {
8679        mid.x = start.x + (finish.x - start.x) / 2;
8680        mid.y = start.y;
8681      } else {
8682        mid.x = start.x;
8683        mid.y = start.y + (finish.y - start.y) / 2;
8684      }
8685   } else {
8686     mid.x = start.x + (finish.x - start.x) / 2;
8687     mid.y = start.y + (finish.y - start.y) / 2;
8688   }
8689
8690   /* Don't use as many frames for very short moves */
8691   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8692     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8693   else
8694     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8695   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8696   if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
8697     int i,j;
8698     for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
8699       if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
8700   }
8701
8702   /* Be sure end square is redrawn */
8703   damage[0][toY][toX] = True;
8704 }
8705
8706 void
8707 DragPieceBegin(x, y, instantly)
8708      int x; int y; Boolean instantly;
8709 {
8710     int  boardX, boardY, color;
8711     XPoint corner;
8712
8713     /* Are we animating? */
8714     if (!appData.animateDragging || appData.blindfold)
8715       return;
8716
8717     /* Figure out which square we start in and the
8718        mouse position relative to top left corner. */
8719     BoardSquare(x, y, &boardX, &boardY);
8720     player.startBoardX = boardX;
8721     player.startBoardY = boardY;
8722     ScreenSquare(boardX, boardY, &corner, &color);
8723     player.startSquare  = corner;
8724     player.startColor   = color;
8725     /* As soon as we start dragging, the piece will jump slightly to
8726        be centered over the mouse pointer. */
8727     player.mouseDelta.x = squareSize/2;
8728     player.mouseDelta.y = squareSize/2;
8729     /* Initialise animation */
8730     player.dragPiece = PieceForSquare(boardX, boardY);
8731     /* Sanity check */
8732     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8733         player.dragActive = True;
8734         BeginAnimation(&player, player.dragPiece, color, &corner);
8735         /* Mark this square as needing to be redrawn. Note that
8736            we don't remove the piece though, since logically (ie
8737            as seen by opponent) the move hasn't been made yet. */
8738            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8739               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8740            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8741                      corner.x, corner.y, squareSize, squareSize,
8742                      0, 0); // [HGM] zh: unstack in stead of grab
8743            if(gatingPiece != EmptySquare) {
8744                /* Kludge alert: When gating we want the introduced
8745                   piece to appear on the from square. To generate an
8746                   image of it, we draw it on the board, copy the image,
8747                   and draw the original piece again. */
8748                ChessSquare piece = boards[currentMove][boardY][boardX];
8749                DrawSquare(boardY, boardX, gatingPiece, 0);
8750                XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8751                      corner.x, corner.y, squareSize, squareSize, 0, 0);
8752                DrawSquare(boardY, boardX, piece, 0);
8753            }
8754         damage[0][boardY][boardX] = True;
8755     } else {
8756         player.dragActive = False;
8757     }
8758 }
8759
8760 void
8761 ChangeDragPiece(ChessSquare piece)
8762 {
8763   Pixmap mask;
8764   player.dragPiece = piece;
8765   /* The piece will be drawn using its own bitmap as a matte    */
8766   SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
8767   XSetClipMask(xDisplay, player.pieceGC, mask);
8768 }
8769
8770 static void
8771 DragPieceMove(x, y)
8772      int x; int y;
8773 {
8774     XPoint corner;
8775
8776     /* Are we animating? */
8777     if (!appData.animateDragging || appData.blindfold)
8778       return;
8779
8780     /* Sanity check */
8781     if (! player.dragActive)
8782       return;
8783     /* Move piece, maintaining same relative position
8784        of mouse within square    */
8785     corner.x = x - player.mouseDelta.x;
8786     corner.y = y - player.mouseDelta.y;
8787     AnimationFrame(&player, &corner, player.dragPiece);
8788 #if HIGHDRAG*0
8789     if (appData.highlightDragging) {
8790         int boardX, boardY;
8791         BoardSquare(x, y, &boardX, &boardY);
8792         SetHighlights(fromX, fromY, boardX, boardY);
8793     }
8794 #endif
8795 }
8796
8797 void
8798 DragPieceEnd(x, y)
8799      int x; int y;
8800 {
8801     int boardX, boardY, color;
8802     XPoint corner;
8803
8804     /* Are we animating? */
8805     if (!appData.animateDragging || appData.blindfold)
8806       return;
8807
8808     /* Sanity check */
8809     if (! player.dragActive)
8810       return;
8811     /* Last frame in sequence is square piece is
8812        placed on, which may not match mouse exactly. */
8813     BoardSquare(x, y, &boardX, &boardY);
8814     ScreenSquare(boardX, boardY, &corner, &color);
8815     EndAnimation(&player, &corner);
8816
8817     /* Be sure end square is redrawn */
8818     damage[0][boardY][boardX] = True;
8819
8820     /* This prevents weird things happening with fast successive
8821        clicks which on my Sun at least can cause motion events
8822        without corresponding press/release. */
8823     player.dragActive = False;
8824 }
8825
8826 /* Handle expose event while piece being dragged */
8827
8828 static void
8829 DrawDragPiece ()
8830 {
8831   if (!player.dragActive || appData.blindfold)
8832     return;
8833
8834   /* What we're doing: logically, the move hasn't been made yet,
8835      so the piece is still in it's original square. But visually
8836      it's being dragged around the board. So we erase the square
8837      that the piece is on and draw it at the last known drag point. */
8838   BlankSquare(player.startSquare.x, player.startSquare.y,
8839                 player.startColor, EmptySquare, xBoardWindow, 1);
8840   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8841   damage[0][player.startBoardY][player.startBoardX] = TRUE;
8842 }
8843
8844 #include <sys/ioctl.h>
8845 int get_term_width()
8846 {
8847     int fd, default_width;
8848
8849     fd = STDIN_FILENO;
8850     default_width = 79; // this is FICS default anyway...
8851
8852 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
8853     struct ttysize win;
8854     if (!ioctl(fd, TIOCGSIZE, &win))
8855         default_width = win.ts_cols;
8856 #elif defined(TIOCGWINSZ)
8857     struct winsize win;
8858     if (!ioctl(fd, TIOCGWINSZ, &win))
8859         default_width = win.ws_col;
8860 #endif
8861     return default_width;
8862 }
8863
8864 void
8865 update_ics_width()
8866 {
8867   static int old_width = 0;
8868   int new_width = get_term_width();
8869
8870   if (old_width != new_width)
8871     ics_printf("set width %d\n", new_width);
8872   old_width = new_width;
8873 }
8874
8875 void NotifyFrontendLogin()
8876 {
8877     update_ics_width();
8878 }
8879
8880 /* [AS] Arrow highlighting support */
8881
8882 static double A_WIDTH = 5; /* Width of arrow body */
8883
8884 #define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
8885 #define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
8886
8887 static double Sqr( double x )
8888 {
8889     return x*x;
8890 }
8891
8892 static int Round( double x )
8893 {
8894     return (int) (x + 0.5);
8895 }
8896
8897 void SquareToPos(int rank, int file, int *x, int *y)
8898 {
8899     if (flipView) {
8900         *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
8901         *y = lineGap + rank * (squareSize + lineGap);
8902     } else {
8903         *x = lineGap + file * (squareSize + lineGap);
8904         *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
8905     }
8906 }
8907
8908 /* Draw an arrow between two points using current settings */
8909 void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
8910 {
8911     XPoint arrow[7];
8912     double dx, dy, j, k, x, y;
8913
8914     if( d_x == s_x ) {
8915         int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8916
8917         arrow[0].x = s_x + A_WIDTH + 0.5;
8918         arrow[0].y = s_y;
8919
8920         arrow[1].x = s_x + A_WIDTH + 0.5;
8921         arrow[1].y = d_y - h;
8922
8923         arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8924         arrow[2].y = d_y - h;
8925
8926         arrow[3].x = d_x;
8927         arrow[3].y = d_y;
8928
8929         arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
8930         arrow[5].y = d_y - h;
8931
8932         arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8933         arrow[4].y = d_y - h;
8934
8935         arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
8936         arrow[6].y = s_y;
8937     }
8938     else if( d_y == s_y ) {
8939         int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
8940
8941         arrow[0].x = s_x;
8942         arrow[0].y = s_y + A_WIDTH + 0.5;
8943
8944         arrow[1].x = d_x - w;
8945         arrow[1].y = s_y + A_WIDTH + 0.5;
8946
8947         arrow[2].x = d_x - w;
8948         arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8949
8950         arrow[3].x = d_x;
8951         arrow[3].y = d_y;
8952
8953         arrow[5].x = d_x - w;
8954         arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
8955
8956         arrow[4].x = d_x - w;
8957         arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
8958
8959         arrow[6].x = s_x;
8960         arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
8961     }
8962     else {
8963         /* [AS] Needed a lot of paper for this! :-) */
8964         dy = (double) (d_y - s_y) / (double) (d_x - s_x);
8965         dx = (double) (s_x - d_x) / (double) (s_y - d_y);
8966
8967         j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
8968
8969         k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
8970
8971         x = s_x;
8972         y = s_y;
8973
8974         arrow[0].x = Round(x - j);
8975         arrow[0].y = Round(y + j*dx);
8976
8977         arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
8978         arrow[1].y = Round(arrow[0].y - 2*j*dx);
8979
8980         if( d_x > s_x ) {
8981             x = (double) d_x - k;
8982             y = (double) d_y - k*dy;
8983         }
8984         else {
8985             x = (double) d_x + k;
8986             y = (double) d_y + k*dy;
8987         }
8988
8989         x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
8990
8991         arrow[6].x = Round(x - j);
8992         arrow[6].y = Round(y + j*dx);
8993
8994         arrow[2].x = Round(arrow[6].x + 2*j);
8995         arrow[2].y = Round(arrow[6].y - 2*j*dx);
8996
8997         arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
8998         arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
8999
9000         arrow[4].x = d_x;
9001         arrow[4].y = d_y;
9002
9003         arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
9004         arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
9005     }
9006
9007     XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
9008 //    Polygon( hdc, arrow, 7 );
9009 }
9010
9011 /* [AS] Draw an arrow between two squares */
9012 void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
9013 {
9014     int s_x, s_y, d_x, d_y, hor, vert, i;
9015
9016     if( s_col == d_col && s_row == d_row ) {
9017         return;
9018     }
9019
9020     /* Get source and destination points */
9021     SquareToPos( s_row, s_col, &s_x, &s_y);
9022     SquareToPos( d_row, d_col, &d_x, &d_y);
9023
9024     if( d_y > s_y ) {
9025         d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
9026     }
9027     else if( d_y < s_y ) {
9028         d_y += squareSize / 2 + squareSize / 4;
9029     }
9030     else {
9031         d_y += squareSize / 2;
9032     }
9033
9034     if( d_x > s_x ) {
9035         d_x += squareSize / 2 - squareSize / 4;
9036     }
9037     else if( d_x < s_x ) {
9038         d_x += squareSize / 2 + squareSize / 4;
9039     }
9040     else {
9041         d_x += squareSize / 2;
9042     }
9043
9044     s_x += squareSize / 2;
9045     s_y += squareSize / 2;
9046
9047     /* Adjust width */
9048     A_WIDTH = squareSize / 14.; //[HGM] make float
9049
9050     DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
9051
9052     hor = 64*s_col + 32; vert = 64*s_row + 32;
9053     for(i=0; i<= 64; i++) {
9054             damage[0][vert+6>>6][hor+6>>6] = True;
9055             damage[0][vert-6>>6][hor+6>>6] = True;
9056             damage[0][vert+6>>6][hor-6>>6] = True;
9057             damage[0][vert-6>>6][hor-6>>6] = True;
9058             hor += d_col - s_col; vert += d_row - s_row;
9059     }
9060 }
9061
9062 Boolean IsDrawArrowEnabled()
9063 {
9064     return appData.highlightMoveWithArrow && squareSize >= 32;
9065 }
9066
9067 void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
9068 {
9069     if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
9070         DrawArrowBetweenSquares(fromX, fromY, toX, toY);
9071 }
9072
9073 void UpdateLogos(int displ)
9074 {
9075     return; // no logos in XBoard yet
9076 }
9077