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