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