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