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