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