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