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