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