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