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