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