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