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