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