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