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