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