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