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