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