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