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