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