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