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