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