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