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