Implement -flipBlack in XBoard
[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     if(appData.upsideDown && flipView) kind ^= 2; // swap white and black pieces
4219     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4220               dest, wlPieceGC, 0, 0,
4221               squareSize, squareSize, x, y);
4222 }
4223
4224 typedef void (*DrawFunc)();
4225
4226 DrawFunc ChooseDrawFunc()
4227 {
4228     if (appData.monoMode) {
4229         if (DefaultDepth(xDisplay, xScreen) == 1) {
4230             return monoDrawPiece_1bit;
4231         } else {
4232             return monoDrawPiece;
4233         }
4234     } else {
4235         if (useImages)
4236           return colorDrawPieceImage;
4237         else
4238           return colorDrawPiece;
4239     }
4240 }
4241
4242 /* [HR] determine square color depending on chess variant. */
4243 static int SquareColor(row, column)
4244      int row, column;
4245 {
4246     int square_color;
4247
4248     if (gameInfo.variant == VariantXiangqi) {
4249         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4250             square_color = 1;
4251         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4252             square_color = 0;
4253         } else if (row <= 4) {
4254             square_color = 0;
4255         } else {
4256             square_color = 1;
4257         }
4258     } else {
4259         square_color = ((column + row) % 2) == 1;
4260     }
4261
4262     /* [hgm] holdings: next line makes all holdings squares light */
4263     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4264
4265     return square_color;
4266 }
4267
4268 void DrawSquare(row, column, piece, do_flash)
4269      int row, column, do_flash;
4270      ChessSquare piece;
4271 {
4272     int square_color, x, y, direction, font_ascent, font_descent;
4273     int i;
4274     char string[2];
4275     XCharStruct overall;
4276     DrawFunc drawfunc;
4277     int flash_delay;
4278
4279     /* Calculate delay in milliseconds (2-delays per complete flash) */
4280     flash_delay = 500 / appData.flashRate;
4281
4282     if (flipView) {
4283         x = lineGap + ((BOARD_WIDTH-1)-column) *
4284           (squareSize + lineGap);
4285         y = lineGap + row * (squareSize + lineGap);
4286     } else {
4287         x = lineGap + column * (squareSize + lineGap);
4288         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4289           (squareSize + lineGap);
4290     }
4291
4292     if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4293
4294     square_color = SquareColor(row, column);
4295
4296     if ( // [HGM] holdings: blank out area between board and holdings
4297                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4298               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4299                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4300                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4301
4302                         // [HGM] print piece counts next to holdings
4303                         string[1] = NULLCHAR;
4304                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4305                             string[0] = '0' + piece;
4306                             XTextExtents(countFontStruct, string, 1, &direction,
4307                                  &font_ascent, &font_descent, &overall);
4308                             if (appData.monoMode) {
4309                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4310                                                  x + squareSize - overall.width - 2,
4311                                                  y + font_ascent + 1, string, 1);
4312                             } else {
4313                                 XDrawString(xDisplay, xBoardWindow, countGC,
4314                                             x + squareSize - overall.width - 2,
4315                                             y + font_ascent + 1, string, 1);
4316                             }
4317                         }
4318                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4319                             string[0] = '0' + piece;
4320                             XTextExtents(countFontStruct, string, 1, &direction,
4321                                          &font_ascent, &font_descent, &overall);
4322                             if (appData.monoMode) {
4323                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4324                                                  x + 2, y + font_ascent + 1, string, 1);
4325                             } else {
4326                                 XDrawString(xDisplay, xBoardWindow, countGC,
4327                                             x + 2, y + font_ascent + 1, string, 1);
4328                             }
4329                         }
4330     } else {
4331             if (piece == EmptySquare || appData.blindfold) {
4332                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4333             } else {
4334                         drawfunc = ChooseDrawFunc();
4335                         if (do_flash && appData.flashCount > 0) {
4336                             for (i=0; i<appData.flashCount; ++i) {
4337
4338                                         drawfunc(piece, square_color, x, y, xBoardWindow);
4339                                         XSync(xDisplay, False);
4340                                         do_flash_delay(flash_delay);
4341
4342                                         BlankSquare(x, y, square_color, piece, xBoardWindow);
4343                                         XSync(xDisplay, False);
4344                                         do_flash_delay(flash_delay);
4345                             }
4346                         }
4347                         drawfunc(piece, square_color, x, y, xBoardWindow);
4348         }
4349         }
4350
4351     string[1] = NULLCHAR;
4352     if (appData.showCoords && row == (flipView ? BOARD_HEIGHT-1 : 0)
4353                 && column >= BOARD_LEFT && column < BOARD_RGHT) {
4354         string[0] = 'a' + column - BOARD_LEFT;
4355         XTextExtents(coordFontStruct, string, 1, &direction,
4356                      &font_ascent, &font_descent, &overall);
4357         if (appData.monoMode) {
4358             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4359                              x + squareSize - overall.width - 2,
4360                              y + squareSize - font_descent - 1, string, 1);
4361         } else {
4362             XDrawString(xDisplay, xBoardWindow, coordGC,
4363                         x + squareSize - overall.width - 2,
4364                         y + squareSize - font_descent - 1, string, 1);
4365         }
4366     }
4367     if (appData.showCoords && column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT)) {
4368         string[0] = ONE + row;
4369         XTextExtents(coordFontStruct, string, 1, &direction,
4370                      &font_ascent, &font_descent, &overall);
4371         if (appData.monoMode) {
4372             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4373                              x + 2, y + font_ascent + 1, string, 1);
4374         } else {
4375             XDrawString(xDisplay, xBoardWindow, coordGC,
4376                         x + 2, y + font_ascent + 1, string, 1);
4377         }
4378     }
4379     if(!partnerUp && marker[row][column]) {
4380         XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
4381                 x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
4382     }
4383 }
4384
4385
4386 /* Why is this needed on some versions of X? */
4387 void EventProc(widget, unused, event)
4388      Widget widget;
4389      caddr_t unused;
4390      XEvent *event;
4391 {
4392     if (!XtIsRealized(widget))
4393       return;
4394
4395     switch (event->type) {
4396       case Expose:
4397         if (event->xexpose.count > 0) return;  /* no clipping is done */
4398         XDrawPosition(widget, True, NULL);
4399         if(twoBoards) { // [HGM] dual: draw other board in other orientation
4400             flipView = !flipView; partnerUp = !partnerUp;
4401             XDrawPosition(widget, True, NULL);
4402             flipView = !flipView; partnerUp = !partnerUp;
4403         }
4404         break;
4405       case MotionNotify:
4406         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;\r
4407       default:
4408         return;
4409     }
4410 }
4411 /* end why */
4412
4413 void DrawPosition(fullRedraw, board)
4414      /*Boolean*/int fullRedraw;
4415      Board board;
4416 {
4417     XDrawPosition(boardWidget, fullRedraw, board);
4418 }
4419
4420 /* Returns 1 if there are "too many" differences between b1 and b2
4421    (i.e. more than 1 move was made) */
4422 static int too_many_diffs(b1, b2)
4423      Board b1, b2;
4424 {
4425     int i, j;
4426     int c = 0;
4427
4428     for (i=0; i<BOARD_HEIGHT; ++i) {
4429         for (j=0; j<BOARD_WIDTH; ++j) {
4430             if (b1[i][j] != b2[i][j]) {
4431                 if (++c > 4)    /* Castling causes 4 diffs */
4432                   return 1;
4433             }
4434         }
4435     }
4436
4437     return 0;
4438 }
4439
4440 /* Matrix describing castling maneuvers */
4441 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4442 static int castling_matrix[4][5] = {
4443     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4444     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4445     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4446     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4447 };
4448
4449 /* Checks whether castling occurred. If it did, *rrow and *rcol
4450    are set to the destination (row,col) of the rook that moved.
4451
4452    Returns 1 if castling occurred, 0 if not.
4453
4454    Note: Only handles a max of 1 castling move, so be sure
4455    to call too_many_diffs() first.
4456    */
4457 static int check_castle_draw(newb, oldb, rrow, rcol)
4458      Board newb, oldb;
4459      int *rrow, *rcol;
4460 {
4461     int i, *r, j;
4462     int match;
4463
4464     /* For each type of castling... */
4465     for (i=0; i<4; ++i) {
4466         r = castling_matrix[i];
4467
4468         /* Check the 4 squares involved in the castling move */
4469         match = 0;
4470         for (j=1; j<=4; ++j) {
4471             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4472                 match = 1;
4473                 break;
4474             }
4475         }
4476
4477         if (!match) {
4478             /* All 4 changed, so it must be a castling move */
4479             *rrow = r[0];
4480             *rcol = r[3];
4481             return 1;
4482         }
4483     }
4484     return 0;
4485 }
4486
4487 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
4488 void DrawSeekAxis( int x, int y, int xTo, int yTo )
4489 {
4490       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
4491 }
4492
4493 void DrawSeekBackground( int left, int top, int right, int bottom )
4494 {
4495     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
4496 }
4497
4498 void DrawSeekText(char *buf, int x, int y)
4499 {
4500     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
4501 }
4502
4503 void DrawSeekDot(int x, int y, int colorNr)
4504 {
4505     int square = colorNr & 0x80;
4506     GC color;
4507     colorNr &= 0x7F;
4508     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
4509     if(square)
4510         XFillRectangle(xDisplay, xBoardWindow, color,
4511                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
4512     else
4513         XFillArc(xDisplay, xBoardWindow, color,
4514                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
4515 }
4516
4517 static int damage[2][BOARD_RANKS][BOARD_FILES];
4518
4519 /*
4520  * event handler for redrawing the board
4521  */
4522 void XDrawPosition(w, repaint, board)
4523      Widget w;
4524      /*Boolean*/int repaint;
4525      Board board;
4526 {
4527     int i, j, do_flash;
4528     static int lastFlipView = 0;
4529     static int lastBoardValid[2] = {0, 0};
4530     static Board lastBoard[2];
4531     Arg args[16];
4532     int rrow, rcol;
4533     int nr = twoBoards*partnerUp;
4534
4535     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
4536
4537     if (board == NULL) {
4538         if (!lastBoardValid[nr]) return;
4539         board = lastBoard[nr];
4540     }
4541     if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
4542         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4543         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
4544                     args, 1);
4545     }
4546
4547     /*
4548      * It would be simpler to clear the window with XClearWindow()
4549      * but this causes a very distracting flicker.
4550      */
4551
4552     if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
4553
4554         /* If too much changes (begin observing new game, etc.), don't
4555            do flashing */
4556         do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
4557
4558         /* Special check for castling so we don't flash both the king
4559            and the rook (just flash the king). */
4560         if (do_flash) {
4561             if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
4562                 /* Draw rook with NO flashing. King will be drawn flashing later */
4563                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4564                 lastBoard[nr][rrow][rcol] = board[rrow][rcol];
4565             }
4566         }
4567
4568         /* First pass -- Draw (newly) empty squares and repair damage.
4569            This prevents you from having a piece show up twice while it
4570            is flashing on its new square */
4571         for (i = 0; i < BOARD_HEIGHT; i++)
4572           for (j = 0; j < BOARD_WIDTH; j++)
4573             if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
4574                 || damage[nr][i][j]) {
4575                 DrawSquare(i, j, board[i][j], 0);
4576                 damage[nr][i][j] = False;
4577             }
4578
4579         /* Second pass -- Draw piece(s) in new position and flash them */
4580         for (i = 0; i < BOARD_HEIGHT; i++)
4581           for (j = 0; j < BOARD_WIDTH; j++)
4582             if (board[i][j] != lastBoard[nr][i][j]) {
4583                 DrawSquare(i, j, board[i][j], do_flash);
4584             }
4585     } else {
4586         if (lineGap > 0)
4587           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4588                         twoBoards & partnerUp ? secondSegments : // [HGM] dual
4589                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
4590
4591         for (i = 0; i < BOARD_HEIGHT; i++)
4592           for (j = 0; j < BOARD_WIDTH; j++) {
4593               DrawSquare(i, j, board[i][j], 0);
4594               damage[nr][i][j] = False;
4595           }
4596     }
4597
4598     CopyBoard(lastBoard[nr], board);
4599     lastBoardValid[nr] = 1;
4600   if(nr == 0) { // [HGM] dual: no highlights on second board yet
4601     lastFlipView = flipView;
4602
4603     /* Draw highlights */
4604     if (pm1X >= 0 && pm1Y >= 0) {
4605       drawHighlight(pm1X, pm1Y, prelineGC);
4606     }
4607     if (pm2X >= 0 && pm2Y >= 0) {
4608       drawHighlight(pm2X, pm2Y, prelineGC);
4609     }
4610     if (hi1X >= 0 && hi1Y >= 0) {
4611       drawHighlight(hi1X, hi1Y, highlineGC);
4612     }
4613     if (hi2X >= 0 && hi2Y >= 0) {
4614       drawHighlight(hi2X, hi2Y, highlineGC);
4615     }
4616   }
4617     /* If piece being dragged around board, must redraw that too */
4618     DrawDragPiece();
4619
4620     XSync(xDisplay, False);
4621 }
4622
4623
4624 /*
4625  * event handler for redrawing the board
4626  */
4627 void DrawPositionProc(w, event, prms, nprms)
4628      Widget w;
4629      XEvent *event;
4630      String *prms;
4631      Cardinal *nprms;
4632 {
4633     XDrawPosition(w, True, NULL);
4634 }
4635
4636
4637 /*
4638  * event handler for parsing user moves
4639  */
4640 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
4641 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
4642 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
4643 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
4644 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
4645 //       and at the end FinishMove() to perform the move after optional promotion popups.
4646 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
4647 void HandleUserMove(w, event, prms, nprms)
4648      Widget w;
4649      XEvent *event;
4650      String *prms;
4651      Cardinal *nprms;
4652 {
4653     if (w != boardWidget || errorExitStatus != -1) return;
4654
4655     if (promotionUp) {
4656         if (event->type == ButtonPress) {
4657             XtPopdown(promotionShell);
4658             XtDestroyWidget(promotionShell);
4659             promotionUp = False;
4660             ClearHighlights();
4661             fromX = fromY = -1;
4662         } else {
4663             return;
4664         }
4665     }
4666
4667     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
4668     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
4669     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
4670 }
4671
4672 void AnimateUserMove (Widget w, XEvent * event,
4673                       String * params, Cardinal * nParams)
4674 {
4675     DragPieceMove(event->xmotion.x, event->xmotion.y);
4676 }
4677
4678 void HandlePV (Widget w, XEvent * event,
4679                       String * params, Cardinal * nParams)
4680 {   // [HGM] pv: walk PV
4681     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
4682 }
4683
4684 Widget CommentCreate(name, text, mutable, callback, lines)
4685      char *name, *text;
4686      int /*Boolean*/ mutable;
4687      XtCallbackProc callback;
4688      int lines;
4689 {
4690     Arg args[16];
4691     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4692     Dimension bw_width;
4693     int j;
4694
4695     j = 0;
4696     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4697     XtGetValues(boardWidget, args, j);
4698
4699     j = 0;
4700     XtSetArg(args[j], XtNresizable, True);  j++;
4701 #if TOPLEVEL
4702     shell =
4703       XtCreatePopupShell(name, topLevelShellWidgetClass,
4704                          shellWidget, args, j);
4705 #else
4706     shell =
4707       XtCreatePopupShell(name, transientShellWidgetClass,
4708                          shellWidget, args, j);
4709 #endif
4710     layout =
4711       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4712                             layoutArgs, XtNumber(layoutArgs));
4713     form =
4714       XtCreateManagedWidget("form", formWidgetClass, layout,
4715                             formArgs, XtNumber(formArgs));
4716
4717     j = 0;
4718     if (mutable) {
4719         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4720         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4721     }
4722     XtSetArg(args[j], XtNstring, text);  j++;
4723     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4724     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4725     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4726     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4727     XtSetArg(args[j], XtNresizable, True);  j++;
4728     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
4729     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4730     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4731     XtSetArg(args[j], XtNautoFill, True);  j++;
4732     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4733     edit =
4734       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4735     XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
4736
4737     if (mutable) {
4738         j = 0;
4739         XtSetArg(args[j], XtNfromVert, edit);  j++;
4740         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4741         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4742         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4743         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4744         b_ok =
4745           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
4746         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4747
4748         j = 0;
4749         XtSetArg(args[j], XtNfromVert, edit);  j++;
4750         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4751         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4752         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4753         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4754         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4755         b_cancel =
4756           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
4757         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4758
4759         j = 0;
4760         XtSetArg(args[j], XtNfromVert, edit);  j++;
4761         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4762         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4763         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4764         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4765         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4766         b_clear =
4767           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
4768         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4769     } else {
4770         j = 0;
4771         XtSetArg(args[j], XtNfromVert, edit);  j++;
4772         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4773         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4774         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4775         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4776         b_close =
4777           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
4778         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4779
4780         j = 0;
4781         XtSetArg(args[j], XtNfromVert, edit);  j++;
4782         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4783         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4784         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4785         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4786         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4787         b_edit =
4788           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
4789         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4790     }
4791
4792     XtRealizeWidget(shell);
4793
4794     if (commentX == -1) {
4795         int xx, yy;
4796         Window junk;
4797         Dimension pw_height;
4798         Dimension ew_height;
4799
4800         j = 0;
4801         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4802         XtGetValues(edit, args, j);
4803
4804         j = 0;
4805         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4806         XtGetValues(shell, args, j);
4807         commentH = pw_height + (lines - 1) * ew_height;
4808         commentW = bw_width - 16;
4809
4810         XSync(xDisplay, False);
4811 #ifdef NOTDEF
4812         /* This code seems to tickle an X bug if it is executed too soon
4813            after xboard starts up.  The coordinates get transformed as if
4814            the main window was positioned at (0, 0).
4815            */
4816         XtTranslateCoords(shellWidget,
4817                           (bw_width - commentW) / 2, 0 - commentH / 2,
4818                           &commentX, &commentY);
4819 #else  /*!NOTDEF*/
4820         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4821                               RootWindowOfScreen(XtScreen(shellWidget)),
4822                               (bw_width - commentW) / 2, 0 - commentH / 2,
4823                               &xx, &yy, &junk);
4824         commentX = xx;
4825         commentY = yy;
4826 #endif /*!NOTDEF*/
4827         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4828     }
4829
4830     if(wpComment.width > 0) {
4831       commentX = wpComment.x;
4832       commentY = wpComment.y;
4833       commentW = wpComment.width;
4834       commentH = wpComment.height;
4835     }
4836
4837     j = 0;
4838     XtSetArg(args[j], XtNheight, commentH);  j++;
4839     XtSetArg(args[j], XtNwidth, commentW);  j++;
4840     XtSetArg(args[j], XtNx, commentX);  j++;
4841     XtSetArg(args[j], XtNy, commentY);  j++;
4842     XtSetValues(shell, args, j);
4843     XtSetKeyboardFocus(shell, edit);
4844
4845     return shell;
4846 }
4847
4848 /* Used for analysis window and ICS input window */
4849 Widget MiscCreate(name, text, mutable, callback, lines)
4850      char *name, *text;
4851      int /*Boolean*/ mutable;
4852      XtCallbackProc callback;
4853      int lines;
4854 {
4855     Arg args[16];
4856     Widget shell, layout, form, edit;
4857     Position x, y;
4858     Dimension bw_width, pw_height, ew_height, w, h;
4859     int j;
4860     int xx, yy;
4861     Window junk;
4862
4863     j = 0;
4864     XtSetArg(args[j], XtNresizable, True);  j++;
4865 #if TOPLEVEL
4866     shell =
4867       XtCreatePopupShell(name, topLevelShellWidgetClass,
4868                          shellWidget, args, j);
4869 #else
4870     shell =
4871       XtCreatePopupShell(name, transientShellWidgetClass,
4872                          shellWidget, args, j);
4873 #endif
4874     layout =
4875       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4876                             layoutArgs, XtNumber(layoutArgs));
4877     form =
4878       XtCreateManagedWidget("form", formWidgetClass, layout,
4879                             formArgs, XtNumber(formArgs));
4880
4881     j = 0;
4882     if (mutable) {
4883         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4884         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4885     }
4886     XtSetArg(args[j], XtNstring, text);  j++;
4887     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4888     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4889     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4890     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4891     XtSetArg(args[j], XtNresizable, True);  j++;
4892     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4893     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4894     XtSetArg(args[j], XtNautoFill, True);  j++;
4895     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4896     edit =
4897       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4898
4899     XtRealizeWidget(shell);
4900
4901     j = 0;
4902     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4903     XtGetValues(boardWidget, args, j);
4904
4905     j = 0;
4906     XtSetArg(args[j], XtNheight, &ew_height);  j++;
4907     XtGetValues(edit, args, j);
4908
4909     j = 0;
4910     XtSetArg(args[j], XtNheight, &pw_height);  j++;
4911     XtGetValues(shell, args, j);
4912     h = pw_height + (lines - 1) * ew_height;
4913     w = bw_width - 16;
4914
4915     XSync(xDisplay, False);
4916 #ifdef NOTDEF
4917     /* This code seems to tickle an X bug if it is executed too soon
4918        after xboard starts up.  The coordinates get transformed as if
4919        the main window was positioned at (0, 0).
4920     */
4921     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4922 #else  /*!NOTDEF*/
4923     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4924                           RootWindowOfScreen(XtScreen(shellWidget)),
4925                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4926 #endif /*!NOTDEF*/
4927     x = xx;
4928     y = yy;
4929     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4930
4931     j = 0;
4932     XtSetArg(args[j], XtNheight, h);  j++;
4933     XtSetArg(args[j], XtNwidth, w);  j++;
4934     XtSetArg(args[j], XtNx, x);  j++;
4935     XtSetArg(args[j], XtNy, y);  j++;
4936     XtSetValues(shell, args, j);
4937
4938     return shell;
4939 }
4940
4941
4942 static int savedIndex;  /* gross that this is global */
4943
4944 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
4945 {
4946         String val;
4947         XawTextPosition index, dummy;
4948         Arg arg;
4949
4950         XawTextGetSelectionPos(w, &index, &dummy);
4951         XtSetArg(arg, XtNstring, &val);
4952         XtGetValues(w, &arg, 1);
4953         ReplaceComment(savedIndex, val);
4954         if(savedIndex != currentMove) ToNrEvent(savedIndex);
4955         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
4956 }
4957
4958 void EditCommentPopUp(index, title, text)
4959      int index;
4960      char *title, *text;
4961 {
4962     Widget edit;
4963     Arg args[16];
4964     int j;
4965
4966     savedIndex = index;
4967     if (text == NULL) text = "";
4968
4969     if (editShell == NULL) {
4970         editShell =
4971           CommentCreate(title, text, True, EditCommentCallback, 4);
4972         XtRealizeWidget(editShell);
4973         CatchDeleteWindow(editShell, "EditCommentPopDown");
4974     } else {
4975         edit = XtNameToWidget(editShell, "*form.text");
4976         j = 0;
4977         XtSetArg(args[j], XtNstring, text); j++;
4978         XtSetValues(edit, args, j);
4979         j = 0;
4980         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4981         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4982         XtSetValues(editShell, args, j);
4983     }
4984
4985     XtPopup(editShell, XtGrabNone);
4986
4987     editUp = True;
4988     j = 0;
4989     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4990     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4991                 args, j);
4992 }
4993
4994 void EditCommentCallback(w, client_data, call_data)
4995      Widget w;
4996      XtPointer client_data, call_data;
4997 {
4998     String name, val;
4999     Arg args[16];
5000     int j;
5001     Widget edit;
5002
5003     j = 0;
5004     XtSetArg(args[j], XtNlabel, &name);  j++;
5005     XtGetValues(w, args, j);
5006
5007     if (strcmp(name, _("ok")) == 0) {
5008         edit = XtNameToWidget(editShell, "*form.text");
5009         j = 0;
5010         XtSetArg(args[j], XtNstring, &val); j++;
5011         XtGetValues(edit, args, j);
5012         ReplaceComment(savedIndex, val);
5013         EditCommentPopDown();
5014     } else if (strcmp(name, _("cancel")) == 0) {
5015         EditCommentPopDown();
5016     } else if (strcmp(name, _("clear")) == 0) {
5017         edit = XtNameToWidget(editShell, "*form.text");
5018         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5019         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5020     }
5021 }
5022
5023 void EditCommentPopDown()
5024 {
5025     Arg args[16];
5026     int j;
5027
5028     if (!editUp) return;
5029     j = 0;
5030     XtSetArg(args[j], XtNx, &commentX); j++;
5031     XtSetArg(args[j], XtNy, &commentY); j++;
5032     XtSetArg(args[j], XtNheight, &commentH); j++;
5033     XtSetArg(args[j], XtNwidth, &commentW); j++;
5034     XtGetValues(editShell, args, j);
5035     XtPopdown(editShell);
5036     editUp = False;
5037     j = 0;
5038     XtSetArg(args[j], XtNleftBitmap, None); j++;
5039     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
5040                 args, j);
5041 }
5042
5043 void ICSInputBoxPopUp()
5044 {
5045     Widget edit;
5046     Arg args[16];
5047     int j;
5048     char *title = _("ICS Input");
5049     XtTranslations tr;
5050
5051     if (ICSInputShell == NULL) {
5052         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
5053         tr = XtParseTranslationTable(ICSInputTranslations);
5054         edit = XtNameToWidget(ICSInputShell, "*form.text");
5055         XtOverrideTranslations(edit, tr);
5056         XtRealizeWidget(ICSInputShell);
5057         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
5058
5059     } else {
5060         edit = XtNameToWidget(ICSInputShell, "*form.text");
5061         j = 0;
5062         XtSetArg(args[j], XtNstring, ""); j++;
5063         XtSetValues(edit, args, j);
5064         j = 0;
5065         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5066         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5067         XtSetValues(ICSInputShell, args, j);
5068     }
5069
5070     XtPopup(ICSInputShell, XtGrabNone);
5071     XtSetKeyboardFocus(ICSInputShell, edit);
5072
5073     ICSInputBoxUp = True;
5074     j = 0;
5075     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
5076     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5077                 args, j);
5078 }
5079
5080 void ICSInputSendText()
5081 {
5082     Widget edit;
5083     int j;
5084     Arg args[16];
5085     String val;
5086
5087     edit = XtNameToWidget(ICSInputShell, "*form.text");
5088     j = 0;
5089     XtSetArg(args[j], XtNstring, &val); j++;
5090     XtGetValues(edit, args, j);
5091     SaveInHistory(val);
5092     SendMultiLineToICS(val);
5093     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
5094     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
5095 }
5096
5097 void ICSInputBoxPopDown()
5098 {
5099     Arg args[16];
5100     int j;
5101
5102     if (!ICSInputBoxUp) return;
5103     j = 0;
5104     XtPopdown(ICSInputShell);
5105     ICSInputBoxUp = False;
5106     j = 0;
5107     XtSetArg(args[j], XtNleftBitmap, None); j++;
5108     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
5109                 args, j);
5110 }
5111
5112 void CommentPopUp(title, text)
5113      char *title, *text;
5114 {
5115     Arg args[16];
5116     int j;
5117     Widget edit;
5118
5119     savedIndex = currentMove; // [HGM] vari
5120     if (commentShell == NULL) {
5121         commentShell =
5122           CommentCreate(title, text, False, CommentCallback, 4);
5123         XtRealizeWidget(commentShell);
5124         CatchDeleteWindow(commentShell, "CommentPopDown");
5125     } else {
5126         edit = XtNameToWidget(commentShell, "*form.text");
5127         j = 0;
5128         XtSetArg(args[j], XtNstring, text); j++;
5129         XtSetValues(edit, args, j);
5130         j = 0;
5131         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
5132         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
5133         XtSetValues(commentShell, args, j);
5134     }
5135
5136     XtPopup(commentShell, XtGrabNone);
5137     XSync(xDisplay, False);
5138
5139     commentUp = True;
5140 }
5141
5142 void CommentCallback(w, client_data, call_data)
5143      Widget w;
5144      XtPointer client_data, call_data;
5145 {
5146     String name;
5147     Arg args[16];
5148     int j;
5149
5150     j = 0;
5151     XtSetArg(args[j], XtNlabel, &name);  j++;
5152     XtGetValues(w, args, j);
5153
5154     if (strcmp(name, _("close")) == 0) {
5155         CommentPopDown();
5156     } else if (strcmp(name, _("edit")) == 0) {
5157         CommentPopDown();
5158         EditCommentEvent();
5159     }
5160 }
5161
5162
5163 void CommentPopDown()
5164 {
5165     Arg args[16];
5166     int j;
5167
5168     if (!commentUp) return;
5169     j = 0;
5170     XtSetArg(args[j], XtNx, &commentX); j++;
5171     XtSetArg(args[j], XtNy, &commentY); j++;
5172     XtSetArg(args[j], XtNwidth, &commentW); j++;
5173     XtSetArg(args[j], XtNheight, &commentH); j++;
5174     XtGetValues(commentShell, args, j);
5175     XtPopdown(commentShell);
5176     XSync(xDisplay, False);
5177     commentUp = False;
5178 }
5179
5180 void FileNamePopUp(label, def, proc, openMode)
5181      char *label;
5182      char *def;
5183      FileProc proc;
5184      char *openMode;
5185 {
5186     fileProc = proc;            /* I can't see a way not */
5187     fileOpenMode = openMode;    /*   to use globals here */
5188     {   // [HGM] use file-selector dialog stolen from Ghostview
5189         char *name;
5190         int index; // this is not supported yet
5191         FILE *f;
5192         if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
5193                            def, openMode, NULL, &name))
5194           (void) (*fileProc)(f, index=0, name);
5195     }
5196 }
5197
5198 void FileNamePopDown()
5199 {
5200     if (!filenameUp) return;
5201     XtPopdown(fileNameShell);
5202     XtDestroyWidget(fileNameShell);
5203     filenameUp = False;
5204     ModeHighlight();
5205 }
5206
5207 void FileNameCallback(w, client_data, call_data)
5208      Widget w;
5209      XtPointer client_data, call_data;
5210 {
5211     String name;
5212     Arg args[16];
5213
5214     XtSetArg(args[0], XtNlabel, &name);
5215     XtGetValues(w, args, 1);
5216
5217     if (strcmp(name, _("cancel")) == 0) {
5218         FileNamePopDown();
5219         return;
5220     }
5221
5222     FileNameAction(w, NULL, NULL, NULL);
5223 }
5224
5225 void FileNameAction(w, event, prms, nprms)
5226      Widget w;
5227      XEvent *event;
5228      String *prms;
5229      Cardinal *nprms;
5230 {
5231     char buf[MSG_SIZ];
5232     String name;
5233     FILE *f;
5234     char *p, *fullname;
5235     int index;
5236
5237     name = XawDialogGetValueString(w = XtParent(w));
5238
5239     if ((name != NULL) && (*name != NULLCHAR)) {
5240         safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
5241         XtPopdown(w = XtParent(XtParent(w)));
5242         XtDestroyWidget(w);
5243         filenameUp = False;
5244
5245         p = strrchr(buf, ' ');
5246         if (p == NULL) {
5247             index = 0;
5248         } else {
5249             *p++ = NULLCHAR;
5250             index = atoi(p);
5251         }
5252         fullname = ExpandPathName(buf);
5253         if (!fullname) {
5254             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
5255         }
5256         else {
5257             f = fopen(fullname, fileOpenMode);
5258             if (f == NULL) {
5259                 DisplayError(_("Failed to open file"), errno);
5260             } else {
5261                 (void) (*fileProc)(f, index, buf);
5262             }
5263         }
5264         ModeHighlight();
5265         return;
5266     }
5267
5268     XtPopdown(w = XtParent(XtParent(w)));
5269     XtDestroyWidget(w);
5270     filenameUp = False;
5271     ModeHighlight();
5272 }
5273
5274 void PromotionPopUp()
5275 {
5276     Arg args[16];
5277     Widget dialog, layout;
5278     Position x, y;
5279     Dimension bw_width, pw_width;
5280     int j;
5281
5282     j = 0;
5283     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5284     XtGetValues(boardWidget, args, j);
5285
5286     j = 0;
5287     XtSetArg(args[j], XtNresizable, True); j++;
5288     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
5289     promotionShell =
5290       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5291                          shellWidget, args, j);
5292     layout =
5293       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5294                             layoutArgs, XtNumber(layoutArgs));
5295
5296     j = 0;
5297     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
5298     XtSetArg(args[j], XtNborderWidth, 0); j++;
5299     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5300                                    layout, args, j);
5301
5302   if(gameInfo.variant != VariantShogi) {
5303     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
5304                        (XtPointer) dialog);
5305     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
5306                        (XtPointer) dialog);
5307     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
5308                        (XtPointer) dialog);
5309     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
5310                        (XtPointer) dialog);
5311     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5312         gameInfo.variant == VariantGiveaway) {
5313       XawDialogAddButton(dialog, _("King"), PromotionCallback,
5314                          (XtPointer) dialog);
5315     }
5316     if(gameInfo.variant == VariantCapablanca ||
5317        gameInfo.variant == VariantGothic ||
5318        gameInfo.variant == VariantCapaRandom) {
5319       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
5320                          (XtPointer) dialog);
5321       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
5322                          (XtPointer) dialog);
5323     }
5324   } else // [HGM] shogi
5325   {
5326       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
5327                          (XtPointer) dialog);
5328       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
5329                          (XtPointer) dialog);
5330   }
5331     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
5332                        (XtPointer) dialog);
5333
5334     XtRealizeWidget(promotionShell);
5335     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5336
5337     j = 0;
5338     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5339     XtGetValues(promotionShell, args, j);
5340
5341     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5342                       lineGap + squareSize/3 +
5343                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
5344                        0 : 6*(squareSize + lineGap)), &x, &y);
5345
5346     j = 0;
5347     XtSetArg(args[j], XtNx, x); j++;
5348     XtSetArg(args[j], XtNy, y); j++;
5349     XtSetValues(promotionShell, args, j);
5350
5351     XtPopup(promotionShell, XtGrabNone);
5352
5353     promotionUp = True;
5354 }
5355
5356 void PromotionPopDown()
5357 {
5358     if (!promotionUp) return;
5359     XtPopdown(promotionShell);
5360     XtDestroyWidget(promotionShell);
5361     promotionUp = False;
5362 }
5363
5364 void PromotionCallback(w, client_data, call_data)
5365      Widget w;
5366      XtPointer client_data, call_data;
5367 {
5368     String name;
5369     Arg args[16];
5370     int promoChar;
5371
5372     XtSetArg(args[0], XtNlabel, &name);
5373     XtGetValues(w, args, 1);
5374
5375     PromotionPopDown();
5376
5377     if (fromX == -1) return;
5378
5379     if (strcmp(name, _("cancel")) == 0) {
5380         fromX = fromY = -1;
5381         ClearHighlights();
5382         return;
5383     } else if (strcmp(name, _("Knight")) == 0) {
5384         promoChar = 'n';
5385     } else if (strcmp(name, _("Promote")) == 0) {
5386         promoChar = '+';
5387     } else if (strcmp(name, _("Defer")) == 0) {
5388         promoChar = '=';
5389     } else {
5390         promoChar = ToLower(name[0]);
5391     }
5392
5393     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5394
5395     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5396     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5397     fromX = fromY = -1;
5398 }
5399
5400
5401 void ErrorCallback(w, client_data, call_data)
5402      Widget w;
5403      XtPointer client_data, call_data;
5404 {
5405     errorUp = False;
5406     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5407     XtDestroyWidget(w);
5408     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5409 }
5410
5411
5412 void ErrorPopDown()
5413 {
5414     if (!errorUp) return;
5415     errorUp = False;
5416     XtPopdown(errorShell);
5417     XtDestroyWidget(errorShell);
5418     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5419 }
5420
5421 void ErrorPopUp(title, label, modal)
5422      char *title, *label;
5423      int modal;
5424 {
5425     Arg args[16];
5426     Widget dialog, layout;
5427     Position x, y;
5428     int xx, yy;
5429     Window junk;
5430     Dimension bw_width, pw_width;
5431     Dimension pw_height;
5432     int i;
5433
5434     i = 0;
5435     XtSetArg(args[i], XtNresizable, True);  i++;
5436     XtSetArg(args[i], XtNtitle, title); i++;
5437     errorShell =
5438       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5439                          shellWidget, args, i);
5440     layout =
5441       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5442                             layoutArgs, XtNumber(layoutArgs));
5443
5444     i = 0;
5445     XtSetArg(args[i], XtNlabel, label); i++;
5446     XtSetArg(args[i], XtNborderWidth, 0); i++;
5447     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5448                                    layout, args, i);
5449
5450     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
5451
5452     XtRealizeWidget(errorShell);
5453     CatchDeleteWindow(errorShell, "ErrorPopDown");
5454
5455     i = 0;
5456     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5457     XtGetValues(boardWidget, args, i);
5458     i = 0;
5459     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5460     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5461     XtGetValues(errorShell, args, i);
5462
5463 #ifdef NOTDEF
5464     /* This code seems to tickle an X bug if it is executed too soon
5465        after xboard starts up.  The coordinates get transformed as if
5466        the main window was positioned at (0, 0).
5467        */
5468     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5469                       0 - pw_height + squareSize / 3, &x, &y);
5470 #else
5471     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5472                           RootWindowOfScreen(XtScreen(boardWidget)),
5473                           (bw_width - pw_width) / 2,
5474                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5475     x = xx;
5476     y = yy;
5477 #endif
5478     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5479
5480     i = 0;
5481     XtSetArg(args[i], XtNx, x);  i++;
5482     XtSetArg(args[i], XtNy, y);  i++;
5483     XtSetValues(errorShell, args, i);
5484
5485     errorUp = True;
5486     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5487 }
5488
5489 /* Disable all user input other than deleting the window */
5490 static int frozen = 0;
5491 void FreezeUI()
5492 {
5493   if (frozen) return;
5494   /* Grab by a widget that doesn't accept input */
5495   XtAddGrab(messageWidget, TRUE, FALSE);
5496   frozen = 1;
5497 }
5498
5499 /* Undo a FreezeUI */
5500 void ThawUI()
5501 {
5502   if (!frozen) return;
5503   XtRemoveGrab(messageWidget);
5504   frozen = 0;
5505 }
5506
5507 char *ModeToWidgetName(mode)
5508      GameMode mode;
5509 {
5510     switch (mode) {
5511       case BeginningOfGame:
5512         if (appData.icsActive)
5513           return "menuMode.ICS Client";
5514         else if (appData.noChessProgram ||
5515                  *appData.cmailGameName != NULLCHAR)
5516           return "menuMode.Edit Game";
5517         else
5518           return "menuMode.Machine Black";
5519       case MachinePlaysBlack:
5520         return "menuMode.Machine Black";
5521       case MachinePlaysWhite:
5522         return "menuMode.Machine White";
5523       case AnalyzeMode:
5524         return "menuMode.Analysis Mode";
5525       case AnalyzeFile:
5526         return "menuMode.Analyze File";
5527       case TwoMachinesPlay:
5528         return "menuMode.Two Machines";
5529       case EditGame:
5530         return "menuMode.Edit Game";
5531       case PlayFromGameFile:
5532         return "menuFile.Load Game";
5533       case EditPosition:
5534         return "menuMode.Edit Position";
5535       case Training:
5536         return "menuMode.Training";
5537       case IcsPlayingWhite:
5538       case IcsPlayingBlack:
5539       case IcsObserving:
5540       case IcsIdle:
5541       case IcsExamining:
5542         return "menuMode.ICS Client";
5543       default:
5544       case EndOfGame:
5545         return NULL;
5546     }
5547 }
5548
5549 void ModeHighlight()
5550 {
5551     Arg args[16];
5552     static int oldPausing = FALSE;
5553     static GameMode oldmode = (GameMode) -1;
5554     char *wname;
5555
5556     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5557
5558     if (pausing != oldPausing) {
5559         oldPausing = pausing;
5560         if (pausing) {
5561             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5562         } else {
5563             XtSetArg(args[0], XtNleftBitmap, None);
5564         }
5565         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5566                     args, 1);
5567
5568         if (appData.showButtonBar) {
5569           /* Always toggle, don't set.  Previous code messes up when
5570              invoked while the button is pressed, as releasing it
5571              toggles the state again. */
5572           {
5573             Pixel oldbg, oldfg;
5574             XtSetArg(args[0], XtNbackground, &oldbg);
5575             XtSetArg(args[1], XtNforeground, &oldfg);
5576             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5577                         args, 2);
5578             XtSetArg(args[0], XtNbackground, oldfg);
5579             XtSetArg(args[1], XtNforeground, oldbg);
5580           }
5581           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5582         }
5583     }
5584
5585     wname = ModeToWidgetName(oldmode);
5586     if (wname != NULL) {
5587         XtSetArg(args[0], XtNleftBitmap, None);
5588         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5589     }
5590     wname = ModeToWidgetName(gameMode);
5591     if (wname != NULL) {
5592         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5593         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5594     }
5595     oldmode = gameMode;
5596
5597     /* Maybe all the enables should be handled here, not just this one */
5598     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5599                    gameMode == Training || gameMode == PlayFromGameFile);
5600 }
5601
5602
5603 /*
5604  * Button/menu procedures
5605  */
5606 void ResetProc(w, event, prms, nprms)
5607      Widget w;
5608      XEvent *event;
5609      String *prms;
5610      Cardinal *nprms;
5611 {
5612     ResetGameEvent();
5613 }
5614
5615 int LoadGamePopUp(f, gameNumber, title)
5616      FILE *f;
5617      int gameNumber;
5618      char *title;
5619 {
5620     cmailMsgLoaded = FALSE;
5621     if (gameNumber == 0) {
5622         int error = GameListBuild(f);
5623         if (error) {
5624             DisplayError(_("Cannot build game list"), error);
5625         } else if (!ListEmpty(&gameList) &&
5626                    ((ListGame *) gameList.tailPred)->number > 1) {
5627             GameListPopUp(f, title);
5628             return TRUE;
5629         }
5630         GameListDestroy();
5631         gameNumber = 1;
5632     }
5633     return LoadGame(f, gameNumber, title, FALSE);
5634 }
5635
5636 void LoadGameProc(w, event, prms, nprms)
5637      Widget w;
5638      XEvent *event;
5639      String *prms;
5640      Cardinal *nprms;
5641 {
5642     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5643         Reset(FALSE, TRUE);
5644     }
5645     FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
5646 }
5647
5648 void LoadNextGameProc(w, event, prms, nprms)
5649      Widget w;
5650      XEvent *event;
5651      String *prms;
5652      Cardinal *nprms;
5653 {
5654     ReloadGame(1);
5655 }
5656
5657 void LoadPrevGameProc(w, event, prms, nprms)
5658      Widget w;
5659      XEvent *event;
5660      String *prms;
5661      Cardinal *nprms;
5662 {
5663     ReloadGame(-1);
5664 }
5665
5666 void ReloadGameProc(w, event, prms, nprms)
5667      Widget w;
5668      XEvent *event;
5669      String *prms;
5670      Cardinal *nprms;
5671 {
5672     ReloadGame(0);
5673 }
5674
5675 void LoadNextPositionProc(w, event, prms, nprms)
5676      Widget w;
5677      XEvent *event;
5678      String *prms;
5679      Cardinal *nprms;
5680 {
5681     ReloadPosition(1);
5682 }
5683
5684 void LoadPrevPositionProc(w, event, prms, nprms)
5685      Widget w;
5686      XEvent *event;
5687      String *prms;
5688      Cardinal *nprms;
5689 {
5690     ReloadPosition(-1);
5691 }
5692
5693 void ReloadPositionProc(w, event, prms, nprms)
5694      Widget w;
5695      XEvent *event;
5696      String *prms;
5697      Cardinal *nprms;
5698 {
5699     ReloadPosition(0);
5700 }
5701
5702 void LoadPositionProc(w, event, prms, nprms)
5703      Widget w;
5704      XEvent *event;
5705      String *prms;
5706      Cardinal *nprms;
5707 {
5708     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5709         Reset(FALSE, TRUE);
5710     }
5711     FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
5712 }
5713
5714 void SaveGameProc(w, event, prms, nprms)
5715      Widget w;
5716      XEvent *event;
5717      String *prms;
5718      Cardinal *nprms;
5719 {
5720     FileNamePopUp(_("Save game file name?"),
5721                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5722                   SaveGame, "a");
5723 }
5724
5725 void SavePositionProc(w, event, prms, nprms)
5726      Widget w;
5727      XEvent *event;
5728      String *prms;
5729      Cardinal *nprms;
5730 {
5731     FileNamePopUp(_("Save position file name?"),
5732                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5733                   SavePosition, "a");
5734 }
5735
5736 void ReloadCmailMsgProc(w, event, prms, nprms)
5737      Widget w;
5738      XEvent *event;
5739      String *prms;
5740      Cardinal *nprms;
5741 {
5742     ReloadCmailMsgEvent(FALSE);
5743 }
5744
5745 void MailMoveProc(w, event, prms, nprms)
5746      Widget w;
5747      XEvent *event;
5748      String *prms;
5749      Cardinal *nprms;
5750 {
5751     MailMoveEvent();
5752 }
5753
5754 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5755 char *selected_fen_position=NULL;
5756
5757 Boolean
5758 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5759                  Atom *type_return, XtPointer *value_return,
5760                  unsigned long *length_return, int *format_return)
5761 {
5762   char *selection_tmp;
5763
5764   if (!selected_fen_position) return False; /* should never happen */
5765   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5766     /* note: since no XtSelectionDoneProc was registered, Xt will
5767      * automatically call XtFree on the value returned.  So have to
5768      * make a copy of it allocated with XtMalloc */
5769     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5770     safeStrCpy(selection_tmp, selected_fen_position, sizeof(selection_tmp)/sizeof(selection_tmp[0]) );
5771
5772     *value_return=selection_tmp;
5773     *length_return=strlen(selection_tmp);
5774     *type_return=*target;
5775     *format_return = 8; /* bits per byte */
5776     return True;
5777   } else if (*target == XA_TARGETS(xDisplay)) {
5778     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5779     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5780     targets_tmp[1] = XA_STRING;
5781     *value_return = targets_tmp;
5782     *type_return = XA_ATOM;
5783     *length_return = 2;
5784     *format_return = 8 * sizeof(Atom);
5785     if (*format_return > 32) {
5786       *length_return *= *format_return / 32;
5787       *format_return = 32;
5788     }
5789     return True;
5790   } else {
5791     return False;
5792   }
5793 }
5794
5795 /* note: when called from menu all parameters are NULL, so no clue what the
5796  * Widget which was clicked on was, or what the click event was
5797  */
5798 void CopyPositionProc(w, event, prms, nprms)
5799   Widget w;
5800   XEvent *event;
5801   String *prms;
5802   Cardinal *nprms;
5803   {
5804     /*
5805      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5806      * have a notion of a position that is selected but not copied.
5807      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5808      */
5809     if(gameMode == EditPosition) EditPositionDone(TRUE);
5810     if (selected_fen_position) free(selected_fen_position);
5811     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
5812     if (!selected_fen_position) return;
5813     XtOwnSelection(menuBarWidget, XA_PRIMARY,
5814                    CurrentTime,
5815                    SendPositionSelection,
5816                    NULL/* lose_ownership_proc */ ,
5817                    NULL/* transfer_done_proc */);
5818     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5819                    CurrentTime,
5820                    SendPositionSelection,
5821                    NULL/* lose_ownership_proc */ ,
5822                    NULL/* transfer_done_proc */);
5823   }
5824
5825 /* function called when the data to Paste is ready */
5826 static void
5827 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5828            Atom *type, XtPointer value, unsigned long *len, int *format)
5829 {
5830   char *fenstr=value;
5831   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5832   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5833   EditPositionPasteFEN(fenstr);
5834   XtFree(value);
5835 }
5836
5837 /* called when Paste Position button is pressed,
5838  * all parameters will be NULL */
5839 void PastePositionProc(w, event, prms, nprms)
5840   Widget w;
5841   XEvent *event;
5842   String *prms;
5843   Cardinal *nprms;
5844 {
5845     XtGetSelectionValue(menuBarWidget,
5846       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5847       /* (XtSelectionCallbackProc) */ PastePositionCB,
5848       NULL, /* client_data passed to PastePositionCB */
5849
5850       /* better to use the time field from the event that triggered the
5851        * call to this function, but that isn't trivial to get
5852        */
5853       CurrentTime
5854     );
5855     return;
5856 }
5857
5858 static Boolean
5859 SendGameSelection(Widget w, Atom *selection, Atom *target,
5860                   Atom *type_return, XtPointer *value_return,
5861                   unsigned long *length_return, int *format_return)
5862 {
5863   char *selection_tmp;
5864
5865   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
5866     FILE* f = fopen(gameCopyFilename, "r");
5867     long len;
5868     size_t count;
5869     if (f == NULL) return False;
5870     fseek(f, 0, 2);
5871     len = ftell(f);
5872     rewind(f);
5873     selection_tmp = XtMalloc(len + 1);
5874     count = fread(selection_tmp, 1, len, f);
5875     if (len != count) {
5876       XtFree(selection_tmp);
5877       return False;
5878     }
5879     selection_tmp[len] = NULLCHAR;
5880     *value_return = selection_tmp;
5881     *length_return = len;
5882     *type_return = *target;
5883     *format_return = 8; /* bits per byte */
5884     return True;
5885   } else if (*target == XA_TARGETS(xDisplay)) {
5886     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
5887     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
5888     targets_tmp[1] = XA_STRING;
5889     *value_return = targets_tmp;
5890     *type_return = XA_ATOM;
5891     *length_return = 2;
5892     *format_return = 8 * sizeof(Atom);
5893     if (*format_return > 32) {
5894       *length_return *= *format_return / 32;
5895       *format_return = 32;
5896     }
5897     return True;
5898   } else {
5899     return False;
5900   }
5901 }
5902
5903 /* note: when called from menu all parameters are NULL, so no clue what the
5904  * Widget which was clicked on was, or what the click event was
5905  */
5906 void CopyGameProc(w, event, prms, nprms)
5907   Widget w;
5908   XEvent *event;
5909   String *prms;
5910   Cardinal *nprms;
5911 {
5912   int ret;
5913
5914   ret = SaveGameToFile(gameCopyFilename, FALSE);
5915   if (!ret) return;
5916
5917   /*
5918    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
5919    * have a notion of a game that is selected but not copied.
5920    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
5921    */
5922   XtOwnSelection(menuBarWidget, XA_PRIMARY,
5923                  CurrentTime,
5924                  SendGameSelection,
5925                  NULL/* lose_ownership_proc */ ,
5926                  NULL/* transfer_done_proc */);
5927   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
5928                  CurrentTime,
5929                  SendGameSelection,
5930                  NULL/* lose_ownership_proc */ ,
5931                  NULL/* transfer_done_proc */);
5932 }
5933
5934 /* function called when the data to Paste is ready */
5935 static void
5936 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5937             Atom *type, XtPointer value, unsigned long *len, int *format)
5938 {
5939   FILE* f;
5940   if (value == NULL || *len == 0) {
5941     return; /* nothing had been selected to copy */
5942   }
5943   f = fopen(gamePasteFilename, "w");
5944   if (f == NULL) {
5945     DisplayError(_("Can't open temp file"), errno);
5946     return;
5947   }
5948   fwrite(value, 1, *len, f);
5949   fclose(f);
5950   XtFree(value);
5951   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5952 }
5953
5954 /* called when Paste Game button is pressed,
5955  * all parameters will be NULL */
5956 void PasteGameProc(w, event, prms, nprms)
5957   Widget w;
5958   XEvent *event;
5959   String *prms;
5960   Cardinal *nprms;
5961 {
5962     XtGetSelectionValue(menuBarWidget,
5963       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
5964       /* (XtSelectionCallbackProc) */ PasteGameCB,
5965       NULL, /* client_data passed to PasteGameCB */
5966
5967       /* better to use the time field from the event that triggered the
5968        * call to this function, but that isn't trivial to get
5969        */
5970       CurrentTime
5971     );
5972     return;
5973 }
5974
5975
5976 void AutoSaveGame()
5977 {
5978     SaveGameProc(NULL, NULL, NULL, NULL);
5979 }
5980
5981
5982 void QuitProc(w, event, prms, nprms)
5983      Widget w;
5984      XEvent *event;
5985      String *prms;
5986      Cardinal *nprms;
5987 {
5988     ExitEvent(0);
5989 }
5990
5991 void PauseProc(w, event, prms, nprms)
5992      Widget w;
5993      XEvent *event;
5994      String *prms;
5995      Cardinal *nprms;
5996 {
5997     PauseEvent();
5998 }
5999
6000
6001 void MachineBlackProc(w, event, prms, nprms)
6002      Widget w;
6003      XEvent *event;
6004      String *prms;
6005      Cardinal *nprms;
6006 {
6007     MachineBlackEvent();
6008 }
6009
6010 void MachineWhiteProc(w, event, prms, nprms)
6011      Widget w;
6012      XEvent *event;
6013      String *prms;
6014      Cardinal *nprms;
6015 {
6016     MachineWhiteEvent();
6017 }
6018
6019 void AnalyzeModeProc(w, event, prms, nprms)
6020      Widget w;
6021      XEvent *event;
6022      String *prms;
6023      Cardinal *nprms;
6024 {
6025     char buf[MSG_SIZ];
6026
6027     if (!first.analysisSupport) {
6028       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6029       DisplayError(buf, 0);
6030       return;
6031     }
6032     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
6033     if (appData.icsActive) {
6034         if (gameMode != IcsObserving) {
6035           snprintf(buf, MSG_SIZ, _("You are not observing a game"));
6036             DisplayError(buf, 0);
6037             /* secure check */
6038             if (appData.icsEngineAnalyze) {
6039                 if (appData.debugMode)
6040                     fprintf(debugFP, _("Found unexpected active ICS engine analyze \n"));
6041                 ExitAnalyzeMode();
6042                 ModeHighlight();
6043             }
6044             return;
6045         }
6046         /* if enable, use want disable icsEngineAnalyze */
6047         if (appData.icsEngineAnalyze) {
6048                 ExitAnalyzeMode();
6049                 ModeHighlight();
6050                 return;
6051         }
6052         appData.icsEngineAnalyze = TRUE;
6053         if (appData.debugMode)
6054             fprintf(debugFP, _("ICS engine analyze starting... \n"));
6055     }
6056     if (!appData.showThinking)
6057       ShowThinkingProc(w,event,prms,nprms);
6058
6059     AnalyzeModeEvent();
6060 }
6061
6062 void AnalyzeFileProc(w, event, prms, nprms)
6063      Widget w;
6064      XEvent *event;
6065      String *prms;
6066      Cardinal *nprms;
6067 {
6068     if (!first.analysisSupport) {
6069       char buf[MSG_SIZ];
6070       snprintf(buf, sizeof(buf), _("%s does not support analysis"), first.tidy);
6071       DisplayError(buf, 0);
6072       return;
6073     }
6074     Reset(FALSE, TRUE);
6075
6076     if (!appData.showThinking)
6077       ShowThinkingProc(w,event,prms,nprms);
6078
6079     AnalyzeFileEvent();
6080     FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
6081     AnalysisPeriodicEvent(1);
6082 }
6083
6084 void TwoMachinesProc(w, event, prms, nprms)
6085      Widget w;
6086      XEvent *event;
6087      String *prms;
6088      Cardinal *nprms;
6089 {
6090     TwoMachinesEvent();
6091 }
6092
6093 void IcsClientProc(w, event, prms, nprms)
6094      Widget w;
6095      XEvent *event;
6096      String *prms;
6097      Cardinal *nprms;
6098 {
6099     IcsClientEvent();
6100 }
6101
6102 void EditGameProc(w, event, prms, nprms)
6103      Widget w;
6104      XEvent *event;
6105      String *prms;
6106      Cardinal *nprms;
6107 {
6108     EditGameEvent();
6109 }
6110
6111 void EditPositionProc(w, event, prms, nprms)
6112      Widget w;
6113      XEvent *event;
6114      String *prms;
6115      Cardinal *nprms;
6116 {
6117     EditPositionEvent();
6118 }
6119
6120 void TrainingProc(w, event, prms, nprms)
6121      Widget w;
6122      XEvent *event;
6123      String *prms;
6124      Cardinal *nprms;
6125 {
6126     TrainingEvent();
6127 }
6128
6129 void EditCommentProc(w, event, prms, nprms)
6130      Widget w;
6131      XEvent *event;
6132      String *prms;
6133      Cardinal *nprms;
6134 {
6135     if (editUp) {
6136         EditCommentPopDown();
6137     } else {
6138         EditCommentEvent();
6139     }
6140 }
6141
6142 void IcsInputBoxProc(w, event, prms, nprms)
6143      Widget w;
6144      XEvent *event;
6145      String *prms;
6146      Cardinal *nprms;
6147 {
6148     if (ICSInputBoxUp) {
6149         ICSInputBoxPopDown();
6150     } else {
6151         ICSInputBoxPopUp();
6152     }
6153 }
6154
6155 void AcceptProc(w, event, prms, nprms)
6156      Widget w;
6157      XEvent *event;
6158      String *prms;
6159      Cardinal *nprms;
6160 {
6161     AcceptEvent();
6162 }
6163
6164 void DeclineProc(w, event, prms, nprms)
6165      Widget w;
6166      XEvent *event;
6167      String *prms;
6168      Cardinal *nprms;
6169 {
6170     DeclineEvent();
6171 }
6172
6173 void RematchProc(w, event, prms, nprms)
6174      Widget w;
6175      XEvent *event;
6176      String *prms;
6177      Cardinal *nprms;
6178 {
6179     RematchEvent();
6180 }
6181
6182 void CallFlagProc(w, event, prms, nprms)
6183      Widget w;
6184      XEvent *event;
6185      String *prms;
6186      Cardinal *nprms;
6187 {
6188     CallFlagEvent();
6189 }
6190
6191 void DrawProc(w, event, prms, nprms)
6192      Widget w;
6193      XEvent *event;
6194      String *prms;
6195      Cardinal *nprms;
6196 {
6197     DrawEvent();
6198 }
6199
6200 void AbortProc(w, event, prms, nprms)
6201      Widget w;
6202      XEvent *event;
6203      String *prms;
6204      Cardinal *nprms;
6205 {
6206     AbortEvent();
6207 }
6208
6209 void AdjournProc(w, event, prms, nprms)
6210      Widget w;
6211      XEvent *event;
6212      String *prms;
6213      Cardinal *nprms;
6214 {
6215     AdjournEvent();
6216 }
6217
6218 void ResignProc(w, event, prms, nprms)
6219      Widget w;
6220      XEvent *event;
6221      String *prms;
6222      Cardinal *nprms;
6223 {
6224     ResignEvent();
6225 }
6226
6227 void AdjuWhiteProc(w, event, prms, nprms)
6228      Widget w;
6229      XEvent *event;
6230      String *prms;
6231      Cardinal *nprms;
6232 {
6233     UserAdjudicationEvent(+1);
6234 }
6235
6236 void AdjuBlackProc(w, event, prms, nprms)
6237      Widget w;
6238      XEvent *event;
6239      String *prms;
6240      Cardinal *nprms;
6241 {
6242     UserAdjudicationEvent(-1);
6243 }
6244
6245 void AdjuDrawProc(w, event, prms, nprms)
6246      Widget w;
6247      XEvent *event;
6248      String *prms;
6249      Cardinal *nprms;
6250 {
6251     UserAdjudicationEvent(0);
6252 }
6253
6254 void EnterKeyProc(w, event, prms, nprms)
6255      Widget w;
6256      XEvent *event;
6257      String *prms;
6258      Cardinal *nprms;
6259 {
6260     if (ICSInputBoxUp == True)
6261       ICSInputSendText();
6262 }
6263
6264 void UpKeyProc(w, event, prms, nprms)
6265      Widget w;
6266      XEvent *event;
6267      String *prms;
6268      Cardinal *nprms;
6269 {   // [HGM] input: let up-arrow recall previous line from history
6270     Widget edit;
6271     int j;
6272     Arg args[16];
6273     String val;
6274     XawTextBlock t;
6275
6276     if (!ICSInputBoxUp) return;
6277     edit = XtNameToWidget(ICSInputShell, "*form.text");
6278     j = 0;
6279     XtSetArg(args[j], XtNstring, &val); j++;
6280     XtGetValues(edit, args, j);
6281     val = PrevInHistory(val);
6282     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6283     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6284     if(val) {
6285         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6286         XawTextReplace(edit, 0, 0, &t);
6287         XawTextSetInsertionPoint(edit, 9999);
6288     }
6289 }
6290
6291 void DownKeyProc(w, event, prms, nprms)
6292      Widget w;
6293      XEvent *event;
6294      String *prms;
6295      Cardinal *nprms;
6296 {   // [HGM] input: let down-arrow recall next line from history
6297     Widget edit;
6298     String val;
6299     XawTextBlock t;
6300
6301     if (!ICSInputBoxUp) return;
6302     edit = XtNameToWidget(ICSInputShell, "*form.text");
6303     val = NextInHistory();
6304     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
6305     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
6306     if(val) {
6307         t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
6308         XawTextReplace(edit, 0, 0, &t);
6309         XawTextSetInsertionPoint(edit, 9999);
6310     }
6311 }
6312
6313 void StopObservingProc(w, event, prms, nprms)
6314      Widget w;
6315      XEvent *event;
6316      String *prms;
6317      Cardinal *nprms;
6318 {
6319     StopObservingEvent();
6320 }
6321
6322 void StopExaminingProc(w, event, prms, nprms)
6323      Widget w;
6324      XEvent *event;
6325      String *prms;
6326      Cardinal *nprms;
6327 {
6328     StopExaminingEvent();
6329 }
6330
6331 void UploadProc(w, event, prms, nprms)
6332      Widget w;
6333      XEvent *event;
6334      String *prms;
6335      Cardinal *nprms;
6336 {
6337     UploadGameEvent();
6338 }
6339
6340
6341 void ForwardProc(w, event, prms, nprms)
6342      Widget w;
6343      XEvent *event;
6344      String *prms;
6345      Cardinal *nprms;
6346 {
6347     ForwardEvent();
6348 }
6349
6350
6351 void BackwardProc(w, event, prms, nprms)
6352      Widget w;
6353      XEvent *event;
6354      String *prms;
6355      Cardinal *nprms;
6356 {
6357     BackwardEvent();
6358 }
6359
6360 void ToStartProc(w, event, prms, nprms)
6361      Widget w;
6362      XEvent *event;
6363      String *prms;
6364      Cardinal *nprms;
6365 {
6366     ToStartEvent();
6367 }
6368
6369 void ToEndProc(w, event, prms, nprms)
6370      Widget w;
6371      XEvent *event;
6372      String *prms;
6373      Cardinal *nprms;
6374 {
6375     ToEndEvent();
6376 }
6377
6378 void RevertProc(w, event, prms, nprms)
6379      Widget w;
6380      XEvent *event;
6381      String *prms;
6382      Cardinal *nprms;
6383 {
6384     RevertEvent(False);
6385 }
6386
6387 void AnnotateProc(w, event, prms, nprms)
6388      Widget w;
6389      XEvent *event;
6390      String *prms;
6391      Cardinal *nprms;
6392 {
6393     RevertEvent(True);
6394 }
6395
6396 void TruncateGameProc(w, event, prms, nprms)
6397      Widget w;
6398      XEvent *event;
6399      String *prms;
6400      Cardinal *nprms;
6401 {
6402     TruncateGameEvent();
6403 }
6404 void RetractMoveProc(w, event, prms, nprms)
6405      Widget w;
6406      XEvent *event;
6407      String *prms;
6408      Cardinal *nprms;
6409 {
6410     RetractMoveEvent();
6411 }
6412
6413 void MoveNowProc(w, event, prms, nprms)
6414      Widget w;
6415      XEvent *event;
6416      String *prms;
6417      Cardinal *nprms;
6418 {
6419     MoveNowEvent();
6420 }
6421
6422
6423 void AlwaysQueenProc(w, event, prms, nprms)
6424      Widget w;
6425      XEvent *event;
6426      String *prms;
6427      Cardinal *nprms;
6428 {
6429     Arg args[16];
6430
6431     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6432
6433     if (appData.alwaysPromoteToQueen) {
6434         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6435     } else {
6436         XtSetArg(args[0], XtNleftBitmap, None);
6437     }
6438     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6439                 args, 1);
6440 }
6441
6442 void AnimateDraggingProc(w, event, prms, nprms)
6443      Widget w;
6444      XEvent *event;
6445      String *prms;
6446      Cardinal *nprms;
6447 {
6448     Arg args[16];
6449
6450     appData.animateDragging = !appData.animateDragging;
6451
6452     if (appData.animateDragging) {
6453         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6454         CreateAnimVars();
6455     } else {
6456         XtSetArg(args[0], XtNleftBitmap, None);
6457     }
6458     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6459                 args, 1);
6460 }
6461
6462 void AnimateMovingProc(w, event, prms, nprms)
6463      Widget w;
6464      XEvent *event;
6465      String *prms;
6466      Cardinal *nprms;
6467 {
6468     Arg args[16];
6469
6470     appData.animate = !appData.animate;
6471
6472     if (appData.animate) {
6473         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6474         CreateAnimVars();
6475     } else {
6476         XtSetArg(args[0], XtNleftBitmap, None);
6477     }
6478     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6479                 args, 1);
6480 }
6481
6482 void AutocommProc(w, event, prms, nprms)
6483      Widget w;
6484      XEvent *event;
6485      String *prms;
6486      Cardinal *nprms;
6487 {
6488     Arg args[16];
6489
6490     appData.autoComment = !appData.autoComment;
6491
6492     if (appData.autoComment) {
6493         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6494     } else {
6495         XtSetArg(args[0], XtNleftBitmap, None);
6496     }
6497     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6498                 args, 1);
6499 }
6500
6501
6502 void AutoflagProc(w, event, prms, nprms)
6503      Widget w;
6504      XEvent *event;
6505      String *prms;
6506      Cardinal *nprms;
6507 {
6508     Arg args[16];
6509
6510     appData.autoCallFlag = !appData.autoCallFlag;
6511
6512     if (appData.autoCallFlag) {
6513         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6514     } else {
6515         XtSetArg(args[0], XtNleftBitmap, None);
6516     }
6517     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6518                 args, 1);
6519 }
6520
6521 void AutoflipProc(w, event, prms, nprms)
6522      Widget w;
6523      XEvent *event;
6524      String *prms;
6525      Cardinal *nprms;
6526 {
6527     Arg args[16];
6528
6529     appData.autoFlipView = !appData.autoFlipView;
6530
6531     if (appData.autoFlipView) {
6532         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6533     } else {
6534         XtSetArg(args[0], XtNleftBitmap, None);
6535     }
6536     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6537                 args, 1);
6538 }
6539
6540 void AutobsProc(w, event, prms, nprms)
6541      Widget w;
6542      XEvent *event;
6543      String *prms;
6544      Cardinal *nprms;
6545 {
6546     Arg args[16];
6547
6548     appData.autoObserve = !appData.autoObserve;
6549
6550     if (appData.autoObserve) {
6551         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6552     } else {
6553         XtSetArg(args[0], XtNleftBitmap, None);
6554     }
6555     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6556                 args, 1);
6557 }
6558
6559 void AutoraiseProc(w, event, prms, nprms)
6560      Widget w;
6561      XEvent *event;
6562      String *prms;
6563      Cardinal *nprms;
6564 {
6565     Arg args[16];
6566
6567     appData.autoRaiseBoard = !appData.autoRaiseBoard;
6568
6569     if (appData.autoRaiseBoard) {
6570         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6571     } else {
6572         XtSetArg(args[0], XtNleftBitmap, None);
6573     }
6574     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
6575                 args, 1);
6576 }
6577
6578 void AutosaveProc(w, event, prms, nprms)
6579      Widget w;
6580      XEvent *event;
6581      String *prms;
6582      Cardinal *nprms;
6583 {
6584     Arg args[16];
6585
6586     appData.autoSaveGames = !appData.autoSaveGames;
6587
6588     if (appData.autoSaveGames) {
6589         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6590     } else {
6591         XtSetArg(args[0], XtNleftBitmap, None);
6592     }
6593     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
6594                 args, 1);
6595 }
6596
6597 void BlindfoldProc(w, event, prms, nprms)
6598      Widget w;
6599      XEvent *event;
6600      String *prms;
6601      Cardinal *nprms;
6602 {
6603     Arg args[16];
6604
6605     appData.blindfold = !appData.blindfold;
6606
6607     if (appData.blindfold) {
6608         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6609     } else {
6610         XtSetArg(args[0], XtNleftBitmap, None);
6611     }
6612     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6613                 args, 1);
6614
6615     DrawPosition(True, NULL);
6616 }
6617
6618 void TestLegalityProc(w, event, prms, nprms)
6619      Widget w;
6620      XEvent *event;
6621      String *prms;
6622      Cardinal *nprms;
6623 {
6624     Arg args[16];
6625
6626     appData.testLegality = !appData.testLegality;
6627
6628     if (appData.testLegality) {
6629         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6630     } else {
6631         XtSetArg(args[0], XtNleftBitmap, None);
6632     }
6633     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6634                 args, 1);
6635 }
6636
6637
6638 void FlashMovesProc(w, event, prms, nprms)
6639      Widget w;
6640      XEvent *event;
6641      String *prms;
6642      Cardinal *nprms;
6643 {
6644     Arg args[16];
6645
6646     if (appData.flashCount == 0) {
6647         appData.flashCount = 3;
6648     } else {
6649         appData.flashCount = -appData.flashCount;
6650     }
6651
6652     if (appData.flashCount > 0) {
6653         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6654     } else {
6655         XtSetArg(args[0], XtNleftBitmap, None);
6656     }
6657     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6658                 args, 1);
6659 }
6660
6661 void FlipViewProc(w, event, prms, nprms)
6662      Widget w;
6663      XEvent *event;
6664      String *prms;
6665      Cardinal *nprms;
6666 {
6667     flipView = !flipView;
6668     DrawPosition(True, NULL);
6669 }
6670
6671 void GetMoveListProc(w, event, prms, nprms)
6672      Widget w;
6673      XEvent *event;
6674      String *prms;
6675      Cardinal *nprms;
6676 {
6677     Arg args[16];
6678
6679     appData.getMoveList = !appData.getMoveList;
6680
6681     if (appData.getMoveList) {
6682         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6683         GetMoveListEvent();
6684     } else {
6685         XtSetArg(args[0], XtNleftBitmap, None);
6686     }
6687     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
6688                 args, 1);
6689 }
6690
6691 #if HIGHDRAG
6692 void HighlightDraggingProc(w, event, prms, nprms)
6693      Widget w;
6694      XEvent *event;
6695      String *prms;
6696      Cardinal *nprms;
6697 {
6698     Arg args[16];
6699
6700     appData.highlightDragging = !appData.highlightDragging;
6701
6702     if (appData.highlightDragging) {
6703         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6704     } else {
6705         XtSetArg(args[0], XtNleftBitmap, None);
6706     }
6707     XtSetValues(XtNameToWidget(menuBarWidget,
6708                                "menuOptions.Highlight Dragging"), args, 1);
6709 }
6710 #endif
6711
6712 void HighlightLastMoveProc(w, event, prms, nprms)
6713      Widget w;
6714      XEvent *event;
6715      String *prms;
6716      Cardinal *nprms;
6717 {
6718     Arg args[16];
6719
6720     appData.highlightLastMove = !appData.highlightLastMove;
6721
6722     if (appData.highlightLastMove) {
6723         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6724     } else {
6725         XtSetArg(args[0], XtNleftBitmap, None);
6726     }
6727     XtSetValues(XtNameToWidget(menuBarWidget,
6728                                "menuOptions.Highlight Last Move"), args, 1);
6729 }
6730
6731 void IcsAlarmProc(w, event, prms, nprms)
6732      Widget w;
6733      XEvent *event;
6734      String *prms;
6735      Cardinal *nprms;
6736 {
6737     Arg args[16];
6738
6739     appData.icsAlarm = !appData.icsAlarm;
6740
6741     if (appData.icsAlarm) {
6742         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6743     } else {
6744         XtSetArg(args[0], XtNleftBitmap, None);
6745     }
6746     XtSetValues(XtNameToWidget(menuBarWidget,
6747                                "menuOptions.ICS Alarm"), args, 1);
6748 }
6749
6750 void MoveSoundProc(w, event, prms, nprms)
6751      Widget w;
6752      XEvent *event;
6753      String *prms;
6754      Cardinal *nprms;
6755 {
6756     Arg args[16];
6757
6758     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6759
6760     if (appData.ringBellAfterMoves) {
6761         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6762     } else {
6763         XtSetArg(args[0], XtNleftBitmap, None);
6764     }
6765     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6766                 args, 1);
6767 }
6768
6769
6770 void OldSaveStyleProc(w, event, prms, nprms)
6771      Widget w;
6772      XEvent *event;
6773      String *prms;
6774      Cardinal *nprms;
6775 {
6776     Arg args[16];
6777
6778     appData.oldSaveStyle = !appData.oldSaveStyle;
6779
6780     if (appData.oldSaveStyle) {
6781         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6782     } else {
6783         XtSetArg(args[0], XtNleftBitmap, None);
6784     }
6785     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
6786                 args, 1);
6787 }
6788
6789 void PeriodicUpdatesProc(w, event, prms, nprms)
6790      Widget w;
6791      XEvent *event;
6792      String *prms;
6793      Cardinal *nprms;
6794 {
6795     Arg args[16];
6796
6797     PeriodicUpdatesEvent(!appData.periodicUpdates);
6798
6799     if (appData.periodicUpdates) {
6800         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6801     } else {
6802         XtSetArg(args[0], XtNleftBitmap, None);
6803     }
6804     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6805                 args, 1);
6806 }
6807
6808 void PonderNextMoveProc(w, event, prms, nprms)
6809      Widget w;
6810      XEvent *event;
6811      String *prms;
6812      Cardinal *nprms;
6813 {
6814     Arg args[16];
6815
6816     PonderNextMoveEvent(!appData.ponderNextMove);
6817
6818     if (appData.ponderNextMove) {
6819         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6820     } else {
6821         XtSetArg(args[0], XtNleftBitmap, None);
6822     }
6823     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6824                 args, 1);
6825 }
6826
6827 void PopupExitMessageProc(w, event, prms, nprms)
6828      Widget w;
6829      XEvent *event;
6830      String *prms;
6831      Cardinal *nprms;
6832 {
6833     Arg args[16];
6834
6835     appData.popupExitMessage = !appData.popupExitMessage;
6836
6837     if (appData.popupExitMessage) {
6838         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6839     } else {
6840         XtSetArg(args[0], XtNleftBitmap, None);
6841     }
6842     XtSetValues(XtNameToWidget(menuBarWidget,
6843                                "menuOptions.Popup Exit Message"), args, 1);
6844 }
6845
6846 void PopupMoveErrorsProc(w, event, prms, nprms)
6847      Widget w;
6848      XEvent *event;
6849      String *prms;
6850      Cardinal *nprms;
6851 {
6852     Arg args[16];
6853
6854     appData.popupMoveErrors = !appData.popupMoveErrors;
6855
6856     if (appData.popupMoveErrors) {
6857         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6858     } else {
6859         XtSetArg(args[0], XtNleftBitmap, None);
6860     }
6861     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6862                 args, 1);
6863 }
6864
6865 void PremoveProc(w, event, prms, nprms)
6866      Widget w;
6867      XEvent *event;
6868      String *prms;
6869      Cardinal *nprms;
6870 {
6871     Arg args[16];
6872
6873     appData.premove = !appData.premove;
6874
6875     if (appData.premove) {
6876         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6877     } else {
6878         XtSetArg(args[0], XtNleftBitmap, None);
6879     }
6880     XtSetValues(XtNameToWidget(menuBarWidget,
6881                                "menuOptions.Premove"), args, 1);
6882 }
6883
6884 void QuietPlayProc(w, event, prms, nprms)
6885      Widget w;
6886      XEvent *event;
6887      String *prms;
6888      Cardinal *nprms;
6889 {
6890     Arg args[16];
6891
6892     appData.quietPlay = !appData.quietPlay;
6893
6894     if (appData.quietPlay) {
6895         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6896     } else {
6897         XtSetArg(args[0], XtNleftBitmap, None);
6898     }
6899     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
6900                 args, 1);
6901 }
6902
6903 void ShowCoordsProc(w, event, prms, nprms)
6904      Widget w;
6905      XEvent *event;
6906      String *prms;
6907      Cardinal *nprms;
6908 {
6909     Arg args[16];
6910
6911     appData.showCoords = !appData.showCoords;
6912
6913     if (appData.showCoords) {
6914         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6915     } else {
6916         XtSetArg(args[0], XtNleftBitmap, None);
6917     }
6918     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6919                 args, 1);
6920
6921     DrawPosition(True, NULL);
6922 }
6923
6924 void ShowThinkingProc(w, event, prms, nprms)
6925      Widget w;
6926      XEvent *event;
6927      String *prms;
6928      Cardinal *nprms;
6929 {
6930     appData.showThinking = !appData.showThinking; // [HGM] thinking: tken out of ShowThinkingEvent
6931     ShowThinkingEvent();
6932 }
6933
6934 void HideThinkingProc(w, event, prms, nprms)
6935      Widget w;
6936      XEvent *event;
6937      String *prms;
6938      Cardinal *nprms;
6939 {
6940     Arg args[16];
6941
6942     appData.hideThinkingFromHuman = !appData.hideThinkingFromHuman; // [HGM] thinking: tken out of ShowThinkingEvent
6943     ShowThinkingEvent();
6944
6945     if (appData.hideThinkingFromHuman) {
6946         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6947     } else {
6948         XtSetArg(args[0], XtNleftBitmap, None);
6949     }
6950     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
6951                 args, 1);
6952 }
6953
6954 void SaveOnExitProc(w, event, prms, nprms)
6955      Widget w;
6956      XEvent *event;
6957      String *prms;
6958      Cardinal *nprms;
6959 {
6960     Arg args[16];
6961
6962     saveSettingsOnExit = !saveSettingsOnExit;
6963
6964     if (saveSettingsOnExit) {
6965         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6966     } else {
6967         XtSetArg(args[0], XtNleftBitmap, None);
6968     }
6969     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
6970                 args, 1);
6971 }
6972
6973 void SaveSettingsProc(w, event, prms, nprms)
6974      Widget w;
6975      XEvent *event;
6976      String *prms;
6977      Cardinal *nprms;
6978 {
6979      SaveSettings(settingsFileName);
6980 }
6981
6982 void InfoProc(w, event, prms, nprms)
6983      Widget w;
6984      XEvent *event;
6985      String *prms;
6986      Cardinal *nprms;
6987 {
6988     char buf[MSG_SIZ];
6989     snprintf(buf, sizeof(buf), "xterm -e info --directory %s --directory . -f %s &",
6990             INFODIR, INFOFILE);
6991     system(buf);
6992 }
6993
6994 void ManProc(w, event, prms, nprms)
6995      Widget w;
6996      XEvent *event;
6997      String *prms;
6998      Cardinal *nprms;
6999 {
7000     char buf[MSG_SIZ];
7001     String name;
7002     if (nprms && *nprms > 0)
7003       name = prms[0];
7004     else
7005       name = "xboard";
7006     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
7007     system(buf);
7008 }
7009
7010 void HintProc(w, event, prms, nprms)
7011      Widget w;
7012      XEvent *event;
7013      String *prms;
7014      Cardinal *nprms;
7015 {
7016     HintEvent();
7017 }
7018
7019 void BookProc(w, event, prms, nprms)
7020      Widget w;
7021      XEvent *event;
7022      String *prms;
7023      Cardinal *nprms;
7024 {
7025     BookEvent();
7026 }
7027
7028 void AboutProc(w, event, prms, nprms)
7029      Widget w;
7030      XEvent *event;
7031      String *prms;
7032      Cardinal *nprms;
7033 {
7034     char buf[MSG_SIZ];
7035 #if ZIPPY
7036     char *zippy = " (with Zippy code)";
7037 #else
7038     char *zippy = "";
7039 #endif
7040     snprintf(buf, sizeof(buf), "%s%s\n\n%s\n%s\n%s\n\n%s%s\n%s",
7041             programVersion, zippy,
7042             "Copyright 1991 Digital Equipment Corporation",
7043             "Enhancements Copyright 1992-2009 Free Software Foundation",
7044             "Enhancements Copyright 2005 Alessandro Scotti",
7045             PACKAGE, " is free software and carries NO WARRANTY;",
7046             "see the file COPYING for more information.");
7047     ErrorPopUp(_("About XBoard"), buf, FALSE);
7048 }
7049
7050 void DebugProc(w, event, prms, nprms)
7051      Widget w;
7052      XEvent *event;
7053      String *prms;
7054      Cardinal *nprms;
7055 {
7056     appData.debugMode = !appData.debugMode;
7057 }
7058
7059 void AboutGameProc(w, event, prms, nprms)
7060      Widget w;
7061      XEvent *event;
7062      String *prms;
7063      Cardinal *nprms;
7064 {
7065     AboutGameEvent();
7066 }
7067
7068 void NothingProc(w, event, prms, nprms)
7069      Widget w;
7070      XEvent *event;
7071      String *prms;
7072      Cardinal *nprms;
7073 {
7074     return;
7075 }
7076
7077 void Iconify(w, event, prms, nprms)
7078      Widget w;
7079      XEvent *event;
7080      String *prms;
7081      Cardinal *nprms;
7082 {
7083     Arg args[16];
7084
7085     fromX = fromY = -1;
7086     XtSetArg(args[0], XtNiconic, True);
7087     XtSetValues(shellWidget, args, 1);
7088 }
7089
7090 void DisplayMessage(message, extMessage)
7091      char *message, *extMessage;
7092 {
7093   /* display a message in the message widget */
7094
7095   char buf[MSG_SIZ];
7096   Arg arg;
7097
7098   if (extMessage)
7099     {
7100       if (*message)
7101         {
7102           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
7103           message = buf;
7104         }
7105       else
7106         {
7107           message = extMessage;
7108         };
7109     };
7110
7111   /* need to test if messageWidget already exists, since this function
7112      can also be called during the startup, if for example a Xresource
7113      is not set up correctly */
7114   if(messageWidget)
7115     {
7116       XtSetArg(arg, XtNlabel, message);
7117       XtSetValues(messageWidget, &arg, 1);
7118     };
7119
7120   return;
7121 }
7122
7123 void DisplayTitle(text)
7124      char *text;
7125 {
7126     Arg args[16];
7127     int i;
7128     char title[MSG_SIZ];
7129     char icon[MSG_SIZ];
7130
7131     if (text == NULL) text = "";
7132
7133     if (appData.titleInWindow) {
7134         i = 0;
7135         XtSetArg(args[i], XtNlabel, text);   i++;
7136         XtSetValues(titleWidget, args, i);
7137     }
7138
7139     if (*text != NULLCHAR) {
7140       safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
7141       safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
7142     } else if (appData.icsActive) {
7143         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
7144         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
7145     } else if (appData.cmailGameName[0] != NULLCHAR) {
7146         snprintf(icon, sizeof(icon), "%s", "CMail");
7147         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
7148 #ifdef GOTHIC
7149     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
7150     } else if (gameInfo.variant == VariantGothic) {
7151       safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
7152       safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
7153 #endif
7154 #ifdef FALCON
7155     } else if (gameInfo.variant == VariantFalcon) {
7156       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7157       safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
7158 #endif
7159     } else if (appData.noChessProgram) {
7160       safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
7161       safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
7162     } else {
7163       safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
7164         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
7165     }
7166     i = 0;
7167     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
7168     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
7169     XtSetValues(shellWidget, args, i);
7170 }
7171
7172
7173 void
7174 DisplayError(message, error)
7175      String message;
7176      int error;
7177 {
7178     char buf[MSG_SIZ];
7179
7180     if (error == 0) {
7181         if (appData.debugMode || appData.matchMode) {
7182             fprintf(stderr, "%s: %s\n", programName, message);
7183         }
7184     } else {
7185         if (appData.debugMode || appData.matchMode) {
7186             fprintf(stderr, "%s: %s: %s\n",
7187                     programName, message, strerror(error));
7188         }
7189         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7190         message = buf;
7191     }
7192     ErrorPopUp(_("Error"), message, FALSE);
7193 }
7194
7195
7196 void DisplayMoveError(message)
7197      String message;
7198 {
7199     fromX = fromY = -1;
7200     ClearHighlights();
7201     DrawPosition(FALSE, NULL);
7202     if (appData.debugMode || appData.matchMode) {
7203         fprintf(stderr, "%s: %s\n", programName, message);
7204     }
7205     if (appData.popupMoveErrors) {
7206         ErrorPopUp(_("Error"), message, FALSE);
7207     } else {
7208         DisplayMessage(message, "");
7209     }
7210 }
7211
7212
7213 void DisplayFatalError(message, error, status)
7214      String message;
7215      int error, status;
7216 {
7217     char buf[MSG_SIZ];
7218
7219     errorExitStatus = status;
7220     if (error == 0) {
7221         fprintf(stderr, "%s: %s\n", programName, message);
7222     } else {
7223         fprintf(stderr, "%s: %s: %s\n",
7224                 programName, message, strerror(error));
7225         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
7226         message = buf;
7227     }
7228     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
7229       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
7230     } else {
7231       ExitEvent(status);
7232     }
7233 }
7234
7235 void DisplayInformation(message)
7236      String message;
7237 {
7238     ErrorPopDown();
7239     ErrorPopUp(_("Information"), message, TRUE);
7240 }
7241
7242 void DisplayNote(message)
7243      String message;
7244 {
7245     ErrorPopDown();
7246     ErrorPopUp(_("Note"), message, FALSE);
7247 }
7248
7249 static int
7250 NullXErrorCheck(dpy, error_event)
7251      Display *dpy;
7252      XErrorEvent *error_event;
7253 {
7254     return 0;
7255 }
7256
7257 void DisplayIcsInteractionTitle(message)
7258      String message;
7259 {
7260   if (oldICSInteractionTitle == NULL) {
7261     /* Magic to find the old window title, adapted from vim */
7262     char *wina = getenv("WINDOWID");
7263     if (wina != NULL) {
7264       Window win = (Window) atoi(wina);
7265       Window root, parent, *children;
7266       unsigned int nchildren;
7267       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
7268       for (;;) {
7269         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
7270         if (!XQueryTree(xDisplay, win, &root, &parent,
7271                         &children, &nchildren)) break;
7272         if (children) XFree((void *)children);
7273         if (parent == root || parent == 0) break;
7274         win = parent;
7275       }
7276       XSetErrorHandler(oldHandler);
7277     }
7278     if (oldICSInteractionTitle == NULL) {
7279       oldICSInteractionTitle = "xterm";
7280     }
7281   }
7282   printf("\033]0;%s\007", message);
7283   fflush(stdout);
7284 }
7285
7286 char pendingReplyPrefix[MSG_SIZ];
7287 ProcRef pendingReplyPR;
7288
7289 void AskQuestionProc(w, event, prms, nprms)
7290      Widget w;
7291      XEvent *event;
7292      String *prms;
7293      Cardinal *nprms;
7294 {
7295     if (*nprms != 4) {
7296         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
7297                 *nprms);
7298         return;
7299     }
7300     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
7301 }
7302
7303 void AskQuestionPopDown()
7304 {
7305     if (!askQuestionUp) return;
7306     XtPopdown(askQuestionShell);
7307     XtDestroyWidget(askQuestionShell);
7308     askQuestionUp = False;
7309 }
7310
7311 void AskQuestionReplyAction(w, event, prms, nprms)
7312      Widget w;
7313      XEvent *event;
7314      String *prms;
7315      Cardinal *nprms;
7316 {
7317     char buf[MSG_SIZ];
7318     int err;
7319     String reply;
7320
7321     reply = XawDialogGetValueString(w = XtParent(w));
7322     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
7323     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
7324     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
7325     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
7326     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
7327     AskQuestionPopDown();
7328
7329     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
7330 }
7331
7332 void AskQuestionCallback(w, client_data, call_data)
7333      Widget w;
7334      XtPointer client_data, call_data;
7335 {
7336     String name;
7337     Arg args[16];
7338
7339     XtSetArg(args[0], XtNlabel, &name);
7340     XtGetValues(w, args, 1);
7341
7342     if (strcmp(name, _("cancel")) == 0) {
7343         AskQuestionPopDown();
7344     } else {
7345         AskQuestionReplyAction(w, NULL, NULL, NULL);
7346     }
7347 }
7348
7349 void AskQuestion(title, question, replyPrefix, pr)
7350      char *title, *question, *replyPrefix;
7351      ProcRef pr;
7352 {
7353     Arg args[16];
7354     Widget popup, layout, dialog, edit;
7355     Window root, child;
7356     int x, y, i;
7357     int win_x, win_y;
7358     unsigned int mask;
7359
7360     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
7361     pendingReplyPR = pr;
7362
7363     i = 0;
7364     XtSetArg(args[i], XtNresizable, True); i++;
7365     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
7366     askQuestionShell = popup =
7367       XtCreatePopupShell(title, transientShellWidgetClass,
7368                          shellWidget, args, i);
7369
7370     layout =
7371       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
7372                             layoutArgs, XtNumber(layoutArgs));
7373
7374     i = 0;
7375     XtSetArg(args[i], XtNlabel, question); i++;
7376     XtSetArg(args[i], XtNvalue, ""); i++;
7377     XtSetArg(args[i], XtNborderWidth, 0); i++;
7378     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
7379                                    layout, args, i);
7380
7381     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
7382                        (XtPointer) dialog);
7383     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
7384                        (XtPointer) dialog);
7385
7386     XtRealizeWidget(popup);
7387     CatchDeleteWindow(popup, "AskQuestionPopDown");
7388
7389     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
7390                   &x, &y, &win_x, &win_y, &mask);
7391
7392     XtSetArg(args[0], XtNx, x - 10);
7393     XtSetArg(args[1], XtNy, y - 30);
7394     XtSetValues(popup, args, 2);
7395
7396     XtPopup(popup, XtGrabExclusive);
7397     askQuestionUp = True;
7398
7399     edit = XtNameToWidget(dialog, "*value");
7400     XtSetKeyboardFocus(popup, edit);
7401 }
7402
7403
7404 void
7405 PlaySound(name)
7406      char *name;
7407 {
7408   if (*name == NULLCHAR) {
7409     return;
7410   } else if (strcmp(name, "$") == 0) {
7411     putc(BELLCHAR, stderr);
7412   } else {
7413     char buf[2048];
7414     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
7415     system(buf);
7416   }
7417 }
7418
7419 void
7420 RingBell()
7421 {
7422   PlaySound(appData.soundMove);
7423 }
7424
7425 void
7426 PlayIcsWinSound()
7427 {
7428   PlaySound(appData.soundIcsWin);
7429 }
7430
7431 void
7432 PlayIcsLossSound()
7433 {
7434   PlaySound(appData.soundIcsLoss);
7435 }
7436
7437 void
7438 PlayIcsDrawSound()
7439 {
7440   PlaySound(appData.soundIcsDraw);
7441 }
7442
7443 void
7444 PlayIcsUnfinishedSound()
7445 {
7446   PlaySound(appData.soundIcsUnfinished);
7447 }
7448
7449 void
7450 PlayAlarmSound()
7451 {
7452   PlaySound(appData.soundIcsAlarm);
7453 }
7454
7455 void
7456 EchoOn()
7457 {
7458     system("stty echo");
7459 }
7460
7461 void
7462 EchoOff()
7463 {
7464     system("stty -echo");
7465 }
7466
7467 void
7468 Colorize(cc, continuation)
7469      ColorClass cc;
7470      int continuation;
7471 {
7472     char buf[MSG_SIZ];
7473     int count, outCount, error;
7474
7475     if (textColors[(int)cc].bg > 0) {
7476         if (textColors[(int)cc].fg > 0) {
7477           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7478                    textColors[(int)cc].fg, textColors[(int)cc].bg);
7479         } else {
7480           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7481                    textColors[(int)cc].bg);
7482         }
7483     } else {
7484         if (textColors[(int)cc].fg > 0) {
7485           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
7486                     textColors[(int)cc].fg);
7487         } else {
7488           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
7489         }
7490     }
7491     count = strlen(buf);
7492     outCount = OutputToProcess(NoProc, buf, count, &error);
7493     if (outCount < count) {
7494         DisplayFatalError(_("Error writing to display"), error, 1);
7495     }
7496
7497     if (continuation) return;
7498     switch (cc) {
7499     case ColorShout:
7500       PlaySound(appData.soundShout);
7501       break;
7502     case ColorSShout:
7503       PlaySound(appData.soundSShout);
7504       break;
7505     case ColorChannel1:
7506       PlaySound(appData.soundChannel1);
7507       break;
7508     case ColorChannel:
7509       PlaySound(appData.soundChannel);
7510       break;
7511     case ColorKibitz:
7512       PlaySound(appData.soundKibitz);
7513       break;
7514     case ColorTell:
7515       PlaySound(appData.soundTell);
7516       break;
7517     case ColorChallenge:
7518       PlaySound(appData.soundChallenge);
7519       break;
7520     case ColorRequest:
7521       PlaySound(appData.soundRequest);
7522       break;
7523     case ColorSeek:
7524       PlaySound(appData.soundSeek);
7525       break;
7526     case ColorNormal:
7527     case ColorNone:
7528     default:
7529       break;
7530     }
7531 }
7532
7533 char *UserName()
7534 {
7535     return getpwuid(getuid())->pw_name;
7536 }
7537
7538 static char *
7539 ExpandPathName(path)
7540      char *path;
7541 {
7542     static char static_buf[4*MSG_SIZ];
7543     char *d, *s, buf[4*MSG_SIZ];
7544     struct passwd *pwd;
7545
7546     s = path;
7547     d = static_buf;
7548
7549     while (*s && isspace(*s))
7550       ++s;
7551
7552     if (!*s) {
7553         *d = 0;
7554         return static_buf;
7555     }
7556
7557     if (*s == '~') {
7558         if (*(s+1) == '/') {
7559           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
7560           strcat(d, s+1);
7561         }
7562         else {
7563           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
7564           *strchr(buf, '/') = 0;
7565           pwd = getpwnam(buf);
7566           if (!pwd)
7567             {
7568               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
7569                       buf, path);
7570               return NULL;
7571             }
7572           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
7573           strcat(d, strchr(s+1, '/'));
7574         }
7575     }
7576     else
7577       safeStrCpy(d, s, 4*MSG_SIZ );
7578
7579     return static_buf;
7580 }
7581
7582 char *HostName()
7583 {
7584     static char host_name[MSG_SIZ];
7585
7586 #if HAVE_GETHOSTNAME
7587     gethostname(host_name, MSG_SIZ);
7588     return host_name;
7589 #else  /* not HAVE_GETHOSTNAME */
7590 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7591     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7592     return host_name;
7593 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7594     return "localhost";
7595 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7596 #endif /* not HAVE_GETHOSTNAME */
7597 }
7598
7599 XtIntervalId delayedEventTimerXID = 0;
7600 DelayedEventCallback delayedEventCallback = 0;
7601
7602 void
7603 FireDelayedEvent()
7604 {
7605     delayedEventTimerXID = 0;
7606     delayedEventCallback();
7607 }
7608
7609 void
7610 ScheduleDelayedEvent(cb, millisec)
7611      DelayedEventCallback cb; long millisec;
7612 {
7613     if(delayedEventTimerXID && delayedEventCallback == cb)
7614         // [HGM] alive: replace, rather than add or flush identical event
7615         XtRemoveTimeOut(delayedEventTimerXID);
7616     delayedEventCallback = cb;
7617     delayedEventTimerXID =
7618       XtAppAddTimeOut(appContext, millisec,
7619                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7620 }
7621
7622 DelayedEventCallback
7623 GetDelayedEvent()
7624 {
7625   if (delayedEventTimerXID) {
7626     return delayedEventCallback;
7627   } else {
7628     return NULL;
7629   }
7630 }
7631
7632 void
7633 CancelDelayedEvent()
7634 {
7635   if (delayedEventTimerXID) {
7636     XtRemoveTimeOut(delayedEventTimerXID);
7637     delayedEventTimerXID = 0;
7638   }
7639 }
7640
7641 XtIntervalId loadGameTimerXID = 0;
7642
7643 int LoadGameTimerRunning()
7644 {
7645     return loadGameTimerXID != 0;
7646 }
7647
7648 int StopLoadGameTimer()
7649 {
7650     if (loadGameTimerXID != 0) {
7651         XtRemoveTimeOut(loadGameTimerXID);
7652         loadGameTimerXID = 0;
7653         return TRUE;
7654     } else {
7655         return FALSE;
7656     }
7657 }
7658
7659 void
7660 LoadGameTimerCallback(arg, id)
7661      XtPointer arg;
7662      XtIntervalId *id;
7663 {
7664     loadGameTimerXID = 0;
7665     AutoPlayGameLoop();
7666 }
7667
7668 void
7669 StartLoadGameTimer(millisec)
7670      long millisec;
7671 {
7672     loadGameTimerXID =
7673       XtAppAddTimeOut(appContext, millisec,
7674                       (XtTimerCallbackProc) LoadGameTimerCallback,
7675                       (XtPointer) 0);
7676 }
7677
7678 XtIntervalId analysisClockXID = 0;
7679
7680 void
7681 AnalysisClockCallback(arg, id)
7682      XtPointer arg;
7683      XtIntervalId *id;
7684 {
7685     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
7686          || appData.icsEngineAnalyze) { // [DM]
7687         AnalysisPeriodicEvent(0);
7688         StartAnalysisClock();
7689     }
7690 }
7691
7692 void
7693 StartAnalysisClock()
7694 {
7695     analysisClockXID =
7696       XtAppAddTimeOut(appContext, 2000,
7697                       (XtTimerCallbackProc) AnalysisClockCallback,
7698                       (XtPointer) 0);
7699 }
7700
7701 XtIntervalId clockTimerXID = 0;
7702
7703 int ClockTimerRunning()
7704 {
7705     return clockTimerXID != 0;
7706 }
7707
7708 int StopClockTimer()
7709 {
7710     if (clockTimerXID != 0) {
7711         XtRemoveTimeOut(clockTimerXID);
7712         clockTimerXID = 0;
7713         return TRUE;
7714     } else {
7715         return FALSE;
7716     }
7717 }
7718
7719 void
7720 ClockTimerCallback(arg, id)
7721      XtPointer arg;
7722      XtIntervalId *id;
7723 {
7724     clockTimerXID = 0;
7725     DecrementClocks();
7726 }
7727
7728 void
7729 StartClockTimer(millisec)
7730      long millisec;
7731 {
7732     clockTimerXID =
7733       XtAppAddTimeOut(appContext, millisec,
7734                       (XtTimerCallbackProc) ClockTimerCallback,
7735                       (XtPointer) 0);
7736 }
7737
7738 void
7739 DisplayTimerLabel(w, color, timer, highlight)
7740      Widget w;
7741      char *color;
7742      long timer;
7743      int highlight;
7744 {
7745     char buf[MSG_SIZ];
7746     Arg args[16];
7747
7748     /* check for low time warning */
7749     Pixel foregroundOrWarningColor = timerForegroundPixel;
7750
7751     if (timer > 0 &&
7752         appData.lowTimeWarning &&
7753         (timer / 1000) < appData.icsAlarmTime)
7754       foregroundOrWarningColor = lowTimeWarningColor;
7755
7756     if (appData.clockMode) {
7757       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
7758       XtSetArg(args[0], XtNlabel, buf);
7759     } else {
7760       snprintf(buf, MSG_SIZ, "%s  ", color);
7761       XtSetArg(args[0], XtNlabel, buf);
7762     }
7763
7764     if (highlight) {
7765
7766         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
7767         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7768     } else {
7769         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7770         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
7771     }
7772
7773     XtSetValues(w, args, 3);
7774 }
7775
7776 void
7777 DisplayWhiteClock(timeRemaining, highlight)
7778      long timeRemaining;
7779      int highlight;
7780 {
7781     Arg args[16];
7782
7783     if(appData.noGUI) return;
7784     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
7785     if (highlight && iconPixmap == bIconPixmap) {
7786         iconPixmap = wIconPixmap;
7787         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7788         XtSetValues(shellWidget, args, 1);
7789     }
7790 }
7791
7792 void
7793 DisplayBlackClock(timeRemaining, highlight)
7794      long timeRemaining;
7795      int highlight;
7796 {
7797     Arg args[16];
7798
7799     if(appData.noGUI) return;
7800     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
7801     if (highlight && iconPixmap == wIconPixmap) {
7802         iconPixmap = bIconPixmap;
7803         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7804         XtSetValues(shellWidget, args, 1);
7805     }
7806 }
7807
7808 #define CPNone 0
7809 #define CPReal 1
7810 #define CPComm 2
7811 #define CPSock 3
7812 #define CPLoop 4
7813 typedef int CPKind;
7814
7815 typedef struct {
7816     CPKind kind;
7817     int pid;
7818     int fdTo, fdFrom;
7819 } ChildProc;
7820
7821
7822 int StartChildProcess(cmdLine, dir, pr)
7823      char *cmdLine;
7824      char *dir;
7825      ProcRef *pr;
7826 {
7827     char *argv[64], *p;
7828     int i, pid;
7829     int to_prog[2], from_prog[2];
7830     ChildProc *cp;
7831     char buf[MSG_SIZ];
7832
7833     if (appData.debugMode) {
7834         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7835     }
7836
7837     /* We do NOT feed the cmdLine to the shell; we just
7838        parse it into blank-separated arguments in the
7839        most simple-minded way possible.
7840        */
7841     i = 0;
7842     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
7843     p = buf;
7844     for (;;) {
7845         while(*p == ' ') p++;
7846         argv[i++] = p;
7847         if(*p == '"' || *p == '\'')
7848              p = strchr(++argv[i-1], *p);
7849         else p = strchr(p, ' ');
7850         if (p == NULL) break;
7851         *p++ = NULLCHAR;
7852     }
7853     argv[i] = NULL;
7854
7855     SetUpChildIO(to_prog, from_prog);
7856
7857     if ((pid = fork()) == 0) {
7858         /* Child process */
7859         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
7860         close(to_prog[1]);     // first close the unused pipe ends
7861         close(from_prog[0]);
7862         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
7863         dup2(from_prog[1], 1);
7864         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
7865         close(from_prog[1]);                   // and closing again loses one of the pipes!
7866         if(fileno(stderr) >= 2) // better safe than sorry...
7867                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
7868
7869         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7870             perror(dir);
7871             exit(1);
7872         }
7873
7874         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
7875
7876         execvp(argv[0], argv);
7877
7878         /* If we get here, exec failed */
7879         perror(argv[0]);
7880         exit(1);
7881     }
7882
7883     /* Parent process */
7884     close(to_prog[0]);
7885     close(from_prog[1]);
7886
7887     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7888     cp->kind = CPReal;
7889     cp->pid = pid;
7890     cp->fdFrom = from_prog[0];
7891     cp->fdTo = to_prog[1];
7892     *pr = (ProcRef) cp;
7893     return 0;
7894 }
7895
7896 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
7897 static RETSIGTYPE AlarmCallBack(int n)
7898 {
7899     return;
7900 }
7901
7902 void
7903 DestroyChildProcess(pr, signalType)
7904      ProcRef pr;
7905      int signalType;
7906 {
7907     ChildProc *cp = (ChildProc *) pr;
7908
7909     if (cp->kind != CPReal) return;
7910     cp->kind = CPNone;
7911     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
7912         signal(SIGALRM, AlarmCallBack);
7913         alarm(3);
7914         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
7915             kill(cp->pid, SIGKILL); // kill it forcefully
7916             wait((int *) 0);        // and wait again
7917         }
7918     } else {
7919         if (signalType) {
7920             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
7921         }
7922         /* Process is exiting either because of the kill or because of
7923            a quit command sent by the backend; either way, wait for it to die.
7924         */
7925         wait((int *) 0);
7926     }
7927     close(cp->fdFrom);
7928     close(cp->fdTo);
7929 }
7930
7931 void
7932 InterruptChildProcess(pr)
7933      ProcRef pr;
7934 {
7935     ChildProc *cp = (ChildProc *) pr;
7936
7937     if (cp->kind != CPReal) return;
7938     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7939 }
7940
7941 int OpenTelnet(host, port, pr)
7942      char *host;
7943      char *port;
7944      ProcRef *pr;
7945 {
7946     char cmdLine[MSG_SIZ];
7947
7948     if (port[0] == NULLCHAR) {
7949       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
7950     } else {
7951       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
7952     }
7953     return StartChildProcess(cmdLine, "", pr);
7954 }
7955
7956 int OpenTCP(host, port, pr)
7957      char *host;
7958      char *port;
7959      ProcRef *pr;
7960 {
7961 #if OMIT_SOCKETS
7962     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
7963 #else  /* !OMIT_SOCKETS */
7964     int s;
7965     struct sockaddr_in sa;
7966     struct hostent     *hp;
7967     unsigned short uport;
7968     ChildProc *cp;
7969
7970     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7971         return errno;
7972     }
7973
7974     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7975     sa.sin_family = AF_INET;
7976     sa.sin_addr.s_addr = INADDR_ANY;
7977     uport = (unsigned short) 0;
7978     sa.sin_port = htons(uport);
7979     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7980         return errno;
7981     }
7982
7983     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7984     if (!(hp = gethostbyname(host))) {
7985         int b0, b1, b2, b3;
7986         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7987             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7988             hp->h_addrtype = AF_INET;
7989             hp->h_length = 4;
7990             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7991             hp->h_addr_list[0] = (char *) malloc(4);
7992             hp->h_addr_list[0][0] = b0;
7993             hp->h_addr_list[0][1] = b1;
7994             hp->h_addr_list[0][2] = b2;
7995             hp->h_addr_list[0][3] = b3;
7996         } else {
7997             return ENOENT;
7998         }
7999     }
8000     sa.sin_family = hp->h_addrtype;
8001     uport = (unsigned short) atoi(port);
8002     sa.sin_port = htons(uport);
8003     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
8004
8005     if (connect(s, (struct sockaddr *) &sa,
8006                 sizeof(struct sockaddr_in)) < 0) {
8007         return errno;
8008     }
8009
8010     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8011     cp->kind = CPSock;
8012     cp->pid = 0;
8013     cp->fdFrom = s;
8014     cp->fdTo = s;
8015     *pr = (ProcRef) cp;
8016
8017 #endif /* !OMIT_SOCKETS */
8018
8019     return 0;
8020 }
8021
8022 int OpenCommPort(name, pr)
8023      char *name;
8024      ProcRef *pr;
8025 {
8026     int fd;
8027     ChildProc *cp;
8028
8029     fd = open(name, 2, 0);
8030     if (fd < 0) return errno;
8031
8032     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8033     cp->kind = CPComm;
8034     cp->pid = 0;
8035     cp->fdFrom = fd;
8036     cp->fdTo = fd;
8037     *pr = (ProcRef) cp;
8038
8039     return 0;
8040 }
8041
8042 int OpenLoopback(pr)
8043      ProcRef *pr;
8044 {
8045     ChildProc *cp;
8046     int to[2], from[2];
8047
8048     SetUpChildIO(to, from);
8049
8050     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
8051     cp->kind = CPLoop;
8052     cp->pid = 0;
8053     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
8054     cp->fdTo = to[1];
8055     *pr = (ProcRef) cp;
8056
8057     return 0;
8058 }
8059
8060 int OpenRcmd(host, user, cmd, pr)
8061      char *host, *user, *cmd;
8062      ProcRef *pr;
8063 {
8064     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
8065     return -1;
8066 }
8067
8068 #define INPUT_SOURCE_BUF_SIZE 8192
8069
8070 typedef struct {
8071     CPKind kind;
8072     int fd;
8073     int lineByLine;
8074     char *unused;
8075     InputCallback func;
8076     XtInputId xid;
8077     char buf[INPUT_SOURCE_BUF_SIZE];
8078     VOIDSTAR closure;
8079 } InputSource;
8080
8081 void
8082 DoInputCallback(closure, source, xid)
8083      caddr_t closure;
8084      int *source;
8085      XtInputId *xid;
8086 {
8087     InputSource *is = (InputSource *) closure;
8088     int count;
8089     int error;
8090     char *p, *q;
8091
8092     if (is->lineByLine) {
8093         count = read(is->fd, is->unused,
8094                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
8095         if (count <= 0) {
8096             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
8097             return;
8098         }
8099         is->unused += count;
8100         p = is->buf;
8101         while (p < is->unused) {
8102             q = memchr(p, '\n', is->unused - p);
8103             if (q == NULL) break;
8104             q++;
8105             (is->func)(is, is->closure, p, q - p, 0);
8106             p = q;
8107         }
8108         q = is->buf;
8109         while (p < is->unused) {
8110           *q++ = *p++;
8111         }
8112         is->unused = q;
8113     } else {
8114 # if HAVE_LIBREADLINE
8115       /* check if input is from stdin, if yes, use gnu-readline */
8116       if( is->fd==fileno(stdin) )
8117         {
8118           /* to clear the line */
8119           printf("\r                                                 \r");
8120           
8121           /* read from stdin */
8122           rl_callback_read_char(); 
8123
8124           /* redisplay the current line, check special case for login and password */
8125           if(sending_ICS_password)
8126             {
8127               int i; char buf[MSG_SIZ];
8128
8129               bzero(buf,MSG_SIZ);
8130
8131               /* blank the password */
8132               count = strlen(rl_line_buffer);
8133               if(count>MSG_SIZ-1)
8134                 {
8135                   printf("PROBLEM with readline\n");
8136                   count=MSG_SIZ;
8137                 }
8138               for(i=0;i<count;i++)
8139                 buf[i]='*';
8140               i++;
8141               buf[i]='\0';
8142               printf("\rpassword: %s",buf);
8143             }
8144           else if (sending_ICS_login)
8145             {
8146               /* show login prompt */
8147               count = strlen(rl_line_buffer);
8148               printf("\rlogin: %s",rl_line_buffer);
8149             }
8150           else
8151             rl_reset_line_state();
8152           
8153           if(readline_complete)
8154             {
8155               /* copy into XBoards buffer */
8156               count = strlen(readline_buffer);
8157               if (count>INPUT_SOURCE_BUF_SIZE-1)
8158                 {
8159                   printf("PROBLEM with readline\n");
8160                   count = INPUT_SOURCE_BUF_SIZE;
8161                 };
8162               strncpy(is->buf,readline_buffer,count);
8163               is->buf[count]='\n';count++; 
8164
8165               /* reset gnu-readline state */
8166               free(readline_buffer);
8167               readline_buffer=NULL;
8168               readline_complete=0;
8169
8170               if (count == -1)
8171                 error = errno;
8172               else
8173                 error = 0;
8174               (is->func)(is, is->closure, is->buf, count, error);
8175
8176               /* are we done with the password? */
8177               if(sending_ICS_password)
8178                 sending_ICS_password=0;
8179               if(sending_ICS_login)
8180                 sending_ICS_login=0;
8181             }
8182         }
8183       else
8184         {
8185           /* input not from stdin, use default method */
8186           count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8187           if (count == -1)
8188             error = errno;
8189           else
8190             error = 0;
8191           (is->func)(is, is->closure, is->buf, count, error);
8192         };
8193 #else /* no readline support */
8194       count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
8195       if (count == -1)
8196         error = errno;
8197       else
8198         error = 0;
8199       (is->func)(is, is->closure, is->buf, count, error);
8200 #endif
8201
8202     }
8203 }
8204
8205 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
8206      ProcRef pr;
8207      int lineByLine;
8208      InputCallback func;
8209      VOIDSTAR closure;
8210 {
8211     InputSource *is;
8212     ChildProc *cp = (ChildProc *) pr;
8213
8214     is = (InputSource *) calloc(1, sizeof(InputSource));
8215     is->lineByLine = lineByLine;
8216     is->func = func;
8217     if (pr == NoProc) {
8218         is->kind = CPReal;
8219         is->fd = fileno(stdin);
8220     } else {
8221         is->kind = cp->kind;
8222         is->fd = cp->fdFrom;
8223     }
8224     if (lineByLine) {
8225         is->unused = is->buf;
8226     }
8227
8228     is->xid = XtAppAddInput(appContext, is->fd,
8229                             (XtPointer) (XtInputReadMask),
8230                             (XtInputCallbackProc) DoInputCallback,
8231                             (XtPointer) is);
8232     is->closure = closure;
8233     return (InputSourceRef) is;
8234 }
8235
8236 void
8237 RemoveInputSource(isr)
8238      InputSourceRef isr;
8239 {
8240     InputSource *is = (InputSource *) isr;
8241
8242     if (is->xid == 0) return;
8243     XtRemoveInput(is->xid);
8244     is->xid = 0;
8245 }
8246
8247 int OutputToProcess(pr, message, count, outError)
8248      ProcRef pr;
8249      char *message;
8250      int count;
8251      int *outError;
8252 {
8253     static int line = 0;
8254     ChildProc *cp = (ChildProc *) pr;
8255     int outCount;
8256
8257
8258     if (pr == NoProc)
8259     {
8260       if (appData.noJoin || !appData.useInternalWrap)
8261         outCount = fwrite(message, 1, count, stdout);
8262       else
8263         {
8264           int width = get_term_width();
8265           int len = wrap(NULL, message, count, width, &line);
8266           char *msg = malloc(len);
8267           int dbgchk;
8268           
8269           if (!msg)
8270             outCount = fwrite(message, 1, count, stdout);
8271           else
8272             {
8273               dbgchk = wrap(msg, message, count, width, &line);
8274               if (dbgchk != len && appData.debugMode)
8275                 fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
8276               outCount = fwrite(msg, 1, dbgchk, stdout);
8277               free(msg);
8278             }
8279         }
8280       
8281 # if HAVE_LIBREADLINE
8282       /* readline support */
8283       if(strlen(rl_line_buffer))
8284          printf("\n>  %s",rl_line_buffer);
8285 #endif
8286
8287     }
8288     else
8289       outCount = write(cp->fdTo, message, count);
8290
8291     if (outCount == -1)
8292       *outError = errno;
8293     else
8294       *outError = 0;
8295
8296     return outCount;
8297 }
8298
8299 /* Output message to process, with "ms" milliseconds of delay
8300    between each character. This is needed when sending the logon
8301    script to ICC, which for some reason doesn't like the
8302    instantaneous send. */
8303 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
8304      ProcRef pr;
8305      char *message;
8306      int count;
8307      int *outError;
8308      long msdelay;
8309 {
8310     ChildProc *cp = (ChildProc *) pr;
8311     int outCount = 0;
8312     int r;
8313
8314     while (count--) {
8315         r = write(cp->fdTo, message++, 1);
8316         if (r == -1) {
8317             *outError = errno;
8318             return outCount;
8319         }
8320         ++outCount;
8321         if (msdelay >= 0)
8322           TimeDelay(msdelay);
8323     }
8324
8325     return outCount;
8326 }
8327
8328 /****   Animation code by Hugh Fisher, DCS, ANU.
8329
8330         Known problem: if a window overlapping the board is
8331         moved away while a piece is being animated underneath,
8332         the newly exposed area won't be updated properly.
8333         I can live with this.
8334
8335         Known problem: if you look carefully at the animation
8336         of pieces in mono mode, they are being drawn as solid
8337         shapes without interior detail while moving. Fixing
8338         this would be a major complication for minimal return.
8339 ****/
8340
8341 /*      Masks for XPM pieces. Black and white pieces can have
8342         different shapes, but in the interest of retaining my
8343         sanity pieces must have the same outline on both light
8344         and dark squares, and all pieces must use the same
8345         background square colors/images.                */
8346
8347 static int xpmDone = 0;
8348
8349 static void
8350 CreateAnimMasks (pieceDepth)
8351      int pieceDepth;
8352 {
8353   ChessSquare   piece;
8354   Pixmap        buf;
8355   GC            bufGC, maskGC;
8356   int           kind, n;
8357   unsigned long plane;
8358   XGCValues     values;
8359
8360   /* Need a bitmap just to get a GC with right depth */
8361   buf = XCreatePixmap(xDisplay, xBoardWindow,
8362                         8, 8, 1);
8363   values.foreground = 1;
8364   values.background = 0;
8365   /* Don't use XtGetGC, not read only */
8366   maskGC = XCreateGC(xDisplay, buf,
8367                     GCForeground | GCBackground, &values);
8368   XFreePixmap(xDisplay, buf);
8369
8370   buf = XCreatePixmap(xDisplay, xBoardWindow,
8371                       squareSize, squareSize, pieceDepth);
8372   values.foreground = XBlackPixel(xDisplay, xScreen);
8373   values.background = XWhitePixel(xDisplay, xScreen);
8374   bufGC = XCreateGC(xDisplay, buf,
8375                     GCForeground | GCBackground, &values);
8376
8377   for (piece = WhitePawn; piece <= BlackKing; piece++) {
8378     /* Begin with empty mask */
8379     if(!xpmDone) // [HGM] pieces: keep using existing
8380     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
8381                                  squareSize, squareSize, 1);
8382     XSetFunction(xDisplay, maskGC, GXclear);
8383     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
8384                    0, 0, squareSize, squareSize);
8385
8386     /* Take a copy of the piece */
8387     if (White(piece))
8388       kind = 0;
8389     else
8390       kind = 2;
8391     XSetFunction(xDisplay, bufGC, GXcopy);
8392     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
8393               buf, bufGC,
8394               0, 0, squareSize, squareSize, 0, 0);
8395
8396     /* XOR the background (light) over the piece */
8397     XSetFunction(xDisplay, bufGC, GXxor);
8398     if (useImageSqs)
8399       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
8400                 0, 0, squareSize, squareSize, 0, 0);
8401     else {
8402       XSetForeground(xDisplay, bufGC, lightSquareColor);
8403       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
8404     }
8405
8406     /* We now have an inverted piece image with the background
8407        erased. Construct mask by just selecting all the non-zero
8408        pixels - no need to reconstruct the original image.      */
8409     XSetFunction(xDisplay, maskGC, GXor);
8410     plane = 1;
8411     /* Might be quicker to download an XImage and create bitmap
8412        data from it rather than this N copies per piece, but it
8413        only takes a fraction of a second and there is a much
8414        longer delay for loading the pieces.             */
8415     for (n = 0; n < pieceDepth; n ++) {
8416       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
8417                  0, 0, squareSize, squareSize,
8418                  0, 0, plane);
8419       plane = plane << 1;
8420     }
8421   }
8422   /* Clean up */
8423   XFreePixmap(xDisplay, buf);
8424   XFreeGC(xDisplay, bufGC);
8425   XFreeGC(xDisplay, maskGC);
8426 }
8427
8428 static void
8429 InitAnimState (anim, info)
8430   AnimState * anim;
8431   XWindowAttributes * info;
8432 {
8433   XtGCMask  mask;
8434   XGCValues values;
8435
8436   /* Each buffer is square size, same depth as window */
8437   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
8438                         squareSize, squareSize, info->depth);
8439   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
8440                         squareSize, squareSize, info->depth);
8441
8442   /* Create a plain GC for blitting */
8443   mask = GCForeground | GCBackground | GCFunction |
8444          GCPlaneMask | GCGraphicsExposures;
8445   values.foreground = XBlackPixel(xDisplay, xScreen);
8446   values.background = XWhitePixel(xDisplay, xScreen);
8447   values.function   = GXcopy;
8448   values.plane_mask = AllPlanes;
8449   values.graphics_exposures = False;
8450   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
8451
8452   /* Piece will be copied from an existing context at
8453      the start of each new animation/drag. */
8454   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
8455
8456   /* Outline will be a read-only copy of an existing */
8457   anim->outlineGC = None;
8458 }
8459
8460 static void
8461 CreateAnimVars ()
8462 {
8463   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
8464   XWindowAttributes info;
8465
8466   if (xpmDone && gameInfo.variant == old) return;
8467   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
8468   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
8469
8470   InitAnimState(&game, &info);
8471   InitAnimState(&player, &info);
8472
8473   /* For XPM pieces, we need bitmaps to use as masks. */
8474   if (useImages)
8475     CreateAnimMasks(info.depth);
8476    xpmDone = 1;
8477 }
8478
8479 #ifndef HAVE_USLEEP
8480
8481 static Boolean frameWaiting;
8482
8483 static RETSIGTYPE FrameAlarm (sig)
8484      int sig;
8485 {
8486   frameWaiting = False;
8487   /* In case System-V style signals.  Needed?? */
8488   signal(SIGALRM, FrameAlarm);
8489 }
8490
8491 static void
8492 FrameDelay (time)
8493      int time;
8494 {
8495   struct itimerval delay;
8496
8497   XSync(xDisplay, False);
8498
8499   if (time > 0) {
8500     frameWaiting = True;
8501     signal(SIGALRM, FrameAlarm);
8502     delay.it_interval.tv_sec =
8503       delay.it_value.tv_sec = time / 1000;
8504     delay.it_interval.tv_usec =
8505       delay.it_value.tv_usec = (time % 1000) * 1000;
8506     setitimer(ITIMER_REAL, &delay, NULL);
8507     while (frameWaiting) pause();
8508     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
8509     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
8510     setitimer(ITIMER_REAL, &delay, NULL);
8511   }
8512 }
8513
8514 #else
8515
8516 static void
8517 FrameDelay (time)
8518      int time;
8519 {
8520   XSync(xDisplay, False);
8521   if (time > 0)
8522     usleep(time * 1000);
8523 }
8524
8525 #endif
8526
8527 /*      Convert board position to corner of screen rect and color       */
8528
8529 static void
8530 ScreenSquare(column, row, pt, color)
8531      int column; int row; XPoint * pt; int * color;
8532 {
8533   if (flipView) {
8534     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
8535     pt->y = lineGap + row * (squareSize + lineGap);
8536   } else {
8537     pt->x = lineGap + column * (squareSize + lineGap);
8538     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
8539   }
8540   *color = SquareColor(row, column);
8541 }
8542
8543 /*      Convert window coords to square                 */
8544
8545 static void
8546 BoardSquare(x, y, column, row)
8547      int x; int y; int * column; int * row;
8548 {
8549   *column = EventToSquare(x, BOARD_WIDTH);
8550   if (flipView && *column >= 0)
8551     *column = BOARD_WIDTH - 1 - *column;
8552   *row = EventToSquare(y, BOARD_HEIGHT);
8553   if (!flipView && *row >= 0)
8554     *row = BOARD_HEIGHT - 1 - *row;
8555 }
8556
8557 /*   Utilities  */
8558
8559 #undef Max  /* just in case */
8560 #undef Min
8561 #define Max(a, b) ((a) > (b) ? (a) : (b))
8562 #define Min(a, b) ((a) < (b) ? (a) : (b))
8563
8564 static void
8565 SetRect(rect, x, y, width, height)
8566      XRectangle * rect; int x; int y; int width; int height;
8567 {
8568   rect->x = x;
8569   rect->y = y;
8570   rect->width  = width;
8571   rect->height = height;
8572 }
8573
8574 /*      Test if two frames overlap. If they do, return
8575         intersection rect within old and location of
8576         that rect within new. */
8577
8578 static Boolean
8579 Intersect(old, new, size, area, pt)
8580      XPoint * old; XPoint * new;
8581      int size; XRectangle * area; XPoint * pt;
8582 {
8583   if (old->x > new->x + size || new->x > old->x + size ||
8584       old->y > new->y + size || new->y > old->y + size) {
8585     return False;
8586   } else {
8587     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8588             size - abs(old->x - new->x), size - abs(old->y - new->y));
8589     pt->x = Max(old->x - new->x, 0);
8590     pt->y = Max(old->y - new->y, 0);
8591     return True;
8592   }
8593 }
8594
8595 /*      For two overlapping frames, return the rect(s)
8596         in the old that do not intersect with the new.   */
8597
8598 static void
8599 CalcUpdateRects(old, new, size, update, nUpdates)
8600      XPoint * old; XPoint * new; int size;
8601      XRectangle update[]; int * nUpdates;
8602 {
8603   int        count;
8604
8605   /* If old = new (shouldn't happen) then nothing to draw */
8606   if (old->x == new->x && old->y == new->y) {
8607     *nUpdates = 0;
8608     return;
8609   }
8610   /* Work out what bits overlap. Since we know the rects
8611      are the same size we don't need a full intersect calc. */
8612   count = 0;
8613   /* Top or bottom edge? */
8614   if (new->y > old->y) {
8615     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8616     count ++;
8617   } else if (old->y > new->y) {
8618     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8619                               size, old->y - new->y);
8620     count ++;
8621   }
8622   /* Left or right edge - don't overlap any update calculated above. */
8623   if (new->x > old->x) {
8624     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8625                               new->x - old->x, size - abs(new->y - old->y));
8626     count ++;
8627   } else if (old->x > new->x) {
8628     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8629                               old->x - new->x, size - abs(new->y - old->y));
8630     count ++;
8631   }
8632   /* Done */
8633   *nUpdates = count;
8634 }
8635
8636 /*      Generate a series of frame coords from start->mid->finish.
8637         The movement rate doubles until the half way point is
8638         reached, then halves back down to the final destination,
8639         which gives a nice slow in/out effect. The algorithmn
8640         may seem to generate too many intermediates for short
8641         moves, but remember that the purpose is to attract the
8642         viewers attention to the piece about to be moved and
8643         then to where it ends up. Too few frames would be less
8644         noticeable.                                             */
8645
8646 static void
8647 Tween(start, mid, finish, factor, frames, nFrames)
8648      XPoint * start; XPoint * mid;
8649      XPoint * finish; int factor;
8650      XPoint frames[]; int * nFrames;
8651 {
8652   int fraction, n, count;
8653
8654   count = 0;
8655
8656   /* Slow in, stepping 1/16th, then 1/8th, ... */
8657   fraction = 1;
8658   for (n = 0; n < factor; n++)
8659     fraction *= 2;
8660   for (n = 0; n < factor; n++) {
8661     frames[count].x = start->x + (mid->x - start->x) / fraction;
8662     frames[count].y = start->y + (mid->y - start->y) / fraction;
8663     count ++;
8664     fraction = fraction / 2;
8665   }
8666
8667   /* Midpoint */
8668   frames[count] = *mid;
8669   count ++;
8670
8671   /* Slow out, stepping 1/2, then 1/4, ... */
8672   fraction = 2;
8673   for (n = 0; n < factor; n++) {
8674     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8675     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8676     count ++;
8677     fraction = fraction * 2;
8678   }
8679   *nFrames = count;
8680 }
8681
8682 /*      Draw a piece on the screen without disturbing what's there      */
8683
8684 static void
8685 SelectGCMask(piece, clip, outline, mask)
8686      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8687 {
8688   GC source;
8689
8690   /* Bitmap for piece being moved. */
8691   if (appData.monoMode) {
8692       *mask = *pieceToSolid(piece);
8693   } else if (useImages) {
8694 #if HAVE_LIBXPM
8695       *mask = xpmMask[piece];
8696 #else
8697       *mask = ximMaskPm[piece];
8698 #endif
8699   } else {
8700       *mask = *pieceToSolid(piece);
8701   }
8702
8703   /* GC for piece being moved. Square color doesn't matter, but
8704      since it gets modified we make a copy of the original. */
8705   if (White(piece)) {
8706     if (appData.monoMode)
8707       source = bwPieceGC;
8708     else
8709       source = wlPieceGC;
8710   } else {
8711     if (appData.monoMode)
8712       source = wbPieceGC;
8713     else
8714       source = blPieceGC;
8715   }
8716   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8717
8718   /* Outline only used in mono mode and is not modified */
8719   if (White(piece))
8720     *outline = bwPieceGC;
8721   else
8722     *outline = wbPieceGC;
8723 }
8724
8725 static void
8726 OverlayPiece(piece, clip, outline,  dest)
8727      ChessSquare piece; GC clip; GC outline; Drawable dest;
8728 {
8729   int   kind;
8730
8731   if (!useImages) {
8732     /* Draw solid rectangle which will be clipped to shape of piece */
8733     XFillRectangle(xDisplay, dest, clip,
8734                    0, 0, squareSize, squareSize);
8735     if (appData.monoMode)
8736       /* Also draw outline in contrasting color for black
8737          on black / white on white cases                */
8738       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8739                  0, 0, squareSize, squareSize, 0, 0, 1);
8740   } else {
8741     /* Copy the piece */
8742     if (White(piece))
8743       kind = 0;
8744     else
8745       kind = 2;
8746     if(appData.upsideDown && flipView) kind ^= 2;
8747     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
8748               dest, clip,
8749               0, 0, squareSize, squareSize,
8750               0, 0);
8751   }
8752 }
8753
8754 /* Animate the movement of a single piece */
8755
8756 static void
8757 BeginAnimation(anim, piece, startColor, start)
8758      AnimState *anim;
8759      ChessSquare piece;
8760      int startColor;
8761      XPoint * start;
8762 {
8763   Pixmap mask;
8764
8765   /* The old buffer is initialised with the start square (empty) */
8766   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
8767   anim->prevFrame = *start;
8768
8769   /* The piece will be drawn using its own bitmap as a matte    */
8770   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8771   XSetClipMask(xDisplay, anim->pieceGC, mask);
8772 }
8773
8774 static void
8775 AnimationFrame(anim, frame, piece)
8776      AnimState *anim;
8777      XPoint *frame;
8778      ChessSquare piece;
8779 {
8780   XRectangle updates[4];
8781   XRectangle overlap;
8782   XPoint     pt;
8783   int        count, i;
8784
8785   /* Save what we are about to draw into the new buffer */
8786   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8787             frame->x, frame->y, squareSize, squareSize,
8788             0, 0);
8789
8790   /* Erase bits of the previous frame */
8791   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8792     /* Where the new frame overlapped the previous,
8793        the contents in newBuf are wrong. */
8794     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8795               overlap.x, overlap.y,
8796               overlap.width, overlap.height,
8797               pt.x, pt.y);
8798     /* Repaint the areas in the old that don't overlap new */
8799     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8800     for (i = 0; i < count; i++)
8801       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8802                 updates[i].x - anim->prevFrame.x,
8803                 updates[i].y - anim->prevFrame.y,
8804                 updates[i].width, updates[i].height,
8805                 updates[i].x, updates[i].y);
8806   } else {
8807     /* Easy when no overlap */
8808     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8809                   0, 0, squareSize, squareSize,
8810                   anim->prevFrame.x, anim->prevFrame.y);
8811   }
8812
8813   /* Save this frame for next time round */
8814   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8815                 0, 0, squareSize, squareSize,
8816                 0, 0);
8817   anim->prevFrame = *frame;
8818
8819   /* Draw piece over original screen contents, not current,
8820      and copy entire rect. Wipes out overlapping piece images. */
8821   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8822   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8823                 0, 0, squareSize, squareSize,
8824                 frame->x, frame->y);
8825 }
8826
8827 static void
8828 EndAnimation (anim, finish)
8829      AnimState *anim;
8830      XPoint *finish;
8831 {
8832   XRectangle updates[4];
8833   XRectangle overlap;
8834   XPoint     pt;
8835   int        count, i;
8836
8837   /* The main code will redraw the final square, so we
8838      only need to erase the bits that don't overlap.    */
8839   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8840     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8841     for (i = 0; i < count; i++)
8842       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8843                 updates[i].x - anim->prevFrame.x,
8844                 updates[i].y - anim->prevFrame.y,
8845                 updates[i].width, updates[i].height,
8846                 updates[i].x, updates[i].y);
8847   } else {
8848     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8849                 0, 0, squareSize, squareSize,
8850                 anim->prevFrame.x, anim->prevFrame.y);
8851   }
8852 }
8853
8854 static void
8855 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8856      AnimState *anim;
8857      ChessSquare piece; int startColor;
8858      XPoint * start; XPoint * finish;
8859      XPoint frames[]; int nFrames;
8860 {
8861   int n;
8862
8863   BeginAnimation(anim, piece, startColor, start);
8864   for (n = 0; n < nFrames; n++) {
8865     AnimationFrame(anim, &(frames[n]), piece);
8866     FrameDelay(appData.animSpeed);
8867   }
8868   EndAnimation(anim, finish);
8869 }
8870
8871 /* Main control logic for deciding what to animate and how */
8872
8873 void
8874 AnimateMove(board, fromX, fromY, toX, toY)
8875      Board board;
8876      int fromX;
8877      int fromY;
8878      int toX;
8879      int toY;
8880 {
8881   ChessSquare piece;
8882   int hop;
8883   XPoint      start, finish, mid;
8884   XPoint      frames[kFactor * 2 + 1];
8885   int         nFrames, startColor, endColor;
8886
8887   /* Are we animating? */
8888   if (!appData.animate || appData.blindfold)
8889     return;
8890
8891   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
8892      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
8893         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
8894
8895   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8896   piece = board[fromY][fromX];
8897   if (piece >= EmptySquare) return;
8898
8899 #if DONT_HOP
8900   hop = FALSE;
8901 #else
8902   hop = (piece == WhiteKnight || piece == BlackKnight);
8903 #endif
8904
8905   if (appData.debugMode) {
8906       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
8907                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
8908              piece, fromX, fromY, toX, toY);  }
8909
8910   ScreenSquare(fromX, fromY, &start, &startColor);
8911   ScreenSquare(toX, toY, &finish, &endColor);
8912
8913   if (hop) {
8914     /* Knight: make diagonal movement then straight */
8915     if (abs(toY - fromY) < abs(toX - fromX)) {
8916        mid.x = start.x + (finish.x - start.x) / 2;
8917        mid.y = finish.y;
8918      } else {
8919        mid.x = finish.x;
8920        mid.y = start.y + (finish.y - start.y) / 2;
8921      }
8922   } else {
8923     mid.x = start.x + (finish.x - start.x) / 2;
8924     mid.y = start.y + (finish.y - start.y) / 2;
8925   }
8926
8927   /* Don't use as many frames for very short moves */
8928   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8929     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8930   else
8931     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8932   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8933
8934   /* Be sure end square is redrawn */
8935   damage[0][toY][toX] = True;
8936 }
8937
8938 void
8939 DragPieceBegin(x, y)
8940      int x; int y;
8941 {
8942     int  boardX, boardY, color;
8943     XPoint corner;
8944
8945     /* Are we animating? */
8946     if (!appData.animateDragging || appData.blindfold)
8947       return;
8948
8949     /* Figure out which square we start in and the
8950        mouse position relative to top left corner. */
8951     BoardSquare(x, y, &boardX, &boardY);
8952     player.startBoardX = boardX;
8953     player.startBoardY = boardY;
8954     ScreenSquare(boardX, boardY, &corner, &color);
8955     player.startSquare  = corner;
8956     player.startColor   = color;
8957     /* As soon as we start dragging, the piece will jump slightly to
8958        be centered over the mouse pointer. */
8959     player.mouseDelta.x = squareSize/2;
8960     player.mouseDelta.y = squareSize/2;
8961     /* Initialise animation */
8962     player.dragPiece = PieceForSquare(boardX, boardY);
8963     /* Sanity check */
8964     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8965         player.dragActive = True;
8966         BeginAnimation(&player, player.dragPiece, color, &corner);
8967         /* Mark this square as needing to be redrawn. Note that
8968            we don't remove the piece though, since logically (ie
8969            as seen by opponent) the move hasn't been made yet. */
8970            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
8971               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
8972            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
8973                      corner.x, corner.y, squareSize, squareSize,
8974                      0, 0); // [HGM] zh: unstack in stead of grab
8975         damage[0][boardY][boardX] = True;
8976     } else {
8977         player.dragActive = False;
8978     }
8979 }
8980
8981 static void
8982 DragPieceMove(x, y)
8983      int x; int y;
8984 {
8985     XPoint corner;
8986
8987     /* Are we animating? */
8988     if (!appData.animateDragging || appData.blindfold)
8989       return;
8990
8991     /* Sanity check */
8992     if (! player.dragActive)
8993       return;
8994     /* Move piece, maintaining same relative position
8995        of mouse within square    */
8996     corner.x = x - player.mouseDelta.x;
8997     corner.y = y - player.mouseDelta.y;
8998     AnimationFrame(&player, &corner, player.dragPiece);
8999 #if HIGHDRAG*0
9000     if (appData.highlightDragging) {
9001         int boardX, boardY;
9002         BoardSquare(x, y, &boardX, &boardY);
9003         SetHighlights(fromX, fromY, boardX, boardY);
9004     }
9005 #endif
9006 }
9007
9008 void
9009 DragPieceEnd(x, y)
9010      int x; int y;
9011 {
9012     int boardX, boardY, color;
9013     XPoint corner;
9014
9015     /* Are we animating? */
9016     if (!appData.animateDragging || appData.blindfold)
9017       return;
9018
9019     /* Sanity check */
9020     if (! player.dragActive)
9021       return;
9022     /* Last frame in sequence is square piece is
9023        placed on, which may not match mouse exactly. */
9024     BoardSquare(x, y, &boardX, &boardY);
9025     ScreenSquare(boardX, boardY, &corner, &color);
9026     EndAnimation(&player, &corner);
9027
9028     /* Be sure end square is redrawn */
9029     damage[0][boardY][boardX] = True;
9030
9031     /* This prevents weird things happening with fast successive
9032        clicks which on my Sun at least can cause motion events
9033        without corresponding press/release. */
9034     player.dragActive = False;
9035 }
9036
9037 /* Handle expose event while piece being dragged */
9038
9039 static void
9040 DrawDragPiece ()
9041 {
9042   if (!player.dragActive || appData.blindfold)
9043     return;
9044
9045   /* What we're doing: logically, the move hasn't been made yet,
9046      so the piece is still in it's original square. But visually
9047      it's being dragged around the board. So we erase the square
9048      that the piece is on and draw it at the last known drag point. */
9049   BlankSquare(player.startSquare.x, player.startSquare.y,
9050                 player.startColor, EmptySquare, xBoardWindow);
9051   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
9052   damage[0][player.startBoardY][player.startBoardX] = TRUE;
9053 }
9054
9055 #include <sys/ioctl.h>
9056 int get_term_width()
9057 {
9058     int fd, default_width;
9059
9060     fd = STDIN_FILENO;
9061     default_width = 79; // this is FICS default anyway...
9062
9063 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
9064     struct ttysize win;
9065     if (!ioctl(fd, TIOCGSIZE, &win))
9066         default_width = win.ts_cols;
9067 #elif defined(TIOCGWINSZ)
9068     struct winsize win;
9069     if (!ioctl(fd, TIOCGWINSZ, &win))
9070         default_width = win.ws_col;
9071 #endif
9072     return default_width;
9073 }
9074
9075 void
9076 update_ics_width()
9077 {
9078   static int old_width = 0;
9079   int new_width = get_term_width();
9080
9081   if (old_width != new_width)
9082     ics_printf("set width %d\n", new_width);
9083   old_width = new_width;
9084 }
9085
9086 void NotifyFrontendLogin()
9087 {
9088     update_ics_width();
9089 }
9090
9091 # if HAVE_LIBREADLINE
9092 static void 
9093 ReadlineCompleteHandler(char* ptr)
9094 {
9095   /* make gnu-readline keep the history */
9096   readline_buffer = ptr;
9097   readline_complete = 1;
9098   
9099   if (ptr && *ptr && !sending_ICS_password && !sending_ICS_login)
9100     add_history(ptr);
9101
9102   return;
9103 }
9104 #endif