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