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