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