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