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