9a073d0451f1e0fcf501902a98e47cbf0d633cad
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #define HIGHDRAG 1
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <pwd.h>
63
64 #if !OMIT_SOCKETS
65 # if HAVE_SYS_SOCKET_H
66 #  include <sys/socket.h>
67 #  include <netinet/in.h>
68 #  include <netdb.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 #  if HAVE_LAN_SOCKET_H
71 #   include <lan/socket.h>
72 #   include <lan/in.h>
73 #   include <lan/netdb.h>
74 #  else /* not HAVE_LAN_SOCKET_H */
75 #   define OMIT_SOCKETS 1
76 #  endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
79
80 #if STDC_HEADERS
81 # include <stdlib.h>
82 # include <string.h>
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
85 # if HAVE_STRING_H
86 #  include <string.h>
87 # else /* not HAVE_STRING_H */
88 #  include <strings.h>
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
91
92 #if HAVE_SYS_FCNTL_H
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
95 # if HAVE_FCNTL_H
96 #  include <fcntl.h>
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
99
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
103
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
106 # include <time.h>
107 #else
108 # if HAVE_SYS_TIME_H
109 #  include <sys/time.h>
110 # else
111 #  include <time.h>
112 # endif
113 #endif
114
115 #if HAVE_UNISTD_H
116 # include <unistd.h>
117 #endif
118
119 #if HAVE_SYS_WAIT_H
120 # include <sys/wait.h>
121 #endif
122
123 #if HAVE_DIRENT_H
124 # include <dirent.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
127 #else
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
130 # if HAVE_SYS_NDIR_H
131 #  include <sys/ndir.h>
132 #  define HAVE_DIR_STRUCT
133 # endif
134 # if HAVE_SYS_DIR_H
135 #  include <sys/dir.h>
136 #  define HAVE_DIR_STRUCT
137 # endif
138 # if HAVE_NDIR_H
139 #  include <ndir.h>
140 #  define HAVE_DIR_STRUCT
141 # endif
142 #endif
143
144 #include <X11/Intrinsic.h>
145 #include <X11/StringDefs.h>
146 #include <X11/Shell.h>
147 #include <X11/cursorfont.h>
148 #include <X11/Xatom.h>
149 #include <X11/Xmu/Atoms.h>
150 #if USE_XAW3D
151 #include <X11/Xaw3d/Dialog.h>
152 #include <X11/Xaw3d/Form.h>
153 #include <X11/Xaw3d/List.h>
154 #include <X11/Xaw3d/Label.h>
155 #include <X11/Xaw3d/SimpleMenu.h>
156 #include <X11/Xaw3d/SmeBSB.h>
157 #include <X11/Xaw3d/SmeLine.h>
158 #include <X11/Xaw3d/Box.h>
159 #include <X11/Xaw3d/MenuButton.h>
160 #include <X11/Xaw3d/Text.h>
161 #include <X11/Xaw3d/AsciiText.h>
162 #else
163 #include <X11/Xaw/Dialog.h>
164 #include <X11/Xaw/Form.h>
165 #include <X11/Xaw/List.h>
166 #include <X11/Xaw/Label.h>
167 #include <X11/Xaw/SimpleMenu.h>
168 #include <X11/Xaw/SmeBSB.h>
169 #include <X11/Xaw/SmeLine.h>
170 #include <X11/Xaw/Box.h>
171 #include <X11/Xaw/MenuButton.h>
172 #include <X11/Xaw/Text.h>
173 #include <X11/Xaw/AsciiText.h>
174 #endif
175
176 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
177 #include "common.h"
178
179 #if HAVE_LIBXPM
180 #include <X11/xpm.h>
181 #include "pixmaps/pixmaps.h"
182 #define IMAGE_EXT "xpm"
183 #else
184 #define IMAGE_EXT "xim"
185 #include "bitmaps/bitmaps.h"
186 #endif
187
188 #include "bitmaps/icon_white.bm"
189 #include "bitmaps/icon_black.bm"
190 #include "bitmaps/checkmark.bm"
191
192 #include "frontend.h"
193 #include "backend.h"
194 #include "backendz.h"
195 #include "moves.h"
196 #include "xboard.h"
197 #include "childio.h"
198 #include "xgamelist.h"
199 #include "xhistory.h"
200 #include "xedittags.h"
201 #include "gettext.h"
202
203 // must be moved to xengineoutput.h
204
205 void EngineOutputProc P((Widget w, XEvent *event,
206                          String *prms, Cardinal *nprms));
207 void EvalGraphProc P((Widget w, XEvent *event,
208                       String *prms, Cardinal *nprms));
209
210
211 #ifdef __EMX__
212 #ifndef HAVE_USLEEP
213 #define HAVE_USLEEP
214 #endif
215 #define usleep(t)   _sleep2(((t)+500)/1000)
216 #endif
217
218 #ifdef ENABLE_NLS
219 # define  _(s) gettext (s)
220 # define N_(s) gettext_noop (s)
221 #else
222 # define  _(s) (s)
223 # define N_(s)  s
224 #endif
225
226 typedef struct {
227     String string;
228     XtActionProc proc;
229 } MenuItem;
230
231 typedef struct {
232     String name;
233     MenuItem *mi;
234 } Menu;
235
236 int main P((int argc, char **argv));
237 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
238                 char *init_path, char *mode, int (*show_entry)(), char **name_return));
239 RETSIGTYPE CmailSigHandler P((int sig));
240 RETSIGTYPE IntSigHandler P((int sig));
241 RETSIGTYPE TermSizeSigHandler P((int sig));
242 void CreateGCs P((void));
243 void CreateXIMPieces P((void));
244 void CreateXPMPieces P((void));
245 void CreatePieces P((void));
246 void CreatePieceMenus P((void));
247 Widget CreateMenuBar P((Menu *mb));
248 Widget CreateButtonBar P ((MenuItem *mi));
249 char *FindFont P((char *pattern, int targetPxlSize));
250 void PieceMenuPopup P((Widget w, XEvent *event,
251                        String *params, Cardinal *num_params));
252 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
253 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
254 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
255                    u_int wreq, u_int hreq));
256 void CreateGrid P((void));
257 int EventToSquare P((int x, int limit));
258 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
259 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
260 void HandleUserMove P((Widget w, XEvent *event,
261                      String *prms, Cardinal *nprms));
262 void AnimateUserMove P((Widget w, XEvent * event,
263                      String * params, Cardinal * nParams));
264 void HandlePV P((Widget w, XEvent * event,
265                      String * params, Cardinal * nParams));
266 void SelectPV P((Widget w, XEvent * event,
267                      String * params, Cardinal * nParams));
268 void StopPV P((Widget w, XEvent * event,
269                      String * params, Cardinal * nParams));
270 void WhiteClock P((Widget w, XEvent *event,
271                    String *prms, Cardinal *nprms));
272 void BlackClock P((Widget w, XEvent *event,
273                    String *prms, Cardinal *nprms));
274 void DrawPositionProc P((Widget w, XEvent *event,
275                      String *prms, Cardinal *nprms));
276 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
277                      Board board));
278 void CommentClick P((Widget w, XEvent * event,
279                    String * params, Cardinal * nParams));
280 void CommentPopUp P((char *title, char *label));
281 void CommentPopDown P((void));
282 void CommentCallback P((Widget w, XtPointer client_data,
283                         XtPointer call_data));
284 void ICSInputBoxPopUp P((void));
285 void ICSInputBoxPopDown P((void));
286 void FileNamePopUp P((char *label, char *def,
287                       FileProc proc, char *openMode));
288 void FileNamePopDown P((void));
289 void FileNameCallback P((Widget w, XtPointer client_data,
290                          XtPointer call_data));
291 void FileNameAction P((Widget w, XEvent *event,
292                        String *prms, Cardinal *nprms));
293 void AskQuestionReplyAction P((Widget w, XEvent *event,
294                           String *prms, Cardinal *nprms));
295 void AskQuestionProc P((Widget w, XEvent *event,
296                           String *prms, Cardinal *nprms));
297 void AskQuestionPopDown P((void));
298 void PromotionPopDown P((void));
299 void PromotionCallback P((Widget w, XtPointer client_data,
300                           XtPointer call_data));
301 void EditCommentPopDown P((void));
302 void EditCommentCallback P((Widget w, XtPointer client_data,
303                             XtPointer call_data));
304 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
305 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
308                          Cardinal *nprms));
309 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
310                          Cardinal *nprms));
311 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
312                        Cardinal *nprms));
313 void LoadPositionProc P((Widget w, XEvent *event,
314                          String *prms, Cardinal *nprms));
315 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
316                          Cardinal *nprms));
317 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
318                          Cardinal *nprms));
319 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
320                        Cardinal *nprms));
321 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
322                          Cardinal *nprms));
323 void PastePositionProc P((Widget w, XEvent *event, String *prms,
324                           Cardinal *nprms));
325 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
326 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
327 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
328 void SavePositionProc P((Widget w, XEvent *event,
329                          String *prms, Cardinal *nprms));
330 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
331 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
332                             Cardinal *nprms));
333 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
334 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
336                          Cardinal *nprms));
337 void MachineWhiteProc P((Widget w, XEvent *event,
338                          String *prms, Cardinal *nprms));
339 void AnalyzeModeProc P((Widget w, XEvent *event,
340                          String *prms, Cardinal *nprms));
341 void AnalyzeFileProc P((Widget w, XEvent *event,
342                          String *prms, Cardinal *nprms));
343 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
344                         Cardinal *nprms));
345 void IcsClientProc P((Widget w, XEvent *event, String *prms,
346                       Cardinal *nprms));
347 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
348 void EditPositionProc P((Widget w, XEvent *event,
349                          String *prms, Cardinal *nprms));
350 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
351 void EditCommentProc P((Widget w, XEvent *event,
352                         String *prms, Cardinal *nprms));
353 void IcsInputBoxProc P((Widget w, XEvent *event,
354                         String *prms, Cardinal *nprms));
355 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
356 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
360 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
361 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void StopObservingProc P((Widget w, XEvent *event, String *prms,
370                           Cardinal *nprms));
371 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
372                           Cardinal *nprms));
373 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
377 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
378 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
379 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
380 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
381                          Cardinal *nprms));
382 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
383                         Cardinal *nprms));
384 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
386                         Cardinal *nprms));
387 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
388                          Cardinal *nprms));
389 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
390                          Cardinal *nprms));
391 void AutocommProc P((Widget w, XEvent *event, String *prms,
392                      Cardinal *nprms));
393 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 void AutobsProc P((Widget w, XEvent *event, String *prms,
396                         Cardinal *nprms));
397 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
398 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
399 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
400 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
401                        Cardinal *nprms));
402 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
403 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
404                         Cardinal *nprms));
405 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
406                               Cardinal *nprms));
407 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
408                               Cardinal *nprms));
409 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
411 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
412                          Cardinal *nprms));
413 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
414                          Cardinal *nprms));
415 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
416                            Cardinal *nprms));
417 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
418                         Cardinal *nprms));
419 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
420                              Cardinal *nprms));
421 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
422 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
423 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
424                        Cardinal *nprms));
425 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
426                          Cardinal *nprms));
427 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
428                          Cardinal *nprms));
429 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
430                           Cardinal *nprms));
431 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
432 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
433 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
434 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
435 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
436 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
437 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
438 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void DisplayMove P((int moveNumber));
443 void DisplayTitle P((char *title));
444 void ICSInitScript P((void));
445 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
446 void ErrorPopUp P((char *title, char *text, int modal));
447 void ErrorPopDown P((void));
448 static char *ExpandPathName P((char *path));
449 static void CreateAnimVars P((void));
450 static void DragPieceMove P((int x, int y));
451 static void DrawDragPiece P((void));
452 char *ModeToWidgetName P((GameMode mode));
453 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
454 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
455 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
456 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
457 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
458 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
459 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
460 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void GameListOptionsPopDown P(());
462 void ShufflePopDown P(());
463 void EnginePopDown P(());
464 void UciPopDown P(());
465 void TimeControlPopDown P(());
466 void NewVariantPopDown P(());
467 void SettingsPopDown P(());
468 void update_ics_width P(());
469 int get_term_width P(());
470 int CopyMemoProc P(());
471 /*
472 * XBoard depends on Xt R4 or higher
473 */
474 int xtVersion = XtSpecificationRelease;
475
476 int xScreen;
477 Display *xDisplay;
478 Window xBoardWindow;
479 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
480   jailSquareColor, highlightSquareColor, premoveHighlightColor;
481 Pixel lowTimeWarningColor;
482 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
483   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
484   wjPieceGC, bjPieceGC, prelineGC, countGC;
485 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
486 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
487   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
488   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
489   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
490   ICSInputShell, fileNameShell, askQuestionShell;
491 Widget historyShell, evalGraphShell, gameListShell;
492 int hOffset; // [HGM] dual
493 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
494 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
495 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
496 Font clockFontID, coordFontID, countFontID;
497 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
498 XtAppContext appContext;
499 char *layoutName;
500 char *oldICSInteractionTitle;
501
502 FileProc fileProc;
503 char *fileOpenMode;
504 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
505
506 Position commentX = -1, commentY = -1;
507 Dimension commentW, commentH;
508 typedef unsigned int BoardSize;
509 BoardSize boardSize;
510 Boolean chessProgram;
511
512 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
513 int squareSize, smallLayout = 0, tinyLayout = 0,
514   marginW, marginH, // [HGM] for run-time resizing
515   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
516   ICSInputBoxUp = False, askQuestionUp = False,
517   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
518   editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
519 Pixel timerForegroundPixel, timerBackgroundPixel;
520 Pixel buttonForegroundPixel, buttonBackgroundPixel;
521 char *chessDir, *programName, *programVersion,
522   *gameCopyFilename, *gamePasteFilename;
523 Boolean alwaysOnTop = False;
524 Boolean saveSettingsOnExit;
525 char *settingsFileName;
526 char *icsTextMenuString;
527 char *icsNames;
528 char *firstChessProgramNames;
529 char *secondChessProgramNames;
530
531 WindowPlacement wpMain;
532 WindowPlacement wpConsole;
533 WindowPlacement wpComment;
534 WindowPlacement wpMoveHistory;
535 WindowPlacement wpEvalGraph;
536 WindowPlacement wpEngineOutput;
537 WindowPlacement wpGameList;
538 WindowPlacement wpTags;
539
540 #define SOLID 0
541 #define OUTLINE 1
542 Pixmap pieceBitmap[2][(int)BlackPawn];
543 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
544 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
545 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
546 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
547 int useImages, useImageSqs;
548 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
549 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
550 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
551 XImage *ximLightSquare, *ximDarkSquare;
552 XImage *xim_Cross;
553
554 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
555 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
556
557 #define White(piece) ((int)(piece) < (int)BlackPawn)
558
559 /* Variables for doing smooth animation. This whole thing
560    would be much easier if the board was double-buffered,
561    but that would require a fairly major rewrite.       */
562
563 typedef struct {
564         Pixmap  saveBuf;
565         Pixmap  newBuf;
566         GC      blitGC, pieceGC, outlineGC;
567         XPoint  startSquare, prevFrame, mouseDelta;
568         int     startColor;
569         int     dragPiece;
570         Boolean dragActive;
571         int     startBoardX, startBoardY;
572     } AnimState;
573
574 /* There can be two pieces being animated at once: a player
575    can begin dragging a piece before the remote opponent has moved. */
576
577 static AnimState game, player;
578
579 /* Bitmaps for use as masks when drawing XPM pieces.
580    Need one for each black and white piece.             */
581 static Pixmap xpmMask[BlackKing + 1];
582
583 /* This magic number is the number of intermediate frames used
584    in each half of the animation. For short moves it's reduced
585    by 1. The total number of frames will be factor * 2 + 1.  */
586 #define kFactor    4
587
588 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
589
590 MenuItem fileMenu[] = {
591     {N_("New Game"), ResetProc},
592     {N_("New Shuffle Game ..."), ShuffleMenuProc},
593     {N_("New Variant ..."), NewVariantProc},      // [HGM] variant: not functional yet
594     {"----", NothingProc},
595     {N_("Load Game"), LoadGameProc},
596     {N_("Load Next Game"), LoadNextGameProc},
597     {N_("Load Previous Game"), LoadPrevGameProc},
598     {N_("Reload Same Game"), ReloadGameProc},
599     {N_("Save Game"), SaveGameProc},
600     {"----", NothingProc},
601     {N_("Copy Game"), CopyGameProc},
602     {N_("Paste Game"), PasteGameProc},
603     {"----", NothingProc},
604     {N_("Load Position"), LoadPositionProc},
605     {N_("Load Next Position"), LoadNextPositionProc},
606     {N_("Load Previous Position"), LoadPrevPositionProc},
607     {N_("Reload Same Position"), ReloadPositionProc},
608     {N_("Save Position"), SavePositionProc},
609     {"----", NothingProc},
610     {N_("Copy Position"), CopyPositionProc},
611     {N_("Paste Position"), PastePositionProc},
612     {"----", NothingProc},
613     {N_("Mail Move"), MailMoveProc},
614     {N_("Reload CMail Message"), ReloadCmailMsgProc},
615     {"----", NothingProc},
616     {N_("Exit"), QuitProc},
617     {NULL, NULL}
618 };
619
620 MenuItem modeMenu[] = {
621     {N_("Machine White"), MachineWhiteProc},
622     {N_("Machine Black"), MachineBlackProc},
623     {N_("Two Machines"), TwoMachinesProc},
624     {N_("Analysis Mode"), AnalyzeModeProc},
625     {N_("Analyze File"), AnalyzeFileProc },
626     {N_("ICS Client"), IcsClientProc},
627     {N_("Edit Game"), EditGameProc},
628     {N_("Edit Position"), EditPositionProc},
629     {N_("Training"), TrainingProc},
630     {"----", NothingProc},
631     {N_("Show Engine Output"), EngineOutputProc},
632     {N_("Show Evaluation Graph"), EvalGraphProc},
633     {N_("Show Game List"), ShowGameListProc},
634     {N_("Show Move History"), HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
635     {"----", NothingProc},
636     {N_("Edit Tags"), EditTagsProc},
637     {N_("Edit Comment"), EditCommentProc},
638     {N_("ICS Input Box"), IcsInputBoxProc},
639     {N_("Pause"), PauseProc},
640     {NULL, NULL}
641 };
642
643 MenuItem actionMenu[] = {
644     {N_("Accept"), AcceptProc},
645     {N_("Decline"), DeclineProc},
646     {N_("Rematch"), RematchProc},
647     {"----", NothingProc},
648     {N_("Call Flag"), CallFlagProc},
649     {N_("Draw"), DrawProc},
650     {N_("Adjourn"), AdjournProc},
651     {N_("Abort"), AbortProc},
652     {N_("Resign"), ResignProc},
653     {"----", NothingProc},
654     {N_("Stop Observing"), StopObservingProc},
655     {N_("Stop Examining"), StopExaminingProc},
656     {N_("Upload to Examine"), UploadProc},
657     {"----", NothingProc},
658     {N_("Adjudicate to White"), AdjuWhiteProc},
659     {N_("Adjudicate to Black"), AdjuBlackProc},
660     {N_("Adjudicate Draw"), AdjuDrawProc},
661     {NULL, NULL}
662 };
663
664 MenuItem stepMenu[] = {
665     {N_("Backward"), BackwardProc},
666     {N_("Forward"), ForwardProc},
667     {N_("Back to Start"), ToStartProc},
668     {N_("Forward to End"), ToEndProc},
669     {N_("Revert"), RevertProc},
670     {N_("Annotate"), AnnotateProc},
671     {N_("Truncate Game"), TruncateGameProc},
672     {"----", NothingProc},
673     {N_("Move Now"), MoveNowProc},
674     {N_("Retract Move"), RetractMoveProc},
675     {NULL, NULL}
676 };
677
678 MenuItem optionsMenu[] = {
679     {N_("Flip View"), FlipViewProc},
680     {"----", NothingProc},
681     {N_("Adjudications ..."), EngineMenuProc},
682     {N_("General Settings ..."), UciMenuProc},
683     {N_("Engine #1 Settings ..."), FirstSettingsProc},
684     {N_("Engine #2 Settings ..."), SecondSettingsProc},
685     {N_("Time Control ..."), TimeControlProc},
686     {N_("Game List ..."), GameListOptionsPopUp},
687     {"----", NothingProc},
688     {N_("Always Queen"), AlwaysQueenProc},
689     {N_("Animate Dragging"), AnimateDraggingProc},
690     {N_("Animate Moving"), AnimateMovingProc},
691     {N_("Auto Comment"), AutocommProc},
692     {N_("Auto Flag"), AutoflagProc},
693     {N_("Auto Flip View"), AutoflipProc},
694     {N_("Auto Observe"), AutobsProc},
695     {N_("Auto Raise Board"), AutoraiseProc},
696     {N_("Auto Save"), AutosaveProc},
697     {N_("Blindfold"), BlindfoldProc},
698     {N_("Flash Moves"), FlashMovesProc},
699     {N_("Get Move List"), GetMoveListProc},
700 #if HIGHDRAG
701     {N_("Highlight Dragging"), HighlightDraggingProc},
702 #endif
703     {N_("Highlight Last Move"), HighlightLastMoveProc},
704     {N_("Move Sound"), MoveSoundProc},
705     {N_("ICS Alarm"), IcsAlarmProc},
706     {N_("Old Save Style"), OldSaveStyleProc},
707     {N_("Periodic Updates"), PeriodicUpdatesProc},
708     {N_("Ponder Next Move"), PonderNextMoveProc},
709     {N_("Popup Exit Message"), PopupExitMessageProc},
710     {N_("Popup Move Errors"), PopupMoveErrorsProc},
711     {N_("Premove"), PremoveProc},
712     {N_("Quiet Play"), QuietPlayProc},
713     {N_("Show Coords"), ShowCoordsProc},
714     {N_("Hide Thinking"), HideThinkingProc},
715     {N_("Test Legality"), TestLegalityProc},
716     {"----", NothingProc},
717     {N_("Save Settings Now"), SaveSettingsProc},
718     {N_("Save Settings on Exit"), SaveOnExitProc},
719     {NULL, NULL}
720 };
721
722 MenuItem helpMenu[] = {
723     {N_("Info XBoard"), InfoProc},
724     {N_("Man XBoard"), ManProc},
725     {"----", NothingProc},
726     {N_("Hint"), HintProc},
727     {N_("Book"), BookProc},
728     {"----", NothingProc},
729     {N_("About XBoard"), AboutProc},
730     {NULL, NULL}
731 };
732
733 Menu menuBar[] = {
734     {N_("File"), fileMenu},
735     {N_("Mode"), modeMenu},
736     {N_("Action"), actionMenu},
737     {N_("Step"), stepMenu},
738     {N_("Options"), optionsMenu},
739     {N_("Help"), helpMenu},
740     {NULL, NULL}
741 };
742
743 #define PAUSE_BUTTON N_("P")
744 MenuItem buttonBar[] = {
745     {"<<", ToStartProc},
746     {"<", BackwardProc},
747     {PAUSE_BUTTON, PauseProc},
748     {">", ForwardProc},
749     {">>", ToEndProc},
750     {NULL, NULL}
751 };
752
753 #define PIECE_MENU_SIZE 18
754 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
755     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
756       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
757       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
758       N_("Empty square"), N_("Clear board") },
759     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
760       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
761       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
762       N_("Empty square"), N_("Clear board") }
763 };
764 /* must be in same order as PieceMenuStrings! */
765 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
766     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
767         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
768         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
769         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
770     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
771         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
772         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
773         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
774 };
775
776 #define DROP_MENU_SIZE 6
777 String dropMenuStrings[DROP_MENU_SIZE] = {
778     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
779   };
780 /* must be in same order as PieceMenuStrings! */
781 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
782     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
783     WhiteRook, WhiteQueen
784 };
785
786 typedef struct {
787     char piece;
788     char* widget;
789 } DropMenuEnables;
790
791 DropMenuEnables dmEnables[] = {
792     { 'P', "Pawn" },
793     { 'N', "Knight" },
794     { 'B', "Bishop" },
795     { 'R', "Rook" },
796     { 'Q', "Queen" }
797 };
798
799 Arg shellArgs[] = {
800     { XtNwidth, 0 },
801     { XtNheight, 0 },
802     { XtNminWidth, 0 },
803     { XtNminHeight, 0 },
804     { XtNmaxWidth, 0 },
805     { XtNmaxHeight, 0 }
806 };
807
808 Arg layoutArgs[] = {
809     { XtNborderWidth, 0 },
810     { XtNdefaultDistance, 0 },
811 };
812
813 Arg formArgs[] = {
814     { XtNborderWidth, 0 },
815     { XtNresizable, (XtArgVal) True },
816 };
817
818 Arg boardArgs[] = {
819     { XtNborderWidth, 0 },
820     { XtNwidth, 0 },
821     { XtNheight, 0 }
822 };
823
824 Arg titleArgs[] = {
825     { XtNjustify, (XtArgVal) XtJustifyRight },
826     { XtNlabel, (XtArgVal) "..." },
827     { XtNresizable, (XtArgVal) True },
828     { XtNresize, (XtArgVal) False }
829 };
830
831 Arg messageArgs[] = {
832     { XtNjustify, (XtArgVal) XtJustifyLeft },
833     { XtNlabel, (XtArgVal) "..." },
834     { XtNresizable, (XtArgVal) True },
835     { XtNresize, (XtArgVal) False }
836 };
837
838 Arg timerArgs[] = {
839     { XtNborderWidth, 0 },
840     { XtNjustify, (XtArgVal) XtJustifyLeft }
841 };
842
843 XtResource clientResources[] = {
844     { "flashCount", "flashCount", XtRInt, sizeof(int),
845         XtOffset(AppDataPtr, flashCount), XtRImmediate,
846         (XtPointer) FLASH_COUNT  },
847 };
848
849 XrmOptionDescRec shellOptions[] = {
850     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
851     { "-flash", "flashCount", XrmoptionNoArg, "3" },
852     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
853 };
854
855 XtActionsRec boardActions[] = {
856     { "DrawPosition", DrawPositionProc },
857     { "HandleUserMove", HandleUserMove },
858     { "AnimateUserMove", AnimateUserMove },
859     { "HandlePV", HandlePV },
860     { "SelectPV", SelectPV },
861     { "StopPV", StopPV },
862     { "FileNameAction", FileNameAction },
863     { "AskQuestionProc", AskQuestionProc },
864     { "AskQuestionReplyAction", AskQuestionReplyAction },
865     { "PieceMenuPopup", PieceMenuPopup },
866     { "WhiteClock", WhiteClock },
867     { "BlackClock", BlackClock },
868     { "Iconify", Iconify },
869     { "ResetProc", ResetProc },
870     { "NewVariantProc", NewVariantProc },
871     { "LoadGameProc", LoadGameProc },
872     { "LoadNextGameProc", LoadNextGameProc },
873     { "LoadPrevGameProc", LoadPrevGameProc },
874     { "LoadSelectedProc", LoadSelectedProc },
875     { "SetFilterProc", SetFilterProc },
876     { "ReloadGameProc", ReloadGameProc },
877     { "LoadPositionProc", LoadPositionProc },
878     { "LoadNextPositionProc", LoadNextPositionProc },
879     { "LoadPrevPositionProc", LoadPrevPositionProc },
880     { "ReloadPositionProc", ReloadPositionProc },
881     { "CopyPositionProc", CopyPositionProc },
882     { "PastePositionProc", PastePositionProc },
883     { "CopyGameProc", CopyGameProc },
884     { "PasteGameProc", PasteGameProc },
885     { "SaveGameProc", SaveGameProc },
886     { "SavePositionProc", SavePositionProc },
887     { "MailMoveProc", MailMoveProc },
888     { "ReloadCmailMsgProc", ReloadCmailMsgProc },
889     { "QuitProc", QuitProc },
890     { "MachineWhiteProc", MachineWhiteProc },
891     { "MachineBlackProc", MachineBlackProc },
892     { "AnalysisModeProc", AnalyzeModeProc },
893     { "AnalyzeFileProc", AnalyzeFileProc },
894     { "TwoMachinesProc", TwoMachinesProc },
895     { "IcsClientProc", IcsClientProc },
896     { "EditGameProc", EditGameProc },
897     { "EditPositionProc", EditPositionProc },
898     { "TrainingProc", EditPositionProc },
899     { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
900     { "EvalGraphProc", EvalGraphProc},       // [HGM] Winboard_x avaluation graph window
901     { "ShowGameListProc", ShowGameListProc },
902     { "ShowMoveListProc", HistoryShowProc},
903     { "EditTagsProc", EditCommentProc },
904     { "EditCommentProc", EditCommentProc },
905     { "IcsAlarmProc", IcsAlarmProc },
906     { "IcsInputBoxProc", IcsInputBoxProc },
907     { "PauseProc", PauseProc },
908     { "AcceptProc", AcceptProc },
909     { "DeclineProc", DeclineProc },
910     { "RematchProc", RematchProc },
911     { "CallFlagProc", CallFlagProc },
912     { "DrawProc", DrawProc },
913     { "AdjournProc", AdjournProc },
914     { "AbortProc", AbortProc },
915     { "ResignProc", ResignProc },
916     { "AdjuWhiteProc", AdjuWhiteProc },
917     { "AdjuBlackProc", AdjuBlackProc },
918     { "AdjuDrawProc", AdjuDrawProc },
919     { "EnterKeyProc", EnterKeyProc },
920     { "UpKeyProc", UpKeyProc },
921     { "DownKeyProc", DownKeyProc },
922     { "StopObservingProc", StopObservingProc },
923     { "StopExaminingProc", StopExaminingProc },
924     { "UploadProc", UploadProc },
925     { "BackwardProc", BackwardProc },
926     { "ForwardProc", ForwardProc },
927     { "ToStartProc", ToStartProc },
928     { "ToEndProc", ToEndProc },
929     { "RevertProc", RevertProc },
930     { "AnnotateProc", AnnotateProc },
931     { "TruncateGameProc", TruncateGameProc },
932     { "MoveNowProc", MoveNowProc },
933     { "RetractMoveProc", RetractMoveProc },
934     { "EngineMenuProc", (XtActionProc) EngineMenuProc },
935     { "UciMenuProc", (XtActionProc) UciMenuProc },
936     { "TimeControlProc", (XtActionProc) TimeControlProc },
937     { "AlwaysQueenProc", AlwaysQueenProc },
938     { "AnimateDraggingProc", AnimateDraggingProc },
939     { "AnimateMovingProc", AnimateMovingProc },
940     { "AutoflagProc", AutoflagProc },
941     { "AutoflipProc", AutoflipProc },
942     { "AutobsProc", AutobsProc },
943     { "AutoraiseProc", AutoraiseProc },
944     { "AutosaveProc", AutosaveProc },
945     { "BlindfoldProc", BlindfoldProc },
946     { "FlashMovesProc", FlashMovesProc },
947     { "FlipViewProc", FlipViewProc },
948     { "GetMoveListProc", GetMoveListProc },
949 #if HIGHDRAG
950     { "HighlightDraggingProc", HighlightDraggingProc },
951 #endif
952     { "HighlightLastMoveProc", HighlightLastMoveProc },
953     { "IcsAlarmProc", IcsAlarmProc },
954     { "MoveSoundProc", MoveSoundProc },
955     { "OldSaveStyleProc", OldSaveStyleProc },
956     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
957     { "PonderNextMoveProc", PonderNextMoveProc },
958     { "PopupExitMessageProc", PopupExitMessageProc },
959     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
960     { "PremoveProc", PremoveProc },
961     { "QuietPlayProc", QuietPlayProc },
962     { "ShowCoordsProc", ShowCoordsProc },
963     { "ShowThinkingProc", ShowThinkingProc },
964     { "HideThinkingProc", HideThinkingProc },
965     { "TestLegalityProc", TestLegalityProc },
966     { "SaveSettingsProc", SaveSettingsProc },
967     { "SaveOnExitProc", SaveOnExitProc },
968     { "InfoProc", InfoProc },
969     { "ManProc", ManProc },
970     { "HintProc", HintProc },
971     { "BookProc", BookProc },
972     { "AboutGameProc", AboutGameProc },
973     { "AboutProc", AboutProc },
974     { "DebugProc", DebugProc },
975     { "NothingProc", NothingProc },
976     { "CommentClick", (XtActionProc) CommentClick },
977     { "CommentPopDown", (XtActionProc) CommentPopDown },
978     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
979     { "TagsPopDown", (XtActionProc) TagsPopDown },
980     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
981     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
982     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
983     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
984     { "GameListPopDown", (XtActionProc) GameListPopDown },
985     { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
986     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
987     { "HistoryPopDown", (XtActionProc) HistoryPopDown },
988     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
989     { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
990     { "ShufflePopDown", (XtActionProc) ShufflePopDown },
991     { "EnginePopDown", (XtActionProc) EnginePopDown },
992     { "UciPopDown", (XtActionProc) UciPopDown },
993     { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
994     { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
995     { "SettingsPopDown", (XtActionProc) SettingsPopDown },
996     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
997 };
998
999 char globalTranslations[] =
1000   ":<Key>F9: ResignProc() \n \
1001    :Ctrl<Key>n: ResetProc() \n \
1002    :Meta<Key>V: NewVariantProc() \n \
1003    :Ctrl<Key>o: LoadGameProc() \n \
1004    :Meta<Key>Next: LoadNextGameProc() \n \
1005    :Meta<Key>Prior: LoadPrevGameProc() \n \
1006    :Ctrl<Key>s: SaveGameProc() \n \
1007    :Ctrl<Key>c: CopyGameProc() \n \
1008    :Ctrl<Key>v: PasteGameProc() \n \
1009    :Ctrl<Key>O: LoadPositionProc() \n \
1010    :Shift Meta<Key>Next: LoadNextPositionProc() \n \
1011    :Shift Meta<Key>Prior: LoadPrevPositionProc() \n \
1012    :Ctrl<Key>S: SavePositionProc() \n \
1013    :Ctrl<Key>C: CopyPositionProc() \n \
1014    :Ctrl<Key>V: PastePositionProc() \n \
1015    :Ctrl<Key>q: QuitProc() \n \
1016    :Ctrl<Key>w: MachineWhiteProc() \n \
1017    :Ctrl<Key>b: MachineBlackProc() \n \
1018    :Ctrl<Key>t: TwoMachinesProc() \n \
1019    :Ctrl<Key>a: AnalysisModeProc() \n \
1020    :Ctrl<Key>f: AnalyzeFileProc() \n \
1021    :Ctrl<Key>e: EditGameProc() \n \
1022    :Ctrl<Key>E: EditPositionProc() \n \
1023    :Meta<Key>O: EngineOutputProc() \n \
1024    :Meta<Key>E: EvalGraphProc() \n \
1025    :Meta<Key>G: ShowGameListProc() \n \
1026    :Meta<Key>H: ShowMoveListProc() \n \
1027    :<Key>Pause: PauseProc() \n \
1028    :<Key>F3: AcceptProc() \n \
1029    :<Key>F4: DeclineProc() \n \
1030    :<Key>F12: RematchProc() \n \
1031    :<Key>F5: CallFlagProc() \n \
1032    :<Key>F6: DrawProc() \n \
1033    :<Key>F7: AdjournProc() \n \
1034    :<Key>F8: AbortProc() \n \
1035    :<Key>F10: StopObservingProc() \n \
1036    :<Key>F11: StopExaminingProc() \n \
1037    :Meta Ctrl<Key>F12: DebugProc() \n \
1038    :Meta<Key>End: ToEndProc() \n \
1039    :Meta<Key>Right: ForwardProc() \n \
1040    :Meta<Key>Home: ToStartProc() \n \
1041    :Meta<Key>Left: BackwardProc() \n \
1042    :Ctrl<Key>m: MoveNowProc() \n \
1043    :Ctrl<Key>x: RetractMoveProc() \n \
1044    :Meta<Key>J: EngineMenuProc() \n \
1045    :Meta<Key>U: UciMenuProc() \n \
1046    :Meta<Key>T: TimeControlProc() \n \
1047    :Ctrl<Key>Q: AlwaysQueenProc() \n \
1048    :Ctrl<Key>F: AutoflagProc() \n \
1049    :Ctrl<Key>A: AnimateMovingProc() \n \
1050    :Ctrl<Key>P: PonderNextMoveProc() \n \
1051    :Ctrl<Key>L: TestLegalityProc() \n \
1052    :Ctrl<Key>H: HideThinkingProc() \n \
1053    :<Key>-: Iconify() \n \
1054    :<Key>F1: ManProc() \n \
1055    :<Key>F2: FlipViewProc() \n \
1056    <KeyDown>.: BackwardProc() \n \
1057    <KeyUp>.: ForwardProc() \n \
1058    Shift<Key>1: AskQuestionProc(\"Direct command\",\
1059                                 \"Send to chess program:\",,1) \n \
1060    Shift<Key>2: AskQuestionProc(\"Direct command\",\
1061                                 \"Send to second chess program:\",,2) \n";
1062
1063 char boardTranslations[] =
1064    "<Btn1Down>: HandleUserMove() \n \
1065    <Btn1Up>: HandleUserMove() \n \
1066    <Btn1Motion>: AnimateUserMove() \n \
1067    <Btn3Motion>: HandlePV() \n \
1068    <Btn3Up>: PieceMenuPopup(menuB) \n \
1069    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1070                  PieceMenuPopup(menuB) \n \
1071    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1072                  PieceMenuPopup(menuW) \n \
1073    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1074                  PieceMenuPopup(menuW) \n \
1075    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1076                  PieceMenuPopup(menuB) \n";
1077
1078 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1079 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1080
1081 char ICSInputTranslations[] =
1082     "<Key>Up: UpKeyProc() \n "
1083     "<Key>Down: DownKeyProc() \n "
1084     "<Key>Return: EnterKeyProc() \n";
1085
1086 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1087 //             as the widget is destroyed before the up-click can call extend-end
1088 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1089
1090 String xboardResources[] = {
1091     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1092     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1093     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1094     NULL
1095   };
1096
1097
1098 /* Max possible square size */
1099 #define MAXSQSIZE 256
1100
1101 static int xpm_avail[MAXSQSIZE];
1102
1103 #ifdef HAVE_DIR_STRUCT
1104
1105 /* Extract piece size from filename */
1106 static int
1107 xpm_getsize(name, len, ext)
1108      char *name;
1109      int len;
1110      char *ext;
1111 {
1112     char *p, *d;
1113     char buf[10];
1114
1115     if (len < 4)
1116       return 0;
1117
1118     if ((p=strchr(name, '.')) == NULL ||
1119         StrCaseCmp(p+1, ext) != 0)
1120       return 0;
1121
1122     p = name + 3;
1123     d = buf;
1124
1125     while (*p && isdigit(*p))
1126       *(d++) = *(p++);
1127
1128     *d = 0;
1129     return atoi(buf);
1130 }
1131
1132 /* Setup xpm_avail */
1133 static int
1134 xpm_getavail(dirname, ext)
1135      char *dirname;
1136      char *ext;
1137 {
1138     DIR *dir;
1139     struct dirent *ent;
1140     int  i;
1141
1142     for (i=0; i<MAXSQSIZE; ++i)
1143       xpm_avail[i] = 0;
1144
1145     if (appData.debugMode)
1146       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1147
1148     dir = opendir(dirname);
1149     if (!dir)
1150       {
1151           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1152                   programName, dirname);
1153           exit(1);
1154       }
1155
1156     while ((ent=readdir(dir)) != NULL) {
1157         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1158         if (i > 0 && i < MAXSQSIZE)
1159           xpm_avail[i] = 1;
1160     }
1161
1162     closedir(dir);
1163
1164     return 0;
1165 }
1166
1167 void
1168 xpm_print_avail(fp, ext)
1169      FILE *fp;
1170      char *ext;
1171 {
1172     int i;
1173
1174     fprintf(fp, _("Available `%s' sizes:\n"), ext);
1175     for (i=1; i<MAXSQSIZE; ++i) {
1176         if (xpm_avail[i])
1177           printf("%d\n", i);
1178     }
1179 }
1180
1181 /* Return XPM piecesize closest to size */
1182 int
1183 xpm_closest_to(dirname, size, ext)
1184      char *dirname;
1185      int size;
1186      char *ext;
1187 {
1188     int i;
1189     int sm_diff = MAXSQSIZE;
1190     int sm_index = 0;
1191     int diff;
1192
1193     xpm_getavail(dirname, ext);
1194
1195     if (appData.debugMode)
1196       xpm_print_avail(stderr, ext);
1197
1198     for (i=1; i<MAXSQSIZE; ++i) {
1199         if (xpm_avail[i]) {
1200             diff = size - i;
1201             diff = (diff<0) ? -diff : diff;
1202             if (diff < sm_diff) {
1203                 sm_diff = diff;
1204                 sm_index = i;
1205             }
1206         }
1207     }
1208
1209     if (!sm_index) {
1210         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1211         exit(1);
1212     }
1213
1214     return sm_index;
1215 }
1216 #else   /* !HAVE_DIR_STRUCT */
1217 /* If we are on a system without a DIR struct, we can't
1218    read the directory, so we can't collect a list of
1219    filenames, etc., so we can't do any size-fitting. */
1220 int
1221 xpm_closest_to(dirname, size, ext)
1222      char *dirname;
1223      int size;
1224      char *ext;
1225 {
1226     fprintf(stderr, _("\
1227 Warning: No DIR structure found on this system --\n\
1228          Unable to autosize for XPM/XIM pieces.\n\
1229    Please report this error to frankm@hiwaay.net.\n\
1230    Include system type & operating system in message.\n"));
1231     return size;
1232 }
1233 #endif /* HAVE_DIR_STRUCT */
1234
1235 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1236                              "magenta", "cyan", "white" };
1237 typedef struct {
1238     int attr, bg, fg;
1239 } TextColors;
1240 TextColors textColors[(int)NColorClasses];
1241
1242 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1243 static int
1244 parse_color(str, which)
1245      char *str;
1246      int which;
1247 {
1248     char *p, buf[100], *d;
1249     int i;
1250
1251     if (strlen(str) > 99)       /* watch bounds on buf */
1252       return -1;
1253
1254     p = str;
1255     d = buf;
1256     for (i=0; i<which; ++i) {
1257         p = strchr(p, ',');
1258         if (!p)
1259           return -1;
1260         ++p;
1261     }
1262
1263     /* Could be looking at something like:
1264        black, , 1
1265        .. in which case we want to stop on a comma also */
1266     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1267       ++p;
1268
1269     if (*p == ',') {
1270         return -1;              /* Use default for empty field */
1271     }
1272
1273     if (which == 2 || isdigit(*p))
1274       return atoi(p);
1275
1276     while (*p && isalpha(*p))
1277       *(d++) = *(p++);
1278
1279     *d = 0;
1280
1281     for (i=0; i<8; ++i) {
1282         if (!StrCaseCmp(buf, cnames[i]))
1283           return which? (i+40) : (i+30);
1284     }
1285     if (!StrCaseCmp(buf, "default")) return -1;
1286
1287     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1288     return -2;
1289 }
1290
1291 static int
1292 parse_cpair(cc, str)
1293      ColorClass cc;
1294      char *str;
1295 {
1296     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1297         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1298                 programName, str);
1299         return -1;
1300     }
1301
1302     /* bg and attr are optional */
1303     textColors[(int)cc].bg = parse_color(str, 1);
1304     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1305         textColors[(int)cc].attr = 0;
1306     }
1307     return 0;
1308 }
1309
1310
1311 /* Arrange to catch delete-window events */
1312 Atom wm_delete_window;
1313 void
1314 CatchDeleteWindow(Widget w, String procname)
1315 {
1316   char buf[MSG_SIZ];
1317   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1318   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1319   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1320 }
1321
1322 void
1323 BoardToTop()
1324 {
1325   Arg args[16];
1326   XtSetArg(args[0], XtNiconic, False);
1327   XtSetValues(shellWidget, args, 1);
1328
1329   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1330 }
1331
1332 //---------------------------------------------------------------------------------------------------------
1333 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1334 #define XBOARD True
1335 #define JAWS_ARGS
1336 #define CW_USEDEFAULT (1<<31)
1337 #define ICS_TEXT_MENU_SIZE 90
1338 #define DEBUG_FILE "xboard.debug"
1339 #define SetCurrentDirectory chdir
1340 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1341 #define OPTCHAR "-"
1342 #define SEPCHAR " "
1343
1344 // these two must some day move to frontend.h, when they are implemented
1345 Boolean GameListIsUp();
1346
1347 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1348 #include "args.h"
1349
1350 // front-end part of option handling
1351
1352 // [HGM] This platform-dependent table provides the location for storing the color info
1353 extern char *crWhite, * crBlack;
1354
1355 void *
1356 colorVariable[] = {
1357   &appData.whitePieceColor,
1358   &appData.blackPieceColor,
1359   &appData.lightSquareColor,
1360   &appData.darkSquareColor,
1361   &appData.highlightSquareColor,
1362   &appData.premoveHighlightColor,
1363   &appData.lowTimeWarningColor,
1364   NULL,
1365   NULL,
1366   NULL,
1367   NULL,
1368   NULL,
1369   &crWhite,
1370   &crBlack,
1371   NULL
1372 };
1373
1374 // [HGM] font: keep a font for each square size, even non-stndard ones
1375 #define NUM_SIZES 18
1376 #define MAX_SIZE 130
1377 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1378 char *fontTable[NUM_FONTS][MAX_SIZE];
1379
1380 void
1381 ParseFont(char *name, int number)
1382 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1383   int size;
1384   if(sscanf(name, "size%d:", &size)) {
1385     // [HGM] font: font is meant for specific boardSize (likely from settings file);
1386     //       defer processing it until we know if it matches our board size
1387     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1388         fontTable[number][size] = strdup(strchr(name, ':')+1);
1389         fontValid[number][size] = True;
1390     }
1391     return;
1392   }
1393   switch(number) {
1394     case 0: // CLOCK_FONT
1395         appData.clockFont = strdup(name);
1396       break;
1397     case 1: // MESSAGE_FONT
1398         appData.font = strdup(name);
1399       break;
1400     case 2: // COORD_FONT
1401         appData.coordFont = strdup(name);
1402       break;
1403     default:
1404       return;
1405   }
1406   fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1407 }
1408
1409 void
1410 SetFontDefaults()
1411 { // only 2 fonts currently
1412   appData.clockFont = CLOCK_FONT_NAME;
1413   appData.coordFont = COORD_FONT_NAME;
1414   appData.font  =   DEFAULT_FONT_NAME;
1415 }
1416
1417 void
1418 CreateFonts()
1419 { // no-op, until we identify the code for this already in XBoard and move it here
1420 }
1421
1422 void
1423 ParseColor(int n, char *name)
1424 { // in XBoard, just copy the color-name string
1425   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1426 }
1427
1428 void
1429 ParseTextAttribs(ColorClass cc, char *s)
1430 {
1431     (&appData.colorShout)[cc] = strdup(s);
1432 }
1433
1434 void
1435 ParseBoardSize(void *addr, char *name)
1436 {
1437     appData.boardSize = strdup(name);
1438 }
1439
1440 void
1441 LoadAllSounds()
1442 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1443 }
1444
1445 void
1446 SetCommPortDefaults()
1447 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1448 }
1449
1450 // [HGM] args: these three cases taken out to stay in front-end
1451 void
1452 SaveFontArg(FILE *f, ArgDescriptor *ad)
1453 {
1454   char *name;
1455   int i, n = (int)ad->argLoc;
1456   switch(n) {
1457     case 0: // CLOCK_FONT
1458         name = appData.clockFont;
1459       break;
1460     case 1: // MESSAGE_FONT
1461         name = appData.font;
1462       break;
1463     case 2: // COORD_FONT
1464         name = appData.coordFont;
1465       break;
1466     default:
1467       return;
1468   }
1469   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1470     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1471         fontTable[n][squareSize] = strdup(name);
1472         fontValid[n][squareSize] = True;
1473         break;
1474   }
1475   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1476     fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1477 }
1478
1479 void
1480 ExportSounds()
1481 { // nothing to do, as the sounds are at all times represented by their text-string names already
1482 }
1483
1484 void
1485 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1486 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1487         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1488 }
1489
1490 void
1491 SaveColor(FILE *f, ArgDescriptor *ad)
1492 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1493         if(colorVariable[(int)ad->argLoc])
1494         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1495 }
1496
1497 void
1498 SaveBoardSize(FILE *f, char *name, void *addr)
1499 { // wrapper to shield back-end from BoardSize & sizeInfo
1500   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1501 }
1502
1503 void
1504 ParseCommPortSettings(char *s)
1505 { // no such option in XBoard (yet)
1506 }
1507
1508 extern Widget engineOutputShell;
1509 extern Widget tagsShell, editTagsShell;
1510 void
1511 GetActualPlacement(Widget wg, WindowPlacement *wp)
1512 {
1513   Arg args[16];
1514   Dimension w, h;
1515   Position x, y;
1516   int i;
1517
1518   if(!wg) return;
1519
1520     i = 0;
1521     XtSetArg(args[i], XtNx, &x); i++;
1522     XtSetArg(args[i], XtNy, &y); i++;
1523     XtSetArg(args[i], XtNwidth, &w); i++;
1524     XtSetArg(args[i], XtNheight, &h); i++;
1525     XtGetValues(wg, args, i);
1526     wp->x = x - 4;
1527     wp->y = y - 23;
1528     wp->height = h;
1529     wp->width = w;
1530 }
1531
1532 void
1533 GetWindowCoords()
1534 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1535   // In XBoard this will have to wait until awareness of window parameters is implemented
1536   GetActualPlacement(shellWidget, &wpMain);
1537   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1538   if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1539   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1540   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1541   if(commentShell) GetActualPlacement(commentShell, &wpComment);
1542   else             GetActualPlacement(editShell,    &wpComment);
1543   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1544   else      GetActualPlacement(editTagsShell, &wpTags);
1545 }
1546
1547 void
1548 PrintCommPortSettings(FILE *f, char *name)
1549 { // This option does not exist in XBoard
1550 }
1551
1552 int
1553 MySearchPath(char *installDir, char *name, char *fullname)
1554 { // just append installDir and name. Perhaps ExpandPath should be used here?
1555   name = ExpandPathName(name);
1556   if(name && name[0] == '/')
1557     safeStrCpy(fullname, name, MSG_SIZ );
1558   else {
1559     sprintf(fullname, "%s%c%s", installDir, '/', name);
1560   }
1561   return 1;
1562 }
1563
1564 int
1565 MyGetFullPathName(char *name, char *fullname)
1566 { // should use ExpandPath?
1567   name = ExpandPathName(name);
1568   safeStrCpy(fullname, name, MSG_SIZ );
1569   return 1;
1570 }
1571
1572 void
1573 EnsureOnScreen(int *x, int *y, int minX, int minY)
1574 {
1575   return;
1576 }
1577
1578 int
1579 MainWindowUp()
1580 { // [HGM] args: allows testing if main window is realized from back-end
1581   return xBoardWindow != 0;
1582 }
1583
1584 void
1585 PopUpStartupDialog()
1586 {  // start menu not implemented in XBoard
1587 }
1588
1589 char *
1590 ConvertToLine(int argc, char **argv)
1591 {
1592   static char line[128*1024], buf[1024];
1593   int i;
1594
1595   line[0] = NULLCHAR;
1596   for(i=1; i<argc; i++)
1597     {
1598       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1599           && argv[i][0] != '{' )
1600         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1601       else
1602         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1603       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1604     }
1605
1606   line[strlen(line)-1] = NULLCHAR;
1607   return line;
1608 }
1609
1610 //--------------------------------------------------------------------------------------------
1611
1612 extern Boolean twoBoards, partnerUp;
1613
1614 #ifdef IDSIZES
1615   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1616 #else
1617 #define BoardSize int
1618 void InitDrawingSizes(BoardSize boardSize, int flags)
1619 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1620     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1621     Arg args[16];
1622     XtGeometryResult gres;
1623     int i;
1624
1625     if(!formWidget) return;
1626
1627     /*
1628      * Enable shell resizing.
1629      */
1630     shellArgs[0].value = (XtArgVal) &w;
1631     shellArgs[1].value = (XtArgVal) &h;
1632     XtGetValues(shellWidget, shellArgs, 2);
1633
1634     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1635     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1636     XtSetValues(shellWidget, &shellArgs[2], 4);
1637
1638     XtSetArg(args[0], XtNdefaultDistance, &sep);
1639     XtGetValues(formWidget, args, 1);
1640
1641     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1642     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1643     CreateGrid();
1644     hOffset = boardWidth + 10;
1645     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1646         secondSegments[i] = gridSegments[i];
1647         secondSegments[i].x1 += hOffset;
1648         secondSegments[i].x2 += hOffset;
1649     }
1650
1651     XtSetArg(args[0], XtNwidth, boardWidth);
1652     XtSetArg(args[1], XtNheight, boardHeight);
1653     XtSetValues(boardWidget, args, 2);
1654
1655     timerWidth = (boardWidth - sep) / 2;
1656     XtSetArg(args[0], XtNwidth, timerWidth);
1657     XtSetValues(whiteTimerWidget, args, 1);
1658     XtSetValues(blackTimerWidget, args, 1);
1659
1660     XawFormDoLayout(formWidget, False);
1661
1662     if (appData.titleInWindow) {
1663         i = 0;
1664         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1665         XtSetArg(args[i], XtNheight, &h);  i++;
1666         XtGetValues(titleWidget, args, i);
1667         if (smallLayout) {
1668             w = boardWidth - 2*bor;
1669         } else {
1670             XtSetArg(args[0], XtNwidth, &w);
1671             XtGetValues(menuBarWidget, args, 1);
1672             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1673         }
1674
1675         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1676         if (gres != XtGeometryYes && appData.debugMode) {
1677             fprintf(stderr,
1678                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1679                     programName, gres, w, h, wr, hr);
1680         }
1681     }
1682
1683     XawFormDoLayout(formWidget, True);
1684
1685     /*
1686      * Inhibit shell resizing.
1687      */
1688     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1689     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1690     shellArgs[4].value = shellArgs[2].value = w;
1691     shellArgs[5].value = shellArgs[3].value = h;
1692     XtSetValues(shellWidget, &shellArgs[0], 6);
1693
1694     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1695     // (only for xpm)
1696     if(useImages) {
1697       for(i=0; i<4; i++) {
1698         int p;
1699         for(p=0; p<=(int)WhiteKing; p++)
1700            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1701         if(gameInfo.variant == VariantShogi) {
1702            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1703            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1704            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1705            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1706            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1707         }
1708 #ifdef GOTHIC
1709         if(gameInfo.variant == VariantGothic) {
1710            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1711         }
1712 #endif
1713 #if !HAVE_LIBXPM
1714         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1715         for(p=0; p<=(int)WhiteKing; p++)
1716            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1717         if(gameInfo.variant == VariantShogi) {
1718            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1719            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1720            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1721            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1722            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1723         }
1724 #ifdef GOTHIC
1725         if(gameInfo.variant == VariantGothic) {
1726            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1727         }
1728 #endif
1729 #endif
1730       }
1731     } else {
1732       for(i=0; i<2; i++) {
1733         int p;
1734         for(p=0; p<=(int)WhiteKing; p++)
1735            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1736         if(gameInfo.variant == VariantShogi) {
1737            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1738            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1739            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1740            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1741            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1742         }
1743 #ifdef GOTHIC
1744         if(gameInfo.variant == VariantGothic) {
1745            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1746         }
1747 #endif
1748       }
1749     }
1750 #if HAVE_LIBXPM
1751     CreateAnimVars();
1752 #endif
1753 }
1754 #endif
1755
1756 int
1757 main(argc, argv)
1758      int argc;
1759      char **argv;
1760 {
1761     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1762     XSetWindowAttributes window_attributes;
1763     Arg args[16];
1764     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1765     XrmValue vFrom, vTo;
1766     XtGeometryResult gres;
1767     char *p;
1768     XrmDatabase xdb;
1769     int forceMono = False;
1770
1771     srandom(time(0)); // [HGM] book: make random truly random
1772
1773     setbuf(stdout, NULL);
1774     setbuf(stderr, NULL);
1775     debugFP = stderr;
1776
1777     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1778         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1779         exit(0);
1780     }
1781
1782     programName = strrchr(argv[0], '/');
1783     if (programName == NULL)
1784       programName = argv[0];
1785     else
1786       programName++;
1787
1788 #ifdef ENABLE_NLS
1789     XtSetLanguageProc(NULL, NULL, NULL);
1790     bindtextdomain(PACKAGE, LOCALEDIR);
1791     textdomain(PACKAGE);
1792 #endif
1793
1794     shellWidget =
1795       XtAppInitialize(&appContext, "XBoard", shellOptions,
1796                       XtNumber(shellOptions),
1797                       &argc, argv, xboardResources, NULL, 0);
1798     appData.boardSize = "";
1799     InitAppData(ConvertToLine(argc, argv));
1800     p = getenv("HOME");
1801     if (p == NULL) p = "/tmp";
1802     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1803     gameCopyFilename = (char*) malloc(i);
1804     gamePasteFilename = (char*) malloc(i);
1805     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1806     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1807
1808     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1809                               clientResources, XtNumber(clientResources),
1810                               NULL, 0);
1811
1812     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1813         static char buf[MSG_SIZ];
1814         EscapeExpand(buf, appData.initString);
1815         appData.initString = strdup(buf);
1816         EscapeExpand(buf, appData.secondInitString);
1817         appData.secondInitString = strdup(buf);
1818         EscapeExpand(buf, appData.firstComputerString);
1819         appData.firstComputerString = strdup(buf);
1820         EscapeExpand(buf, appData.secondComputerString);
1821         appData.secondComputerString = strdup(buf);
1822     }
1823
1824     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1825         chessDir = ".";
1826     } else {
1827         if (chdir(chessDir) != 0) {
1828             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1829             perror(chessDir);
1830             exit(1);
1831         }
1832     }
1833
1834     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1835         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1836         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1837            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1838            exit(errno);
1839         }
1840         setbuf(debugFP, NULL);
1841     }
1842
1843     /* [HGM,HR] make sure board size is acceptable */
1844     if(appData.NrFiles > BOARD_FILES ||
1845        appData.NrRanks > BOARD_RANKS   )
1846          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1847
1848 #if !HIGHDRAG
1849     /* This feature does not work; animation needs a rewrite */
1850     appData.highlightDragging = FALSE;
1851 #endif
1852     InitBackEnd1();
1853
1854     xDisplay = XtDisplay(shellWidget);
1855     xScreen = DefaultScreen(xDisplay);
1856     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1857
1858         gameInfo.variant = StringToVariant(appData.variant);
1859         InitPosition(FALSE);
1860
1861 #ifdef IDSIZE
1862     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1863 #else
1864     if (isdigit(appData.boardSize[0])) {
1865         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1866                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1867                    &fontPxlSize, &smallLayout, &tinyLayout);
1868         if (i == 0) {
1869             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1870                     programName, appData.boardSize);
1871             exit(2);
1872         }
1873         if (i < 7) {
1874             /* Find some defaults; use the nearest known size */
1875             SizeDefaults *szd, *nearest;
1876             int distance = 99999;
1877             nearest = szd = sizeDefaults;
1878             while (szd->name != NULL) {
1879                 if (abs(szd->squareSize - squareSize) < distance) {
1880                     nearest = szd;
1881                     distance = abs(szd->squareSize - squareSize);
1882                     if (distance == 0) break;
1883                 }
1884                 szd++;
1885             }
1886             if (i < 2) lineGap = nearest->lineGap;
1887             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1888             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1889             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1890             if (i < 6) smallLayout = nearest->smallLayout;
1891             if (i < 7) tinyLayout = nearest->tinyLayout;
1892         }
1893     } else {
1894         SizeDefaults *szd = sizeDefaults;
1895         if (*appData.boardSize == NULLCHAR) {
1896             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1897                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1898               szd++;
1899             }
1900             if (szd->name == NULL) szd--;
1901             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1902         } else {
1903             while (szd->name != NULL &&
1904                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1905             if (szd->name == NULL) {
1906                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1907                         programName, appData.boardSize);
1908                 exit(2);
1909             }
1910         }
1911         squareSize = szd->squareSize;
1912         lineGap = szd->lineGap;
1913         clockFontPxlSize = szd->clockFontPxlSize;
1914         coordFontPxlSize = szd->coordFontPxlSize;
1915         fontPxlSize = szd->fontPxlSize;
1916         smallLayout = szd->smallLayout;
1917         tinyLayout = szd->tinyLayout;
1918         // [HGM] font: use defaults from settings file if available and not overruled
1919     }
1920     if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1921         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1922     if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1923         appData.font = fontTable[MESSAGE_FONT][squareSize];
1924     if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1925         appData.coordFont = fontTable[COORD_FONT][squareSize];
1926
1927     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1928     if (strlen(appData.pixmapDirectory) > 0) {
1929         p = ExpandPathName(appData.pixmapDirectory);
1930         if (!p) {
1931             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1932                    appData.pixmapDirectory);
1933             exit(1);
1934         }
1935         if (appData.debugMode) {
1936           fprintf(stderr, _("\
1937 XBoard square size (hint): %d\n\
1938 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1939         }
1940         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1941         if (appData.debugMode) {
1942             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1943         }
1944     }
1945
1946     /* [HR] height treated separately (hacked) */
1947     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1948     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1949     if (appData.showJail == 1) {
1950         /* Jail on top and bottom */
1951         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1952         XtSetArg(boardArgs[2], XtNheight,
1953                  boardHeight + 2*(lineGap + squareSize));
1954     } else if (appData.showJail == 2) {
1955         /* Jail on sides */
1956         XtSetArg(boardArgs[1], XtNwidth,
1957                  boardWidth + 2*(lineGap + squareSize));
1958         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1959     } else {
1960         /* No jail */
1961         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1962         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1963     }
1964
1965     /*
1966      * Determine what fonts to use.
1967      */
1968     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1969     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1970     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1971     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1972     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1973     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1974     appData.font = FindFont(appData.font, fontPxlSize);
1975     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1976     countFontStruct = XQueryFont(xDisplay, countFontID);
1977 //    appData.font = FindFont(appData.font, fontPxlSize);
1978
1979     xdb = XtDatabase(xDisplay);
1980     XrmPutStringResource(&xdb, "*font", appData.font);
1981
1982     /*
1983      * Detect if there are not enough colors available and adapt.
1984      */
1985     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1986       appData.monoMode = True;
1987     }
1988
1989     if (!appData.monoMode) {
1990         vFrom.addr = (caddr_t) appData.lightSquareColor;
1991         vFrom.size = strlen(appData.lightSquareColor);
1992         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1993         if (vTo.addr == NULL) {
1994           appData.monoMode = True;
1995           forceMono = True;
1996         } else {
1997           lightSquareColor = *(Pixel *) vTo.addr;
1998         }
1999     }
2000     if (!appData.monoMode) {
2001         vFrom.addr = (caddr_t) appData.darkSquareColor;
2002         vFrom.size = strlen(appData.darkSquareColor);
2003         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2004         if (vTo.addr == NULL) {
2005           appData.monoMode = True;
2006           forceMono = True;
2007         } else {
2008           darkSquareColor = *(Pixel *) vTo.addr;
2009         }
2010     }
2011     if (!appData.monoMode) {
2012         vFrom.addr = (caddr_t) appData.whitePieceColor;
2013         vFrom.size = strlen(appData.whitePieceColor);
2014         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2015         if (vTo.addr == NULL) {
2016           appData.monoMode = True;
2017           forceMono = True;
2018         } else {
2019           whitePieceColor = *(Pixel *) vTo.addr;
2020         }
2021     }
2022     if (!appData.monoMode) {
2023         vFrom.addr = (caddr_t) appData.blackPieceColor;
2024         vFrom.size = strlen(appData.blackPieceColor);
2025         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2026         if (vTo.addr == NULL) {
2027           appData.monoMode = True;
2028           forceMono = True;
2029         } else {
2030           blackPieceColor = *(Pixel *) vTo.addr;
2031         }
2032     }
2033
2034     if (!appData.monoMode) {
2035         vFrom.addr = (caddr_t) appData.highlightSquareColor;
2036         vFrom.size = strlen(appData.highlightSquareColor);
2037         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2038         if (vTo.addr == NULL) {
2039           appData.monoMode = True;
2040           forceMono = True;
2041         } else {
2042           highlightSquareColor = *(Pixel *) vTo.addr;
2043         }
2044     }
2045
2046     if (!appData.monoMode) {
2047         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
2048         vFrom.size = strlen(appData.premoveHighlightColor);
2049         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2050         if (vTo.addr == NULL) {
2051           appData.monoMode = True;
2052           forceMono = True;
2053         } else {
2054           premoveHighlightColor = *(Pixel *) vTo.addr;
2055         }
2056     }
2057
2058     if (forceMono) {
2059       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2060               programName);
2061
2062       if (appData.bitmapDirectory == NULL ||
2063               appData.bitmapDirectory[0] == NULLCHAR)
2064             appData.bitmapDirectory = DEF_BITMAP_DIR;
2065     }
2066
2067     if (appData.lowTimeWarning && !appData.monoMode) {
2068       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2069       vFrom.size = strlen(appData.lowTimeWarningColor);
2070       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2071       if (vTo.addr == NULL)
2072                 appData.monoMode = True;
2073       else
2074                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2075     }
2076
2077     if (appData.monoMode && appData.debugMode) {
2078         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2079                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2080                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2081     }
2082
2083     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2084         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2085         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2086         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2087         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2088         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2089         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2090         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2091         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2092         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2093       {
2094           if (appData.colorize) {
2095               fprintf(stderr,
2096                       _("%s: can't parse color names; disabling colorization\n"),
2097                       programName);
2098           }
2099           appData.colorize = FALSE;
2100       }
2101     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2102     textColors[ColorNone].attr = 0;
2103
2104     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2105
2106     /*
2107      * widget hierarchy
2108      */
2109     if (tinyLayout) {
2110         layoutName = "tinyLayout";
2111     } else if (smallLayout) {
2112         layoutName = "smallLayout";
2113     } else {
2114         layoutName = "normalLayout";
2115     }
2116     /* Outer layoutWidget is there only to provide a name for use in
2117        resources that depend on the layout style */
2118     layoutWidget =
2119       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2120                             layoutArgs, XtNumber(layoutArgs));
2121     formWidget =
2122       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2123                             formArgs, XtNumber(formArgs));
2124     XtSetArg(args[0], XtNdefaultDistance, &sep);
2125     XtGetValues(formWidget, args, 1);
2126
2127     j = 0;
2128     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2129     XtSetArg(args[0], XtNtop,    XtChainTop);
2130     XtSetArg(args[1], XtNbottom, XtChainTop);
2131     XtSetArg(args[2], XtNright,  XtChainLeft);
2132     XtSetValues(menuBarWidget, args, 3);
2133
2134     widgetList[j++] = whiteTimerWidget =
2135       XtCreateWidget("whiteTime", labelWidgetClass,
2136                      formWidget, timerArgs, XtNumber(timerArgs));
2137     XtSetArg(args[0], XtNfont, clockFontStruct);
2138     XtSetArg(args[1], XtNtop,    XtChainTop);
2139     XtSetArg(args[2], XtNbottom, XtChainTop);
2140     XtSetValues(whiteTimerWidget, args, 3);
2141
2142     widgetList[j++] = blackTimerWidget =
2143       XtCreateWidget("blackTime", labelWidgetClass,
2144                      formWidget, timerArgs, XtNumber(timerArgs));
2145     XtSetArg(args[0], XtNfont, clockFontStruct);
2146     XtSetArg(args[1], XtNtop,    XtChainTop);
2147     XtSetArg(args[2], XtNbottom, XtChainTop);
2148     XtSetValues(blackTimerWidget, args, 3);
2149
2150     if (appData.titleInWindow) {
2151         widgetList[j++] = titleWidget =
2152           XtCreateWidget("title", labelWidgetClass, formWidget,
2153                          titleArgs, XtNumber(titleArgs));
2154         XtSetArg(args[0], XtNtop,    XtChainTop);
2155         XtSetArg(args[1], XtNbottom, XtChainTop);
2156         XtSetValues(titleWidget, args, 2);
2157     }
2158
2159     if (appData.showButtonBar) {
2160       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2161       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2162       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2163       XtSetArg(args[2], XtNtop,    XtChainTop);
2164       XtSetArg(args[3], XtNbottom, XtChainTop);
2165       XtSetValues(buttonBarWidget, args, 4);
2166     }
2167
2168     widgetList[j++] = messageWidget =
2169       XtCreateWidget("message", labelWidgetClass, formWidget,
2170                      messageArgs, XtNumber(messageArgs));
2171     XtSetArg(args[0], XtNtop,    XtChainTop);
2172     XtSetArg(args[1], XtNbottom, XtChainTop);
2173     XtSetValues(messageWidget, args, 2);
2174
2175     widgetList[j++] = boardWidget =
2176       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2177                      XtNumber(boardArgs));
2178
2179     XtManageChildren(widgetList, j);
2180
2181     timerWidth = (boardWidth - sep) / 2;
2182     XtSetArg(args[0], XtNwidth, timerWidth);
2183     XtSetValues(whiteTimerWidget, args, 1);
2184     XtSetValues(blackTimerWidget, args, 1);
2185
2186     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2187     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2188     XtGetValues(whiteTimerWidget, args, 2);
2189
2190     if (appData.showButtonBar) {
2191       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2192       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2193       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2194     }
2195
2196     /*
2197      * formWidget uses these constraints but they are stored
2198      * in the children.
2199      */
2200     i = 0;
2201     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2202     XtSetValues(menuBarWidget, args, i);
2203     if (appData.titleInWindow) {
2204         if (smallLayout) {
2205             i = 0;
2206             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2207             XtSetValues(whiteTimerWidget, args, i);
2208             i = 0;
2209             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2210             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2211             XtSetValues(blackTimerWidget, args, i);
2212             i = 0;
2213             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2214             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2215             XtSetValues(titleWidget, args, i);
2216             i = 0;
2217             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2218             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2219             XtSetValues(messageWidget, args, i);
2220             if (appData.showButtonBar) {
2221               i = 0;
2222               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2223               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2224               XtSetValues(buttonBarWidget, args, i);
2225             }
2226         } else {
2227             i = 0;
2228             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2229             XtSetValues(whiteTimerWidget, args, i);
2230             i = 0;
2231             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2232             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2233             XtSetValues(blackTimerWidget, args, i);
2234             i = 0;
2235             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2236             XtSetValues(titleWidget, args, i);
2237             i = 0;
2238             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2239             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2240             XtSetValues(messageWidget, args, i);
2241             if (appData.showButtonBar) {
2242               i = 0;
2243               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2244               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2245               XtSetValues(buttonBarWidget, args, i);
2246             }
2247         }
2248     } else {
2249         i = 0;
2250         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2251         XtSetValues(whiteTimerWidget, args, i);
2252         i = 0;
2253         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2254         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2255         XtSetValues(blackTimerWidget, args, i);
2256         i = 0;
2257         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2258         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2259         XtSetValues(messageWidget, args, i);
2260         if (appData.showButtonBar) {
2261           i = 0;
2262           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2263           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2264           XtSetValues(buttonBarWidget, args, i);
2265         }
2266     }
2267     i = 0;
2268     XtSetArg(args[0], XtNfromVert, messageWidget);
2269     XtSetArg(args[1], XtNtop,    XtChainTop);
2270     XtSetArg(args[2], XtNbottom, XtChainBottom);
2271     XtSetArg(args[3], XtNleft,   XtChainLeft);
2272     XtSetArg(args[4], XtNright,  XtChainRight);
2273     XtSetValues(boardWidget, args, 5);
2274
2275     XtRealizeWidget(shellWidget);
2276
2277     if(wpMain.x > 0) {
2278       XtSetArg(args[0], XtNx, wpMain.x);
2279       XtSetArg(args[1], XtNy, wpMain.y);
2280       XtSetValues(shellWidget, args, 2);
2281     }
2282
2283     /*
2284      * Correct the width of the message and title widgets.
2285      * It is not known why some systems need the extra fudge term.
2286      * The value "2" is probably larger than needed.
2287      */
2288     XawFormDoLayout(formWidget, False);
2289
2290 #define WIDTH_FUDGE 2
2291     i = 0;
2292     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2293     XtSetArg(args[i], XtNheight, &h);  i++;
2294     XtGetValues(messageWidget, args, i);
2295     if (appData.showButtonBar) {
2296       i = 0;
2297       XtSetArg(args[i], XtNwidth, &w);  i++;
2298       XtGetValues(buttonBarWidget, args, i);
2299       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2300     } else {
2301       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2302     }
2303
2304     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2305     if (gres != XtGeometryYes && appData.debugMode) {
2306       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2307               programName, gres, w, h, wr, hr);
2308     }
2309
2310     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2311     /* The size used for the child widget in layout lags one resize behind
2312        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2313     w--;
2314     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2315     if (gres != XtGeometryYes && appData.debugMode) {
2316       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2317               programName, gres, w, h, wr, hr);
2318     }
2319     /* !! end hack */
2320     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2321     XtSetArg(args[1], XtNright, XtChainRight);
2322     XtSetValues(messageWidget, args, 2);
2323
2324     if (appData.titleInWindow) {
2325         i = 0;
2326         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2327         XtSetArg(args[i], XtNheight, &h);  i++;
2328         XtGetValues(titleWidget, args, i);
2329         if (smallLayout) {
2330             w = boardWidth - 2*bor;
2331         } else {
2332             XtSetArg(args[0], XtNwidth, &w);
2333             XtGetValues(menuBarWidget, args, 1);
2334             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2335         }
2336
2337         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2338         if (gres != XtGeometryYes && appData.debugMode) {
2339             fprintf(stderr,
2340                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2341                     programName, gres, w, h, wr, hr);
2342         }
2343     }
2344     XawFormDoLayout(formWidget, True);
2345
2346     xBoardWindow = XtWindow(boardWidget);
2347
2348     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2349     //       not need to go into InitDrawingSizes().
2350 #endif
2351
2352     /*
2353      * Create X checkmark bitmap and initialize option menu checks.
2354      */
2355     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2356                checkmark_bits, checkmark_width, checkmark_height);
2357     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2358     if (appData.alwaysPromoteToQueen) {
2359         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2360                     args, 1);
2361     }
2362     if (appData.animateDragging) {
2363         XtSetValues(XtNameToWidget(menuBarWidget,
2364                                    "menuOptions.Animate Dragging"),
2365                     args, 1);
2366     }
2367     if (appData.animate) {
2368         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2369                     args, 1);
2370     }
2371     if (appData.autoComment) {
2372         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2373                     args, 1);
2374     }
2375     if (appData.autoCallFlag) {
2376         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2377                     args, 1);
2378     }
2379     if (appData.autoFlipView) {
2380         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2381                     args, 1);
2382     }
2383     if (appData.autoObserve) {
2384         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2385                     args, 1);
2386     }
2387     if (appData.autoRaiseBoard) {
2388         XtSetValues(XtNameToWidget(menuBarWidget,
2389                                    "menuOptions.Auto Raise Board"), args, 1);
2390     }
2391     if (appData.autoSaveGames) {
2392         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2393                     args, 1);
2394     }
2395     if (appData.saveGameFile[0] != NULLCHAR) {
2396         /* Can't turn this off from menu */
2397         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2398                     args, 1);
2399         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2400                        False);
2401
2402     }
2403     if (appData.blindfold) {
2404         XtSetValues(XtNameToWidget(menuBarWidget,
2405                                    "menuOptions.Blindfold"), args, 1);
2406     }
2407     if (appData.flashCount > 0) {
2408         XtSetValues(XtNameToWidget(menuBarWidget,
2409                                    "menuOptions.Flash Moves"),
2410                     args, 1);
2411     }
2412     if (appData.getMoveList) {
2413         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2414                     args, 1);
2415     }
2416 #if HIGHDRAG
2417     if (appData.highlightDragging) {
2418         XtSetValues(XtNameToWidget(menuBarWidget,
2419                                    "menuOptions.Highlight Dragging"),
2420                     args, 1);
2421     }
2422 #endif
2423     if (appData.highlightLastMove) {
2424         XtSetValues(XtNameToWidget(menuBarWidget,
2425                                    "menuOptions.Highlight Last Move"),
2426                     args, 1);
2427     }
2428     if (appData.icsAlarm) {
2429         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2430                     args, 1);
2431     }
2432     if (appData.ringBellAfterMoves) {
2433         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2434                     args, 1);
2435     }
2436     if (appData.oldSaveStyle) {
2437         XtSetValues(XtNameToWidget(menuBarWidget,
2438                                    "menuOptions.Old Save Style"), args, 1);
2439     }
2440     if (appData.periodicUpdates) {
2441         XtSetValues(XtNameToWidget(menuBarWidget,
2442                                    "menuOptions.Periodic Updates"), args, 1);
2443     }
2444     if (appData.ponderNextMove) {
2445         XtSetValues(XtNameToWidget(menuBarWidget,
2446                                    "menuOptions.Ponder Next Move"), args, 1);
2447     }
2448     if (appData.popupExitMessage) {
2449         XtSetValues(XtNameToWidget(menuBarWidget,
2450                                    "menuOptions.Popup Exit Message"), args, 1);
2451     }
2452     if (appData.popupMoveErrors) {
2453         XtSetValues(XtNameToWidget(menuBarWidget,
2454                                    "menuOptions.Popup Move Errors"), args, 1);
2455     }
2456     if (appData.premove) {
2457         XtSetValues(XtNameToWidget(menuBarWidget,
2458                                    "menuOptions.Premove"), args, 1);
2459     }
2460     if (appData.quietPlay) {
2461         XtSetValues(XtNameToWidget(menuBarWidget,
2462                                    "menuOptions.Quiet Play"), args, 1);
2463     }
2464     if (appData.showCoords) {
2465         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2466                     args, 1);
2467     }
2468     if (appData.hideThinkingFromHuman) {
2469         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2470                     args, 1);
2471     }
2472     if (appData.testLegality) {
2473         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2474                     args, 1);
2475     }
2476     if (saveSettingsOnExit) {
2477         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2478                     args, 1);
2479     }
2480
2481     /*
2482      * Create an icon.
2483      */
2484     ReadBitmap(&wIconPixmap, "icon_white.bm",
2485                icon_white_bits, icon_white_width, icon_white_height);
2486     ReadBitmap(&bIconPixmap, "icon_black.bm",
2487                icon_black_bits, icon_black_width, icon_black_height);
2488     iconPixmap = wIconPixmap;
2489     i = 0;
2490     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2491     XtSetValues(shellWidget, args, i);
2492
2493     /*
2494      * Create a cursor for the board widget.
2495      */
2496     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2497     XChangeWindowAttributes(xDisplay, xBoardWindow,
2498                             CWCursor, &window_attributes);
2499
2500     /*
2501      * Inhibit shell resizing.
2502      */
2503     shellArgs[0].value = (XtArgVal) &w;
2504     shellArgs[1].value = (XtArgVal) &h;
2505     XtGetValues(shellWidget, shellArgs, 2);
2506     shellArgs[4].value = shellArgs[2].value = w;
2507     shellArgs[5].value = shellArgs[3].value = h;
2508     XtSetValues(shellWidget, &shellArgs[2], 4);
2509     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2510     marginH =  h - boardHeight;
2511
2512     CatchDeleteWindow(shellWidget, "QuitProc");
2513
2514     CreateGCs();
2515     CreateGrid();
2516 #if HAVE_LIBXPM
2517     if (appData.bitmapDirectory[0] != NULLCHAR) {
2518       CreatePieces();
2519     } else {
2520       CreateXPMPieces();
2521     }
2522 #else
2523     CreateXIMPieces();
2524     /* Create regular pieces */
2525     if (!useImages) CreatePieces();
2526 #endif
2527
2528     CreatePieceMenus();
2529
2530     if (appData.animate || appData.animateDragging)
2531       CreateAnimVars();
2532
2533     XtAugmentTranslations(formWidget,
2534                           XtParseTranslationTable(globalTranslations));
2535     XtAugmentTranslations(boardWidget,
2536                           XtParseTranslationTable(boardTranslations));
2537     XtAugmentTranslations(whiteTimerWidget,
2538                           XtParseTranslationTable(whiteTranslations));
2539     XtAugmentTranslations(blackTimerWidget,
2540                           XtParseTranslationTable(blackTranslations));
2541
2542     /* Why is the following needed on some versions of X instead
2543      * of a translation? */
2544     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2545                       (XtEventHandler) EventProc, NULL);
2546     /* end why */
2547
2548     /* [AS] Restore layout */
2549     if( wpMoveHistory.visible ) {
2550       HistoryPopUp();
2551     }
2552
2553     if( wpEvalGraph.visible )
2554       {
2555         EvalGraphPopUp();
2556       };
2557
2558     if( wpEngineOutput.visible ) {
2559       EngineOutputPopUp();
2560     }
2561
2562     InitBackEnd2();
2563
2564     if (errorExitStatus == -1) {
2565         if (appData.icsActive) {
2566             /* We now wait until we see "login:" from the ICS before
2567                sending the logon script (problems with timestamp otherwise) */
2568             /*ICSInitScript();*/
2569             if (appData.icsInputBox) ICSInputBoxPopUp();
2570         }
2571
2572     #ifdef SIGWINCH
2573     signal(SIGWINCH, TermSizeSigHandler);
2574     #endif
2575         signal(SIGINT, IntSigHandler);
2576         signal(SIGTERM, IntSigHandler);
2577         if (*appData.cmailGameName != NULLCHAR) {
2578             signal(SIGUSR1, CmailSigHandler);
2579         }
2580     }
2581     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2582     InitPosition(TRUE);
2583     XtSetKeyboardFocus(shellWidget, formWidget);
2584
2585     XtAppMainLoop(appContext);
2586     if (appData.debugMode) fclose(debugFP); // [DM] debug
2587     return 0;
2588 }
2589
2590 void
2591 ShutDownFrontEnd()
2592 {
2593     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2594         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2595     }
2596     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2597     unlink(gameCopyFilename);
2598     unlink(gamePasteFilename);
2599 }
2600
2601 RETSIGTYPE TermSizeSigHandler(int sig)
2602 {
2603     update_ics_width();
2604 }
2605
2606 RETSIGTYPE
2607 IntSigHandler(sig)
2608      int sig;
2609 {
2610     ExitEvent(sig);
2611 }
2612
2613 RETSIGTYPE
2614 CmailSigHandler(sig)
2615      int sig;
2616 {
2617     int dummy = 0;
2618     int error;
2619
2620     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2621
2622     /* Activate call-back function CmailSigHandlerCallBack()             */
2623     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2624
2625     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2626 }
2627
2628 void
2629 CmailSigHandlerCallBack(isr, closure, message, count, error)
2630      InputSourceRef isr;
2631      VOIDSTAR closure;
2632      char *message;
2633      int count;
2634      int error;
2635 {
2636     BoardToTop();
2637     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2638 }
2639 /**** end signal code ****/
2640
2641
2642 void
2643 ICSInitScript()
2644 {
2645   /* try to open the icsLogon script, either in the location given
2646    * or in the users HOME directory
2647    */
2648
2649   FILE *f;
2650   char buf[MSG_SIZ];
2651   char *homedir;
2652
2653   f = fopen(appData.icsLogon, "r");
2654   if (f == NULL)
2655     {
2656       homedir = getenv("HOME");
2657       if (homedir != NULL)
2658         {
2659           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2660           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2661           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
2662           f = fopen(buf, "r");
2663         }
2664     }
2665
2666   if (f != NULL)
2667     ProcessICSInitScript(f);
2668   else
2669     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2670
2671   return;
2672 }
2673
2674 void
2675 ResetFrontEnd()
2676 {
2677     CommentPopDown();
2678     EditCommentPopDown();
2679     TagsPopDown();
2680     return;
2681 }
2682
2683 typedef struct {
2684     char *name;
2685     Boolean value;
2686 } Enables;
2687
2688 void
2689 GreyRevert(grey)
2690      Boolean grey;
2691 {
2692     Widget w;
2693     if (!menuBarWidget) return;
2694     w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2695     if (w == NULL) {
2696       DisplayError("menuStep.Revert", 0);
2697     } else {
2698       XtSetSensitive(w, !grey);
2699     }
2700     w = XtNameToWidget(menuBarWidget, "menuStep.Annotate");
2701     if (w == NULL) {
2702       DisplayError("menuStep.Annotate", 0);
2703     } else {
2704       XtSetSensitive(w, !grey);
2705     }
2706 }
2707
2708 void
2709 SetMenuEnables(enab)
2710      Enables *enab;
2711 {
2712   Widget w;
2713   if (!menuBarWidget) return;
2714   while (enab->name != NULL) {
2715     w = XtNameToWidget(menuBarWidget, enab->name);
2716     if (w == NULL) {
2717       DisplayError(enab->name, 0);
2718     } else {
2719       XtSetSensitive(w, enab->value);
2720     }
2721     enab++;
2722   }
2723 }
2724
2725 Enables icsEnables[] = {
2726     { "menuFile.Mail Move", False },
2727     { "menuFile.Reload CMail Message", False },
2728     { "menuMode.Machine Black", False },
2729     { "menuMode.Machine White", False },
2730     { "menuMode.Analysis Mode", False },
2731     { "menuMode.Analyze File", False },
2732     { "menuMode.Two Machines", False },
2733 #ifndef ZIPPY
2734     { "menuHelp.Hint", False },
2735     { "menuHelp.Book", False },
2736     { "menuStep.Move Now", False },
2737     { "menuOptions.Periodic Updates", False },
2738     { "menuOptions.Hide Thinking", False },
2739     { "menuOptions.Ponder Next Move", False },
2740 #endif
2741     { "menuStep.Annotate", False },
2742     { NULL, False }
2743 };
2744
2745 Enables ncpEnables[] = {
2746     { "menuFile.Mail Move", False },
2747     { "menuFile.Reload CMail Message", False },
2748     { "menuMode.Machine White", False },
2749     { "menuMode.Machine Black", False },
2750     { "menuMode.Analysis Mode", False },
2751     { "menuMode.Analyze File", False },
2752     { "menuMode.Two Machines", False },
2753     { "menuMode.ICS Client", False },
2754     { "menuMode.ICS Input Box", False },
2755     { "Action", False },
2756     { "menuStep.Revert", False },
2757     { "menuStep.Annotate", False },
2758     { "menuStep.Move Now", False },
2759     { "menuStep.Retract Move", False },
2760     { "menuOptions.Auto Comment", False },
2761     { "menuOptions.Auto Flag", False },
2762     { "menuOptions.Auto Flip View", False },
2763     { "menuOptions.Auto Observe", False },
2764     { "menuOptions.Auto Raise Board", False },
2765     { "menuOptions.Get Move List", False },
2766     { "menuOptions.ICS Alarm", False },
2767     { "menuOptions.Move Sound", False },
2768     { "menuOptions.Quiet Play", False },
2769     { "menuOptions.Hide Thinking", False },
2770     { "menuOptions.Periodic Updates", False },
2771     { "menuOptions.Ponder Next Move", False },
2772     { "menuHelp.Hint", False },
2773     { "menuHelp.Book", False },
2774     { NULL, False }
2775 };
2776
2777 Enables gnuEnables[] = {
2778     { "menuMode.ICS Client", False },
2779     { "menuMode.ICS Input Box", False },
2780     { "menuAction.Accept", False },
2781     { "menuAction.Decline", False },
2782     { "menuAction.Rematch", False },
2783     { "menuAction.Adjourn", False },
2784     { "menuAction.Stop Examining", False },
2785     { "menuAction.Stop Observing", False },
2786     { "menuAction.Upload to Examine", False },
2787     { "menuStep.Revert", False },
2788     { "menuStep.Annotate", False },
2789     { "menuOptions.Auto Comment", False },
2790     { "menuOptions.Auto Observe", False },
2791     { "menuOptions.Auto Raise Board", False },
2792     { "menuOptions.Get Move List", False },
2793     { "menuOptions.Premove", False },
2794     { "menuOptions.Quiet Play", False },
2795
2796     /* The next two options rely on SetCmailMode being called *after*    */
2797     /* SetGNUMode so that when GNU is being used to give hints these     */
2798     /* menu options are still available                                  */
2799
2800     { "menuFile.Mail Move", False },
2801     { "menuFile.Reload CMail Message", False },
2802     { NULL, False }
2803 };
2804
2805 Enables cmailEnables[] = {
2806     { "Action", True },
2807     { "menuAction.Call Flag", False },
2808     { "menuAction.Draw", True },
2809     { "menuAction.Adjourn", False },
2810     { "menuAction.Abort", False },
2811     { "menuAction.Stop Observing", False },
2812     { "menuAction.Stop Examining", False },
2813     { "menuFile.Mail Move", True },
2814     { "menuFile.Reload CMail Message", True },
2815     { NULL, False }
2816 };
2817
2818 Enables trainingOnEnables[] = {
2819   { "menuMode.Edit Comment", False },
2820   { "menuMode.Pause", False },
2821   { "menuStep.Forward", False },
2822   { "menuStep.Backward", False },
2823   { "menuStep.Forward to End", False },
2824   { "menuStep.Back to Start", False },
2825   { "menuStep.Move Now", False },
2826   { "menuStep.Truncate Game", False },
2827   { NULL, False }
2828 };
2829
2830 Enables trainingOffEnables[] = {
2831   { "menuMode.Edit Comment", True },
2832   { "menuMode.Pause", True },
2833   { "menuStep.Forward", True },
2834   { "menuStep.Backward", True },
2835   { "menuStep.Forward to End", True },
2836   { "menuStep.Back to Start", True },
2837   { "menuStep.Move Now", True },
2838   { "menuStep.Truncate Game", True },
2839   { NULL, False }
2840 };
2841
2842 Enables machineThinkingEnables[] = {
2843   { "menuFile.Load Game", False },
2844   { "menuFile.Load Next Game", False },
2845   { "menuFile.Load Previous Game", False },
2846   { "menuFile.Reload Same Game", False },
2847   { "menuFile.Paste Game", False },
2848   { "menuFile.Load Position", False },
2849   { "menuFile.Load Next Position", False },
2850   { "menuFile.Load Previous Position", False },
2851   { "menuFile.Reload Same Position", False },
2852   { "menuFile.Paste Position", False },
2853   { "menuMode.Machine White", False },
2854   { "menuMode.Machine Black", False },
2855   { "menuMode.Two Machines", False },
2856   { "menuStep.Retract Move", False },
2857   { NULL, False }
2858 };
2859
2860 Enables userThinkingEnables[] = {
2861   { "menuFile.Load Game", True },
2862   { "menuFile.Load Next Game", True },
2863   { "menuFile.Load Previous Game", True },
2864   { "menuFile.Reload Same Game", True },
2865   { "menuFile.Paste Game", True },
2866   { "menuFile.Load Position", True },
2867   { "menuFile.Load Next Position", True },
2868   { "menuFile.Load Previous Position", True },
2869   { "menuFile.Reload Same Position", True },
2870   { "menuFile.Paste Position", True },
2871   { "menuMode.Machine White", True },
2872   { "menuMode.Machine Black", True },
2873   { "menuMode.Two Machines", True },
2874   { "menuStep.Retract Move", True },
2875   { NULL, False }
2876 };
2877
2878 void SetICSMode()
2879 {
2880   SetMenuEnables(icsEnables);
2881
2882 #if ZIPPY
2883   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2884      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2885 #endif
2886 }
2887
2888 void
2889 SetNCPMode()
2890 {
2891   SetMenuEnables(ncpEnables);
2892 }
2893
2894 void
2895 SetGNUMode()
2896 {
2897   SetMenuEnables(gnuEnables);
2898 }
2899
2900 void
2901 SetCmailMode()
2902 {
2903   SetMenuEnables(cmailEnables);
2904 }
2905
2906 void
2907 SetTrainingModeOn()
2908 {
2909   SetMenuEnables(trainingOnEnables);
2910   if (appData.showButtonBar) {
2911     XtSetSensitive(buttonBarWidget, False);
2912   }
2913   CommentPopDown();
2914 }
2915
2916 void
2917 SetTrainingModeOff()
2918 {
2919   SetMenuEnables(trainingOffEnables);
2920   if (appData.showButtonBar) {
2921     XtSetSensitive(buttonBarWidget, True);
2922   }
2923 }
2924
2925 void
2926 SetUserThinkingEnables()
2927 {
2928   if (appData.noChessProgram) return;
2929   SetMenuEnables(userThinkingEnables);
2930 }
2931
2932 void
2933 SetMachineThinkingEnables()
2934 {
2935   if (appData.noChessProgram) return;
2936   SetMenuEnables(machineThinkingEnables);
2937   switch (gameMode) {
2938   case MachinePlaysBlack:
2939   case MachinePlaysWhite:
2940   case TwoMachinesPlay:
2941     XtSetSensitive(XtNameToWidget(menuBarWidget,
2942                                   ModeToWidgetName(gameMode)), True);
2943     break;
2944   default:
2945     break;
2946   }
2947 }
2948
2949 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2950 #define HISTORY_SIZE 64\r
2951 static char *history[HISTORY_SIZE];\r
2952 int histIn = 0, histP = 0;\r
2953 \r
2954 void\r
2955 SaveInHistory(char *cmd)\r
2956 {\r
2957   if (history[histIn] != NULL) {\r
2958     free(history[histIn]);\r
2959     history[histIn] = NULL;\r
2960   }\r
2961   if (*cmd == NULLCHAR) return;\r
2962   history[histIn] = StrSave(cmd);\r
2963   histIn = (histIn + 1) % HISTORY_SIZE;\r
2964   if (history[histIn] != NULL) {\r
2965     free(history[histIn]);\r
2966     history[histIn] = NULL;\r
2967   }\r
2968   histP = histIn;\r
2969 }\r
2970 \r
2971 char *\r
2972 PrevInHistory(char *cmd)\r
2973 {\r
2974   int newhp;\r
2975   if (histP == histIn) {\r
2976     if (history[histIn] != NULL) free(history[histIn]);\r
2977     history[histIn] = StrSave(cmd);\r
2978   }\r
2979   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
2980   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
2981   histP = newhp;\r
2982   return history[histP];\r
2983 }\r
2984 \r
2985 char *\r
2986 NextInHistory()\r
2987 {\r
2988   if (histP == histIn) return NULL;\r
2989   histP = (histP + 1) % HISTORY_SIZE;\r
2990   return history[histP];   \r
2991 }
2992 // end of borrowed code\r
2993 \r
2994 #define Abs(n) ((n)<0 ? -(n) : (n))
2995
2996 /*
2997  * Find a font that matches "pattern" that is as close as
2998  * possible to the targetPxlSize.  Prefer fonts that are k
2999  * pixels smaller to fonts that are k pixels larger.  The
3000  * pattern must be in the X Consortium standard format,
3001  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3002  * The return value should be freed with XtFree when no
3003  * longer needed.
3004  */
3005 char *
3006 FindFont(pattern, targetPxlSize)
3007      char *pattern;
3008      int targetPxlSize;
3009 {
3010     char **fonts, *p, *best, *scalable, *scalableTail;
3011     int i, j, nfonts, minerr, err, pxlSize;
3012
3013 #ifdef ENABLE_NLS
3014     char **missing_list;
3015     int missing_count;
3016     char *def_string, *base_fnt_lst, strInt[3];
3017     XFontSet fntSet;
3018     XFontStruct **fnt_list;
3019
3020     base_fnt_lst = calloc(1, strlen(pattern) + 3);
3021     snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3022     p = strstr(pattern, "--");
3023     strncpy(base_fnt_lst, pattern, p - pattern + 2);
3024     strcat(base_fnt_lst, strInt);
3025     strcat(base_fnt_lst, strchr(p + 2, '-'));
3026
3027     if ((fntSet = XCreateFontSet(xDisplay,
3028                                  base_fnt_lst,
3029                                  &missing_list,
3030                                  &missing_count,
3031                                  &def_string)) == NULL) {
3032
3033        fprintf(stderr, _("Unable to create font set.\n"));
3034        exit (2);
3035     }
3036
3037     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3038 #else
3039     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3040     if (nfonts < 1) {
3041         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3042                 programName, pattern);
3043         exit(2);
3044     }
3045 #endif
3046
3047     best = fonts[0];
3048     scalable = NULL;
3049     minerr = 999999;
3050     for (i=0; i<nfonts; i++) {
3051         j = 0;
3052         p = fonts[i];
3053         if (*p != '-') continue;
3054         while (j < 7) {
3055             if (*p == NULLCHAR) break;
3056             if (*p++ == '-') j++;
3057         }
3058         if (j < 7) continue;
3059         pxlSize = atoi(p);
3060         if (pxlSize == 0) {
3061             scalable = fonts[i];
3062             scalableTail = p;
3063         } else {
3064             err = pxlSize - targetPxlSize;
3065             if (Abs(err) < Abs(minerr) ||
3066                 (minerr > 0 && err < 0 && -err == minerr)) {
3067                 best = fonts[i];
3068                 minerr = err;
3069             }
3070         }
3071     }
3072     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3073         /* If the error is too big and there is a scalable font,
3074            use the scalable font. */
3075         int headlen = scalableTail - scalable;
3076         p = (char *) XtMalloc(strlen(scalable) + 10);
3077         while (isdigit(*scalableTail)) scalableTail++;
3078         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3079     } else {
3080         p = (char *) XtMalloc(strlen(best) + 2);
3081         safeStrCpy(p, best, strlen(best)+1 );
3082     }
3083     if (appData.debugMode) {
3084         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
3085                 pattern, targetPxlSize, p);
3086     }
3087 #ifdef ENABLE_NLS
3088     if (missing_count > 0)
3089        XFreeStringList(missing_list);
3090     XFreeFontSet(xDisplay, fntSet);
3091 #else
3092      XFreeFontNames(fonts);
3093 #endif
3094     return p;
3095 }
3096
3097 void CreateGCs()
3098 {
3099     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3100       | GCBackground | GCFunction | GCPlaneMask;
3101     XGCValues gc_values;
3102     GC copyInvertedGC;
3103
3104     gc_values.plane_mask = AllPlanes;
3105     gc_values.line_width = lineGap;
3106     gc_values.line_style = LineSolid;
3107     gc_values.function = GXcopy;
3108
3109     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3110     gc_values.background = XBlackPixel(xDisplay, xScreen);
3111     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3112
3113     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3114     gc_values.background = XWhitePixel(xDisplay, xScreen);
3115     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3116     XSetFont(xDisplay, coordGC, coordFontID);
3117
3118     // [HGM] make font for holdings counts (white on black0
3119     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3120     gc_values.background = XBlackPixel(xDisplay, xScreen);
3121     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3122     XSetFont(xDisplay, countGC, countFontID);
3123
3124     if (appData.monoMode) {
3125         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3126         gc_values.background = XWhitePixel(xDisplay, xScreen);
3127         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3128
3129         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3130         gc_values.background = XBlackPixel(xDisplay, xScreen);
3131         lightSquareGC = wbPieceGC
3132           = XtGetGC(shellWidget, value_mask, &gc_values);
3133
3134         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3135         gc_values.background = XWhitePixel(xDisplay, xScreen);
3136         darkSquareGC = bwPieceGC
3137           = XtGetGC(shellWidget, value_mask, &gc_values);
3138
3139         if (DefaultDepth(xDisplay, xScreen) == 1) {
3140             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3141             gc_values.function = GXcopyInverted;
3142             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3143             gc_values.function = GXcopy;
3144             if (XBlackPixel(xDisplay, xScreen) == 1) {
3145                 bwPieceGC = darkSquareGC;
3146                 wbPieceGC = copyInvertedGC;
3147             } else {
3148                 bwPieceGC = copyInvertedGC;
3149                 wbPieceGC = lightSquareGC;
3150             }
3151         }
3152     } else {
3153         gc_values.foreground = highlightSquareColor;
3154         gc_values.background = highlightSquareColor;
3155         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3156
3157         gc_values.foreground = premoveHighlightColor;
3158         gc_values.background = premoveHighlightColor;
3159         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3160
3161         gc_values.foreground = lightSquareColor;
3162         gc_values.background = darkSquareColor;
3163         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3164
3165         gc_values.foreground = darkSquareColor;
3166         gc_values.background = lightSquareColor;
3167         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3168
3169         gc_values.foreground = jailSquareColor;
3170         gc_values.background = jailSquareColor;
3171         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3172
3173         gc_values.foreground = whitePieceColor;
3174         gc_values.background = darkSquareColor;
3175         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3176
3177         gc_values.foreground = whitePieceColor;
3178         gc_values.background = lightSquareColor;
3179         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3180
3181         gc_values.foreground = whitePieceColor;
3182         gc_values.background = jailSquareColor;
3183         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3184
3185         gc_values.foreground = blackPieceColor;
3186         gc_values.background = darkSquareColor;
3187         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3188
3189         gc_values.foreground = blackPieceColor;
3190         gc_values.background = lightSquareColor;
3191         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3192
3193         gc_values.foreground = blackPieceColor;
3194         gc_values.background = jailSquareColor;
3195         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3196     }
3197 }
3198
3199 void loadXIM(xim, xmask, filename, dest, mask)
3200      XImage *xim;
3201      XImage *xmask;
3202      char *filename;
3203      Pixmap *dest;
3204      Pixmap *mask;
3205 {
3206     int x, y, w, h, p;
3207     FILE *fp;
3208     Pixmap temp;
3209     XGCValues   values;
3210     GC maskGC;
3211
3212     fp = fopen(filename, "rb");
3213     if (!fp) {
3214         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3215         exit(1);
3216     }
3217
3218     w = fgetc(fp);
3219     h = fgetc(fp);
3220
3221     for (y=0; y<h; ++y) {
3222         for (x=0; x<h; ++x) {
3223             p = fgetc(fp);
3224
3225             switch (p) {
3226               case 0:
3227                 XPutPixel(xim, x, y, blackPieceColor);
3228                 if (xmask)
3229                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3230                 break;
3231               case 1:
3232                 XPutPixel(xim, x, y, darkSquareColor);
3233                 if (xmask)
3234                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3235                 break;
3236               case 2:
3237                 XPutPixel(xim, x, y, whitePieceColor);
3238                 if (xmask)
3239                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3240                 break;
3241               case 3:
3242                 XPutPixel(xim, x, y, lightSquareColor);
3243                 if (xmask)
3244                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3245                 break;
3246             }
3247         }
3248     }
3249
3250     /* create Pixmap of piece */
3251     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3252                           w, h, xim->depth);
3253     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3254               0, 0, 0, 0, w, h);
3255
3256     /* create Pixmap of clipmask
3257        Note: We assume the white/black pieces have the same
3258              outline, so we make only 6 masks. This is okay
3259              since the XPM clipmask routines do the same. */
3260     if (xmask) {
3261       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3262                             w, h, xim->depth);
3263       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3264               0, 0, 0, 0, w, h);
3265
3266       /* now create the 1-bit version */
3267       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3268                           w, h, 1);
3269
3270       values.foreground = 1;
3271       values.background = 0;
3272
3273       /* Don't use XtGetGC, not read only */
3274       maskGC = XCreateGC(xDisplay, *mask,
3275                     GCForeground | GCBackground, &values);
3276       XCopyPlane(xDisplay, temp, *mask, maskGC,
3277                   0, 0, squareSize, squareSize, 0, 0, 1);
3278       XFreePixmap(xDisplay, temp);
3279     }
3280 }
3281
3282
3283 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3284
3285 void CreateXIMPieces()
3286 {
3287     int piece, kind;
3288     char buf[MSG_SIZ];
3289     u_int ss;
3290     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3291     XImage *ximtemp;
3292
3293     ss = squareSize;
3294
3295     /* The XSynchronize calls were copied from CreatePieces.
3296        Not sure if needed, but can't hurt */
3297     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3298                                      buffering bug */
3299
3300     /* temp needed by loadXIM() */
3301     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3302                  0, 0, ss, ss, AllPlanes, XYPixmap);
3303
3304     if (strlen(appData.pixmapDirectory) == 0) {
3305       useImages = 0;
3306     } else {
3307         useImages = 1;
3308         if (appData.monoMode) {
3309           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3310                             0, 2);
3311           ExitEvent(2);
3312         }
3313         fprintf(stderr, _("\nLoading XIMs...\n"));
3314         /* Load pieces */
3315         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3316             fprintf(stderr, "%d", piece+1);
3317             for (kind=0; kind<4; kind++) {
3318                 fprintf(stderr, ".");
3319                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3320                         ExpandPathName(appData.pixmapDirectory),
3321                         piece <= (int) WhiteKing ? "" : "w",
3322                         pieceBitmapNames[piece],
3323                         ximkind[kind], ss);
3324                 ximPieceBitmap[kind][piece] =
3325                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3326                             0, 0, ss, ss, AllPlanes, XYPixmap);
3327                 if (appData.debugMode)
3328                   fprintf(stderr, _("(File:%s:) "), buf);
3329                 loadXIM(ximPieceBitmap[kind][piece],
3330                         ximtemp, buf,
3331                         &(xpmPieceBitmap2[kind][piece]),
3332                         &(ximMaskPm2[piece]));
3333                 if(piece <= (int)WhiteKing)
3334                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3335             }
3336             fprintf(stderr," ");
3337         }
3338         /* Load light and dark squares */
3339         /* If the LSQ and DSQ pieces don't exist, we will
3340            draw them with solid squares. */
3341         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3342         if (access(buf, 0) != 0) {
3343             useImageSqs = 0;
3344         } else {
3345             useImageSqs = 1;
3346             fprintf(stderr, _("light square "));
3347             ximLightSquare=
3348               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3349                         0, 0, ss, ss, AllPlanes, XYPixmap);
3350             if (appData.debugMode)
3351               fprintf(stderr, _("(File:%s:) "), buf);
3352
3353             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3354             fprintf(stderr, _("dark square "));
3355             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3356                     ExpandPathName(appData.pixmapDirectory), ss);
3357             if (appData.debugMode)
3358               fprintf(stderr, _("(File:%s:) "), buf);
3359             ximDarkSquare=
3360               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3361                         0, 0, ss, ss, AllPlanes, XYPixmap);
3362             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3363             xpmJailSquare = xpmLightSquare;
3364         }
3365         fprintf(stderr, _("Done.\n"));
3366     }
3367     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3368 }
3369
3370 #if HAVE_LIBXPM
3371 void CreateXPMPieces()
3372 {
3373     int piece, kind, r;
3374     char buf[MSG_SIZ];
3375     u_int ss = squareSize;
3376     XpmAttributes attr;
3377     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3378     XpmColorSymbol symbols[4];
3379
3380     /* The XSynchronize calls were copied from CreatePieces.
3381        Not sure if needed, but can't hurt */
3382     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3383
3384     /* Setup translations so piece colors match square colors */
3385     symbols[0].name = "light_piece";
3386     symbols[0].value = appData.whitePieceColor;
3387     symbols[1].name = "dark_piece";
3388     symbols[1].value = appData.blackPieceColor;
3389     symbols[2].name = "light_square";
3390     symbols[2].value = appData.lightSquareColor;
3391     symbols[3].name = "dark_square";
3392     symbols[3].value = appData.darkSquareColor;
3393
3394     attr.valuemask = XpmColorSymbols;
3395     attr.colorsymbols = symbols;
3396     attr.numsymbols = 4;
3397
3398     if (appData.monoMode) {
3399       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3400                         0, 2);
3401       ExitEvent(2);
3402     }
3403     if (strlen(appData.pixmapDirectory) == 0) {
3404         XpmPieces* pieces = builtInXpms;
3405         useImages = 1;
3406         /* Load pieces */
3407         while (pieces->size != squareSize && pieces->size) pieces++;
3408         if (!pieces->size) {
3409           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3410           exit(1);
3411         }
3412         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3413             for (kind=0; kind<4; kind++) {
3414
3415                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3416                                                pieces->xpm[piece][kind],
3417                                                &(xpmPieceBitmap2[kind][piece]),
3418                                                NULL, &attr)) != 0) {
3419                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3420                           r, buf);
3421                   exit(1);
3422                 }
3423                 if(piece <= (int) WhiteKing)
3424                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3425             }
3426         }
3427         useImageSqs = 0;
3428         xpmJailSquare = xpmLightSquare;
3429     } else {
3430         useImages = 1;
3431
3432         fprintf(stderr, _("\nLoading XPMs...\n"));
3433
3434         /* Load pieces */
3435         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3436             fprintf(stderr, "%d ", piece+1);
3437             for (kind=0; kind<4; kind++) {
3438               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3439                         ExpandPathName(appData.pixmapDirectory),
3440                         piece > (int) WhiteKing ? "w" : "",
3441                         pieceBitmapNames[piece],
3442                         xpmkind[kind], ss);
3443                 if (appData.debugMode) {
3444                     fprintf(stderr, _("(File:%s:) "), buf);
3445                 }
3446                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3447                                            &(xpmPieceBitmap2[kind][piece]),
3448                                            NULL, &attr)) != 0) {
3449                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3450                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3451                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3452                                 ExpandPathName(appData.pixmapDirectory),
3453                                 xpmkind[kind], ss);
3454                         if (appData.debugMode) {
3455                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3456                         }
3457                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3458                                                 &(xpmPieceBitmap2[kind][piece]),
3459                                                 NULL, &attr);
3460                     }
3461                     if (r != 0) {
3462                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3463                                 r, buf);
3464                         exit(1);
3465                     }
3466                 }
3467                 if(piece <= (int) WhiteKing)
3468                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3469             }
3470         }
3471         /* Load light and dark squares */
3472         /* If the LSQ and DSQ pieces don't exist, we will
3473            draw them with solid squares. */
3474         fprintf(stderr, _("light square "));
3475         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3476         if (access(buf, 0) != 0) {
3477             useImageSqs = 0;
3478         } else {
3479             useImageSqs = 1;
3480             if (appData.debugMode)
3481               fprintf(stderr, _("(File:%s:) "), buf);
3482
3483             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3484                                        &xpmLightSquare, NULL, &attr)) != 0) {
3485                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3486                 exit(1);
3487             }
3488             fprintf(stderr, _("dark square "));
3489             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3490                     ExpandPathName(appData.pixmapDirectory), ss);
3491             if (appData.debugMode) {
3492                 fprintf(stderr, _("(File:%s:) "), buf);
3493             }
3494             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3495                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3496                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3497                 exit(1);
3498             }
3499         }
3500         xpmJailSquare = xpmLightSquare;
3501         fprintf(stderr, _("Done.\n"));
3502     }
3503     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3504                                       buffering bug */
3505 }
3506 #endif /* HAVE_LIBXPM */
3507
3508 #if HAVE_LIBXPM
3509 /* No built-in bitmaps */
3510 void CreatePieces()
3511 {
3512     int piece, kind;
3513     char buf[MSG_SIZ];
3514     u_int ss = squareSize;
3515
3516     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3517                                      buffering bug */
3518
3519     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3520         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3521           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3522                    pieceBitmapNames[piece],
3523                    ss, kind == SOLID ? 's' : 'o');
3524           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3525           if(piece <= (int)WhiteKing)
3526             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3527         }
3528     }
3529
3530     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3531                                       buffering bug */
3532 }
3533 #else
3534 /* With built-in bitmaps */
3535 void CreatePieces()
3536 {
3537     BuiltInBits* bib = builtInBits;
3538     int piece, kind;
3539     char buf[MSG_SIZ];
3540     u_int ss = squareSize;
3541
3542     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3543                                      buffering bug */
3544
3545     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3546
3547     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3548         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3549           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3550                    pieceBitmapNames[piece],
3551                    ss, kind == SOLID ? 's' : 'o');
3552           ReadBitmap(&pieceBitmap2[kind][piece], buf,
3553                      bib->bits[kind][piece], ss, ss);
3554           if(piece <= (int)WhiteKing)
3555             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3556         }
3557     }
3558
3559     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3560                                       buffering bug */
3561 }
3562 #endif
3563
3564 void ReadBitmap(pm, name, bits, wreq, hreq)
3565      Pixmap *pm;
3566      String name;
3567      unsigned char bits[];
3568      u_int wreq, hreq;
3569 {
3570     int x_hot, y_hot;
3571     u_int w, h;
3572     int errcode;
3573     char msg[MSG_SIZ], fullname[MSG_SIZ];
3574
3575     if (*appData.bitmapDirectory != NULLCHAR) {
3576       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3577       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3578       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3579       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3580                                 &w, &h, pm, &x_hot, &y_hot);
3581       fprintf(stderr, "load %s\n", name);
3582         if (errcode != BitmapSuccess) {
3583             switch (errcode) {
3584               case BitmapOpenFailed:
3585                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3586                 break;
3587               case BitmapFileInvalid:
3588                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3589                 break;
3590               case BitmapNoMemory:
3591                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3592                         fullname);
3593                 break;
3594               default:
3595                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3596                         errcode, fullname);
3597                 break;
3598             }
3599             fprintf(stderr, _("%s: %s...using built-in\n"),
3600                     programName, msg);
3601         } else if (w != wreq || h != hreq) {
3602             fprintf(stderr,
3603                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3604                     programName, fullname, w, h, wreq, hreq);
3605         } else {
3606             return;
3607         }
3608     }
3609     if (bits != NULL) {
3610         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3611                                     wreq, hreq);
3612     }
3613 }
3614
3615 void CreateGrid()
3616 {
3617     int i, j;
3618
3619     if (lineGap == 0) return;
3620
3621     /* [HR] Split this into 2 loops for non-square boards. */
3622
3623     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3624         gridSegments[i].x1 = 0;
3625         gridSegments[i].x2 =
3626           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3627         gridSegments[i].y1 = gridSegments[i].y2
3628           = lineGap / 2 + (i * (squareSize + lineGap));
3629     }
3630
3631     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3632         gridSegments[j + i].y1 = 0;
3633         gridSegments[j + i].y2 =
3634           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3635         gridSegments[j + i].x1 = gridSegments[j + i].x2
3636           = lineGap / 2 + (j * (squareSize + lineGap));
3637     }
3638 }
3639
3640 static void MenuBarSelect(w, addr, index)
3641      Widget w;
3642      caddr_t addr;
3643      caddr_t index;
3644 {
3645     XtActionProc proc = (XtActionProc) addr;
3646
3647     (proc)(NULL, NULL, NULL, NULL);
3648 }
3649
3650 void CreateMenuBarPopup(parent, name, mb)
3651      Widget parent;
3652      String name;
3653      Menu *mb;
3654 {
3655     int j;
3656     Widget menu, entry;
3657     MenuItem *mi;
3658     Arg args[16];
3659
3660     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3661                               parent, NULL, 0);
3662     j = 0;
3663     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3664     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3665     mi = mb->mi;
3666     while (mi->string != NULL) {
3667         if (strcmp(mi->string, "----") == 0) {
3668             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3669                                           menu, args, j);
3670         } else {
3671           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3672             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3673                                           menu, args, j+1);
3674             XtAddCallback(entry, XtNcallback,
3675                           (XtCallbackProc) MenuBarSelect,
3676                           (caddr_t) mi->proc);
3677         }
3678         mi++;
3679     }
3680 }
3681
3682 Widget CreateMenuBar(mb)
3683      Menu *mb;
3684 {
3685     int j;
3686     Widget anchor, menuBar;
3687     Arg args[16];
3688     char menuName[MSG_SIZ];
3689
3690     j = 0;
3691     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3692     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3693     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3694     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3695                              formWidget, args, j);
3696
3697     while (mb->name != NULL) {
3698         safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3699         strncat(menuName, mb->name, MSG_SIZ - strlen(menuName) - 1);
3700         j = 0;
3701         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3702         if (tinyLayout) {
3703             char shortName[2];
3704             shortName[0] = _(mb->name)[0];
3705             shortName[1] = NULLCHAR;
3706             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3707         }
3708       else {
3709           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3710       }
3711
3712         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3713         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3714                                        menuBar, args, j);
3715         CreateMenuBarPopup(menuBar, menuName, mb);
3716         mb++;
3717     }
3718     return menuBar;
3719 }
3720
3721 Widget CreateButtonBar(mi)
3722      MenuItem *mi;
3723 {
3724     int j;
3725     Widget button, buttonBar;
3726     Arg args[16];
3727
3728     j = 0;
3729     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3730     if (tinyLayout) {
3731         XtSetArg(args[j], XtNhSpace, 0); j++;
3732     }
3733     XtSetArg(args[j], XtNborderWidth, 0); j++;
3734     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3735     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3736                                formWidget, args, j);
3737
3738     while (mi->string != NULL) {
3739         j = 0;
3740         if (tinyLayout) {
3741             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3742             XtSetArg(args[j], XtNborderWidth, 0); j++;
3743         }
3744       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3745         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3746                                        buttonBar, args, j);
3747         XtAddCallback(button, XtNcallback,
3748                       (XtCallbackProc) MenuBarSelect,
3749                       (caddr_t) mi->proc);
3750         mi++;
3751     }
3752     return buttonBar;
3753 }
3754
3755 Widget
3756 CreatePieceMenu(name, color)
3757      char *name;
3758      int color;
3759 {
3760     int i;
3761     Widget entry, menu;
3762     Arg args[16];
3763     ChessSquare selection;
3764
3765     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3766                               boardWidget, args, 0);
3767
3768     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3769         String item = pieceMenuStrings[color][i];
3770
3771         if (strcmp(item, "----") == 0) {
3772             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3773                                           menu, NULL, 0);
3774         } else {
3775           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3776             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3777                                 menu, args, 1);
3778             selection = pieceMenuTranslation[color][i];
3779             XtAddCallback(entry, XtNcallback,
3780                           (XtCallbackProc) PieceMenuSelect,
3781                           (caddr_t) selection);
3782             if (selection == WhitePawn || selection == BlackPawn) {
3783                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3784                 XtSetValues(menu, args, 1);
3785             }
3786         }
3787     }
3788     return menu;
3789 }
3790
3791 void
3792 CreatePieceMenus()
3793 {
3794     int i;
3795     Widget entry;
3796     Arg args[16];
3797     ChessSquare selection;
3798
3799     whitePieceMenu = CreatePieceMenu("menuW", 0);
3800     blackPieceMenu = CreatePieceMenu("menuB", 1);
3801
3802     XtRegisterGrabAction(PieceMenuPopup, True,
3803                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3804                          GrabModeAsync, GrabModeAsync);
3805
3806     XtSetArg(args[0], XtNlabel, _("Drop"));
3807     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3808                                   boardWidget, args, 1);
3809     for (i = 0; i < DROP_MENU_SIZE; i++) {
3810         String item = dropMenuStrings[i];
3811
3812         if (strcmp(item, "----") == 0) {
3813             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3814                                           dropMenu, NULL, 0);
3815         } else {
3816           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3817             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3818                                 dropMenu, args, 1);
3819             selection = dropMenuTranslation[i];
3820             XtAddCallback(entry, XtNcallback,
3821                           (XtCallbackProc) DropMenuSelect,
3822                           (caddr_t) selection);
3823         }
3824     }
3825 }
3826
3827 void SetupDropMenu()
3828 {
3829     int i, j, count;
3830     char label[32];
3831     Arg args[16];
3832     Widget entry;
3833     char* p;
3834
3835     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3836         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3837         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3838                    dmEnables[i].piece);
3839         XtSetSensitive(entry, p != NULL || !appData.testLegality
3840                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3841                                        && !appData.icsActive));
3842         count = 0;
3843         while (p && *p++ == dmEnables[i].piece) count++;
3844         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3845         j = 0;
3846         XtSetArg(args[j], XtNlabel, label); j++;
3847         XtSetValues(entry, args, j);
3848     }
3849 }
3850
3851 void PieceMenuPopup(w, event, params, num_params)
3852      Widget w;
3853      XEvent *event;
3854      String *params;
3855      Cardinal *num_params;
3856 {
3857     String whichMenu; int menuNr;
3858     if (event->type == ButtonRelease)
3859         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3860     else if (event->type == ButtonPress)
3861         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3862     switch(menuNr) {
3863       case 0: whichMenu = params[0]; break;
3864       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3865       case 2:
3866       case -1: if (errorUp) ErrorPopDown();
3867       default: return;
3868     }
3869     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3870 }
3871
3872 static void PieceMenuSelect(w, piece, junk)
3873      Widget w;
3874      ChessSquare piece;
3875      caddr_t junk;
3876 {
3877     if (pmFromX < 0 || pmFromY < 0) return;
3878     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3879 }
3880
3881 static void DropMenuSelect(w, piece, junk)
3882      Widget w;
3883      ChessSquare piece;
3884      caddr_t junk;
3885 {
3886     if (pmFromX < 0 || pmFromY < 0) return;
3887     DropMenuEvent(piece, pmFromX, pmFromY);
3888 }
3889
3890 void WhiteClock(w, event, prms, nprms)
3891      Widget w;
3892      XEvent *event;
3893      String *prms;
3894      Cardinal *nprms;
3895 {
3896     if (gameMode == EditPosition || gameMode == IcsExamining) {
3897         SetWhiteToPlayEvent();
3898     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3899         CallFlagEvent();
3900     }
3901 }
3902
3903 void BlackClock(w, event, prms, nprms)
3904      Widget w;
3905      XEvent *event;
3906      String *prms;
3907      Cardinal *nprms;
3908 {
3909     if (gameMode == EditPosition || gameMode == IcsExamining) {
3910         SetBlackToPlayEvent();
3911     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3912         CallFlagEvent();
3913     }
3914 }
3915
3916
3917 /*
3918  * If the user selects on a border boundary, return -1; if off the board,
3919  *   return -2.  Otherwise map the event coordinate to the square.
3920  */
3921 int EventToSquare(x, limit)
3922      int x;
3923 {
3924     if (x <= 0)
3925       return -2;
3926     if (x < lineGap)
3927       return -1;
3928     x -= lineGap;
3929     if ((x % (squareSize + lineGap)) >= squareSize)
3930       return -1;
3931     x /= (squareSize + lineGap);
3932     if (x >= limit)
3933       return -2;
3934     return x;
3935 }
3936
3937 static void do_flash_delay(msec)
3938      unsigned long msec;
3939 {
3940     TimeDelay(msec);
3941 }
3942
3943 static void drawHighlight(file, rank, gc)
3944      int file, rank;
3945      GC gc;
3946 {
3947     int x, y;
3948
3949     if (lineGap == 0 || appData.blindfold) return;
3950
3951     if (flipView) {
3952         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3953           (squareSize + lineGap);
3954         y = lineGap/2 + rank * (squareSize + lineGap);
3955     } else {
3956         x = lineGap/2 + file * (squareSize + lineGap);
3957         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3958           (squareSize + lineGap);
3959     }
3960
3961     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3962                    squareSize+lineGap, squareSize+lineGap);
3963 }
3964
3965 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3966 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3967
3968 void
3969 SetHighlights(fromX, fromY, toX, toY)
3970      int fromX, fromY, toX, toY;
3971 {
3972     if (hi1X != fromX || hi1Y != fromY) {
3973         if (hi1X >= 0 && hi1Y >= 0) {
3974             drawHighlight(hi1X, hi1Y, lineGC);
3975         }
3976     } // [HGM] first erase both, then draw new!
3977     if (hi2X != toX || hi2Y != toY) {
3978         if (hi2X >= 0 && hi2Y >= 0) {
3979             drawHighlight(hi2X, hi2Y, lineGC);
3980         }
3981     }
3982     if (hi1X != fromX || hi1Y != fromY) {
3983         if (fromX >= 0 && fromY >= 0) {
3984             drawHighlight(fromX, fromY, highlineGC);
3985         }
3986     }
3987     if (hi2X != toX || hi2Y != toY) {
3988         if (toX >= 0 && toY >= 0) {
3989             drawHighlight(toX, toY, highlineGC);
3990         }
3991     }
3992     hi1X = fromX;
3993     hi1Y = fromY;
3994     hi2X = toX;
3995     hi2Y = toY;
3996 }
3997
3998 void
3999 ClearHighlights()
4000 {
4001     SetHighlights(-1, -1, -1, -1);
4002 }
4003
4004
4005 void
4006 SetPremoveHighlights(fromX, fromY, toX, toY)
4007      int fromX, fromY, toX, toY;
4008 {
4009     if (pm1X != fromX || pm1Y != fromY) {
4010         if (pm1X >= 0 && pm1Y >= 0) {
4011             drawHighlight(pm1X, pm1Y, lineGC);
4012         }
4013         if (fromX >= 0 && fromY >= 0) {
4014             drawHighlight(fromX, fromY, prelineGC);
4015         }
4016     }
4017     if (pm2X != toX || pm2Y != toY) {
4018         if (pm2X >= 0 && pm2Y >= 0) {
4019             drawHighlight(pm2X, pm2Y, lineGC);
4020         }
4021         if (toX >= 0 && toY >= 0) {
4022             drawHighlight(toX, toY, prelineGC);
4023         }
4024     }
4025     pm1X = fromX;
4026     pm1Y = fromY;
4027     pm2X = toX;
4028     pm2Y = toY;
4029 }
4030
4031 void
4032 ClearPremoveHighlights()
4033 {
4034   SetPremoveHighlights(-1, -1, -1, -1);
4035 }
4036
4037 static void BlankSquare(x, y, color, piece, dest)
4038      int x, y, color;
4039      ChessSquare piece;
4040      Drawable dest;
4041 {
4042     if (useImages && useImageSqs) {
4043         Pixmap pm;
4044         switch (color) {
4045           case 1: /* light */
4046             pm = xpmLightSquare;
4047             break;
4048           case 0: /* dark */
4049             pm = xpmDarkSquare;
4050             break;
4051           case 2: /* neutral */
4052           default:
4053             pm = xpmJailSquare;
4054             break;
4055         }
4056         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4057                   squareSize, squareSize, x, y);
4058     } else {
4059         GC gc;
4060         switch (color) {
4061           case 1: /* light */
4062             gc = lightSquareGC;
4063             break;
4064           case 0: /* dark */
4065             gc = darkSquareGC;
4066             break;
4067           case 2: /* neutral */
4068           default:
4069             gc = jailSquareGC;
4070             break;
4071         }
4072         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
4073     }
4074 }
4075
4076 /*
4077    I split out the routines to draw a piece so that I could
4078    make a generic flash routine.
4079 */
4080 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4081      ChessSquare piece;
4082      int square_color, x, y;
4083      Drawable dest;
4084 {
4085     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4086     switch (square_color) {
4087       case 1: /* light */
4088       case 2: /* neutral */
4089       default:
4090         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4091                   ? *pieceToOutline(piece)
4092                   : *pieceToSolid(piece),
4093                   dest, bwPieceGC, 0, 0,
4094                   squareSize, squareSize, x, y);
4095         break;
4096       case 0: /* dark */
4097         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4098                   ? *pieceToSolid(piece)
4099                   : *pieceToOutline(piece),
4100                   dest, wbPieceGC, 0, 0,
4101                   squareSize, squareSize, x, y);
4102         break;
4103     }
4104 }
4105
4106 static void monoDrawPiece(piece, square_color, x, y, dest)
4107      ChessSquare piece;
4108      int square_color, x, y;
4109      Drawable dest;
4110 {
4111     switch (square_color) {
4112       case 1: /* light */
4113       case 2: /* neutral */
4114       default:
4115         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4116                    ? *pieceToOutline(piece)
4117                    : *pieceToSolid(piece),
4118                    dest, bwPieceGC, 0, 0,
4119                    squareSize, squareSize, x, y, 1);
4120         break;
4121       case 0: /* dark */
4122         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4123                    ? *pieceToSolid(piece)
4124                    : *pieceToOutline(piece),
4125                    dest, wbPieceGC, 0, 0,
4126                    squareSize, squareSize, x, y, 1);
4127         break;
4128     }
4129 }
4130
4131 static void colorDrawPiece(piece, square_color, x, y, dest)
4132      ChessSquare piece;
4133      int square_color, x, y;
4134      Drawable dest;
4135 {
4136     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4137     switch (square_color) {
4138       case 1: /* light */
4139         XCopyPlane(xDisplay, *pieceToSolid(piece),
4140                    dest, (int) piece < (int) BlackPawn
4141                    ? wlPieceGC : blPieceGC, 0, 0,
4142                    squareSize, squareSize, x, y, 1);
4143         break;
4144       case 0: /* dark */
4145         XCopyPlane(xDisplay, *pieceToSolid(piece),
4146                    dest, (int) piece < (int) BlackPawn
4147                    ? wdPieceGC : bdPieceGC, 0, 0,
4148                    squareSize, squareSize, x, y, 1);
4149         break;
4150       case 2: /* neutral */
4151       default:
4152         XCopyPlane(xDisplay, *pieceToSolid(piece),
4153                    dest, (int) piece < (int) BlackPawn
4154                    ? wjPieceGC : bjPieceGC, 0, 0,
4155                    squareSize, squareSize, x, y, 1);
4156         break;
4157     }
4158 }
4159
4160 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4161      ChessSquare piece;
4162      int square_color, x, y;
4163      Drawable dest;
4164 {
4165     int kind;
4166
4167     switch (square_color) {
4168       case 1: /* light */
4169       case 2: /* neutral */
4170       default:
4171         if ((int)piece < (int) BlackPawn) {
4172             kind = 0;
4173         } else {
4174             kind = 2;
4175             piece -= BlackPawn;
4176         }
4177         break;
4178       case 0: /* dark */
4179         if ((int)piece < (int) BlackPawn) {
4180             kind = 1;
4181         } else {
4182             kind = 3;
4183             piece -= BlackPawn;
4184         }
4185         break;
4186     }
4187     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4188               dest, wlPieceGC, 0, 0,
4189               squareSize, squareSize, x, y);
4190 }
4191
4192 typedef void (*DrawFunc)();
4193
4194 DrawFunc ChooseDrawFunc()
4195 {
4196     if (appData.monoMode) {
4197         if (DefaultDepth(xDisplay, xScreen) == 1) {
4198             return monoDrawPiece_1bit;
4199         } else {
4200             return monoDrawPiece;
4201         }
4202     } else {
4203         if (useImages)
4204           return colorDrawPieceImage;
4205         else
4206           return colorDrawPiece;
4207     }
4208 }
4209
4210 /* [HR] determine square color depending on chess variant. */
4211 static int SquareColor(row, column)
4212      int row, column;
4213 {
4214     int square_color;
4215
4216     if (gameInfo.variant == VariantXiangqi) {
4217         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4218             square_color = 1;
4219         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4220             square_color = 0;
4221         } else if (row <= 4) {
4222             square_color = 0;
4223         } else {
4224             square_color = 1;
4225         }
4226     } else {
4227         square_color = ((column + row) % 2) == 1;
4228     }
4229
4230     /* [hgm] holdings: next line makes all holdings squares light */
4231     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4232
4233     return square_color;
4234 }
4235
4236 void DrawSquare(row, column, piece, do_flash)
4237      int row, column, do_flash;
4238      ChessSquare piece;
4239 {
4240     int square_color, x, y, direction, font_ascent, font_descent;
4241     int i;
4242     char string[2];
4243     XCharStruct overall;
4244     DrawFunc drawfunc;
4245     int flash_delay;
4246
4247     /* Calculate delay in milliseconds (2-delays per complete flash) */
4248     flash_delay = 500 / appData.flashRate;
4249
4250     if (flipView) {
4251         x = lineGap + ((BOARD_WIDTH-1)-column) *
4252           (squareSize + lineGap);
4253         y = lineGap + row * (squareSize + lineGap);
4254     } else {
4255         x = lineGap + column * (squareSize + lineGap);
4256         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4257           (squareSize + lineGap);
4258     }
4259
4260     if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4261
4262     square_color = SquareColor(row, column);
4263
4264     if ( // [HGM] holdings: blank out area between board and holdings
4265                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4266               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4267                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4268                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4269
4270                         // [HGM] print piece counts next to holdings
4271                         string[1] = NULLCHAR;
4272                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4273                             string[0] = '0' + piece;
4274                             XTextExtents(countFontStruct, string, 1, &direction,
4275                                  &font_ascent, &font_descent, &overall);
4276                             if (appData.monoMode) {
4277                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4278                                                  x + squareSize - overall.width - 2,
4279                                                  y + font_ascent + 1, string, 1);
4280                             } else {
4281                                 XDrawString(xDisplay, xBoardWindow, countGC,
4282                                             x + squareSize - overall.width - 2,
4283                                             y + font_ascent + 1, string, 1);
4284                             }
4285                         }
4286                         if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1) {
4287              &