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