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