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