Alter XBoard key bindings to mimic WinBoard
[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] == '/') strcpy(fullname, name); else {
1556     sprintf(fullname, "%s%c%s", installDir, '/', name);
1557   }
1558   return 1;
1559 }
1560
1561 int
1562 MyGetFullPathName(char *name, char *fullname)
1563 { // should use ExpandPath?
1564   name = ExpandPathName(name);
1565   strcpy(fullname, name);
1566   return 1;
1567 }
1568
1569 void
1570 EnsureOnScreen(int *x, int *y, int minX, int minY)
1571 {
1572   return;
1573 }
1574
1575 int
1576 MainWindowUp()
1577 { // [HGM] args: allows testing if main window is realized from back-end
1578   return xBoardWindow != 0;
1579 }
1580
1581 void
1582 PopUpStartupDialog()
1583 {  // start menu not implemented in XBoard
1584 }
1585 char *
1586 ConvertToLine(int argc, char **argv)
1587 {
1588   static char line[128*1024], buf[1024];
1589   int i;
1590
1591   line[0] = NULLCHAR;
1592   for(i=1; i<argc; i++) {
1593     if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1594         && argv[i][0] != '{' )
1595          sprintf(buf, "{%s} ", argv[i]);
1596     else sprintf(buf, "%s ", argv[i]);
1597     strcat(line, buf);
1598   }
1599     line[strlen(line)-1] = NULLCHAR;
1600   return line;
1601 }
1602
1603 //--------------------------------------------------------------------------------------------
1604
1605 extern Boolean twoBoards, partnerUp;
1606
1607 #ifdef IDSIZES
1608   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1609 #else
1610 #define BoardSize int
1611 void InitDrawingSizes(BoardSize boardSize, int flags)
1612 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1613     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1614     Arg args[16];
1615     XtGeometryResult gres;
1616     int i;
1617
1618     if(!formWidget) return;
1619
1620     /*
1621      * Enable shell resizing.
1622      */
1623     shellArgs[0].value = (XtArgVal) &w;
1624     shellArgs[1].value = (XtArgVal) &h;
1625     XtGetValues(shellWidget, shellArgs, 2);
1626
1627     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1628     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1629     XtSetValues(shellWidget, &shellArgs[2], 4);
1630
1631     XtSetArg(args[0], XtNdefaultDistance, &sep);
1632     XtGetValues(formWidget, args, 1);
1633
1634     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1635     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1636     CreateGrid();
1637     hOffset = boardWidth + 10;
1638     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1639         secondSegments[i] = gridSegments[i];
1640         secondSegments[i].x1 += hOffset;
1641         secondSegments[i].x2 += hOffset;
1642     }
1643
1644     XtSetArg(args[0], XtNwidth, boardWidth);
1645     XtSetArg(args[1], XtNheight, boardHeight);
1646     XtSetValues(boardWidget, args, 2);
1647
1648     timerWidth = (boardWidth - sep) / 2;
1649     XtSetArg(args[0], XtNwidth, timerWidth);
1650     XtSetValues(whiteTimerWidget, args, 1);
1651     XtSetValues(blackTimerWidget, args, 1);
1652
1653     XawFormDoLayout(formWidget, False);
1654
1655     if (appData.titleInWindow) {
1656         i = 0;
1657         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1658         XtSetArg(args[i], XtNheight, &h);  i++;
1659         XtGetValues(titleWidget, args, i);
1660         if (smallLayout) {
1661             w = boardWidth - 2*bor;
1662         } else {
1663             XtSetArg(args[0], XtNwidth, &w);
1664             XtGetValues(menuBarWidget, args, 1);
1665             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1666         }
1667
1668         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1669         if (gres != XtGeometryYes && appData.debugMode) {
1670             fprintf(stderr,
1671                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1672                     programName, gres, w, h, wr, hr);
1673         }
1674     }
1675
1676     XawFormDoLayout(formWidget, True);
1677
1678     /*
1679      * Inhibit shell resizing.
1680      */
1681     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1682     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1683     shellArgs[4].value = shellArgs[2].value = w;
1684     shellArgs[5].value = shellArgs[3].value = h;
1685     XtSetValues(shellWidget, &shellArgs[0], 6);
1686
1687     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1688     // (only for xpm)
1689     if(useImages) {
1690       for(i=0; i<4; i++) {
1691         int p;
1692         for(p=0; p<=(int)WhiteKing; p++)
1693            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1694         if(gameInfo.variant == VariantShogi) {
1695            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1696            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1697            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1698            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1699            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1700         }
1701 #ifdef GOTHIC
1702         if(gameInfo.variant == VariantGothic) {
1703            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1704         }
1705 #endif
1706 #if !HAVE_LIBXPM
1707         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1708         for(p=0; p<=(int)WhiteKing; p++)
1709            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1710         if(gameInfo.variant == VariantShogi) {
1711            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1712            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1713            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1714            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1715            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1716         }
1717 #ifdef GOTHIC
1718         if(gameInfo.variant == VariantGothic) {
1719            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1720         }
1721 #endif
1722 #endif
1723       }
1724     } else {
1725       for(i=0; i<2; i++) {
1726         int p;
1727         for(p=0; p<=(int)WhiteKing; p++)
1728            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1729         if(gameInfo.variant == VariantShogi) {
1730            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1731            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1732            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1733            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1734            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1735         }
1736 #ifdef GOTHIC
1737         if(gameInfo.variant == VariantGothic) {
1738            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1739         }
1740 #endif
1741       }
1742     }
1743 #if HAVE_LIBXPM
1744     CreateAnimVars();
1745 #endif
1746 }
1747 #endif
1748
1749 int
1750 main(argc, argv)
1751      int argc;
1752      char **argv;
1753 {
1754     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1755     XSetWindowAttributes window_attributes;
1756     Arg args[16];
1757     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1758     XrmValue vFrom, vTo;
1759     XtGeometryResult gres;
1760     char *p;
1761     XrmDatabase xdb;
1762     int forceMono = False;
1763
1764     srandom(time(0)); // [HGM] book: make random truly random
1765
1766     setbuf(stdout, NULL);
1767     setbuf(stderr, NULL);
1768     debugFP = stderr;
1769
1770     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1771         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1772         exit(0);
1773     }
1774
1775     programName = strrchr(argv[0], '/');
1776     if (programName == NULL)
1777       programName = argv[0];
1778     else
1779       programName++;
1780
1781 #ifdef ENABLE_NLS
1782     XtSetLanguageProc(NULL, NULL, NULL);
1783     bindtextdomain(PACKAGE, LOCALEDIR);
1784     textdomain(PACKAGE);
1785 #endif
1786
1787     shellWidget =
1788       XtAppInitialize(&appContext, "XBoard", shellOptions,
1789                       XtNumber(shellOptions),
1790                       &argc, argv, xboardResources, NULL, 0);
1791     appData.boardSize = "";
1792     InitAppData(ConvertToLine(argc, argv));
1793     p = getenv("HOME");
1794     if (p == NULL) p = "/tmp";
1795     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1796     gameCopyFilename = (char*) malloc(i);
1797     gamePasteFilename = (char*) malloc(i);
1798     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1799     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1800
1801     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1802                               clientResources, XtNumber(clientResources),
1803                               NULL, 0);
1804
1805     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1806         static char buf[MSG_SIZ];
1807         EscapeExpand(buf, appData.initString);
1808         appData.initString = strdup(buf);
1809         EscapeExpand(buf, appData.secondInitString);
1810         appData.secondInitString = strdup(buf);
1811         EscapeExpand(buf, appData.firstComputerString);
1812         appData.firstComputerString = strdup(buf);
1813         EscapeExpand(buf, appData.secondComputerString);
1814         appData.secondComputerString = strdup(buf);
1815     }
1816
1817     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1818         chessDir = ".";
1819     } else {
1820         if (chdir(chessDir) != 0) {
1821             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1822             perror(chessDir);
1823             exit(1);
1824         }
1825     }
1826
1827     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1828         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1829         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1830            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1831            exit(errno);
1832         }
1833         setbuf(debugFP, NULL);
1834     }
1835
1836     /* [HGM,HR] make sure board size is acceptable */
1837     if(appData.NrFiles > BOARD_FILES ||
1838        appData.NrRanks > BOARD_RANKS   )
1839          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1840
1841 #if !HIGHDRAG
1842     /* This feature does not work; animation needs a rewrite */
1843     appData.highlightDragging = FALSE;
1844 #endif
1845     InitBackEnd1();
1846
1847     xDisplay = XtDisplay(shellWidget);
1848     xScreen = DefaultScreen(xDisplay);
1849     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1850
1851         gameInfo.variant = StringToVariant(appData.variant);
1852         InitPosition(FALSE);
1853
1854 #ifdef IDSIZE
1855     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1856 #else
1857     if (isdigit(appData.boardSize[0])) {
1858         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1859                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1860                    &fontPxlSize, &smallLayout, &tinyLayout);
1861         if (i == 0) {
1862             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1863                     programName, appData.boardSize);
1864             exit(2);
1865         }
1866         if (i < 7) {
1867             /* Find some defaults; use the nearest known size */
1868             SizeDefaults *szd, *nearest;
1869             int distance = 99999;
1870             nearest = szd = sizeDefaults;
1871             while (szd->name != NULL) {
1872                 if (abs(szd->squareSize - squareSize) < distance) {
1873                     nearest = szd;
1874                     distance = abs(szd->squareSize - squareSize);
1875                     if (distance == 0) break;
1876                 }
1877                 szd++;
1878             }
1879             if (i < 2) lineGap = nearest->lineGap;
1880             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1881             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1882             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1883             if (i < 6) smallLayout = nearest->smallLayout;
1884             if (i < 7) tinyLayout = nearest->tinyLayout;
1885         }
1886     } else {
1887         SizeDefaults *szd = sizeDefaults;
1888         if (*appData.boardSize == NULLCHAR) {
1889             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1890                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1891               szd++;
1892             }
1893             if (szd->name == NULL) szd--;
1894             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1895         } else {
1896             while (szd->name != NULL &&
1897                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1898             if (szd->name == NULL) {
1899                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1900                         programName, appData.boardSize);
1901                 exit(2);
1902             }
1903         }
1904         squareSize = szd->squareSize;
1905         lineGap = szd->lineGap;
1906         clockFontPxlSize = szd->clockFontPxlSize;
1907         coordFontPxlSize = szd->coordFontPxlSize;
1908         fontPxlSize = szd->fontPxlSize;
1909         smallLayout = szd->smallLayout;
1910         tinyLayout = szd->tinyLayout;
1911         // [HGM] font: use defaults from settings file if available and not overruled
1912     }
1913     if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1914         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1915     if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1916         appData.font = fontTable[MESSAGE_FONT][squareSize];
1917     if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1918         appData.coordFont = fontTable[COORD_FONT][squareSize];
1919
1920     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1921     if (strlen(appData.pixmapDirectory) > 0) {
1922         p = ExpandPathName(appData.pixmapDirectory);
1923         if (!p) {
1924             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1925                    appData.pixmapDirectory);
1926             exit(1);
1927         }
1928         if (appData.debugMode) {
1929           fprintf(stderr, _("\
1930 XBoard square size (hint): %d\n\
1931 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1932         }
1933         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1934         if (appData.debugMode) {
1935             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1936         }
1937     }
1938
1939     /* [HR] height treated separately (hacked) */
1940     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1941     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1942     if (appData.showJail == 1) {
1943         /* Jail on top and bottom */
1944         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1945         XtSetArg(boardArgs[2], XtNheight,
1946                  boardHeight + 2*(lineGap + squareSize));
1947     } else if (appData.showJail == 2) {
1948         /* Jail on sides */
1949         XtSetArg(boardArgs[1], XtNwidth,
1950                  boardWidth + 2*(lineGap + squareSize));
1951         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1952     } else {
1953         /* No jail */
1954         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1955         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1956     }
1957
1958     /*
1959      * Determine what fonts to use.
1960      */
1961     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1962     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1963     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1964     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1965     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1966     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1967     appData.font = FindFont(appData.font, fontPxlSize);
1968     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1969     countFontStruct = XQueryFont(xDisplay, countFontID);
1970 //    appData.font = FindFont(appData.font, fontPxlSize);
1971
1972     xdb = XtDatabase(xDisplay);
1973     XrmPutStringResource(&xdb, "*font", appData.font);
1974
1975     /*
1976      * Detect if there are not enough colors available and adapt.
1977      */
1978     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1979       appData.monoMode = True;
1980     }
1981
1982     if (!appData.monoMode) {
1983         vFrom.addr = (caddr_t) appData.lightSquareColor;
1984         vFrom.size = strlen(appData.lightSquareColor);
1985         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1986         if (vTo.addr == NULL) {
1987           appData.monoMode = True;
1988           forceMono = True;
1989         } else {
1990           lightSquareColor = *(Pixel *) vTo.addr;
1991         }
1992     }
1993     if (!appData.monoMode) {
1994         vFrom.addr = (caddr_t) appData.darkSquareColor;
1995         vFrom.size = strlen(appData.darkSquareColor);
1996         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1997         if (vTo.addr == NULL) {
1998           appData.monoMode = True;
1999           forceMono = True;
2000         } else {
2001           darkSquareColor = *(Pixel *) vTo.addr;
2002         }
2003     }
2004     if (!appData.monoMode) {
2005         vFrom.addr = (caddr_t) appData.whitePieceColor;
2006         vFrom.size = strlen(appData.whitePieceColor);
2007         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2008         if (vTo.addr == NULL) {
2009           appData.monoMode = True;
2010           forceMono = True;
2011         } else {
2012           whitePieceColor = *(Pixel *) vTo.addr;
2013         }
2014     }
2015     if (!appData.monoMode) {
2016         vFrom.addr = (caddr_t) appData.blackPieceColor;
2017         vFrom.size = strlen(appData.blackPieceColor);
2018         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2019         if (vTo.addr == NULL) {
2020           appData.monoMode = True;
2021           forceMono = True;
2022         } else {
2023           blackPieceColor = *(Pixel *) vTo.addr;
2024         }
2025     }
2026
2027     if (!appData.monoMode) {
2028         vFrom.addr = (caddr_t) appData.highlightSquareColor;
2029         vFrom.size = strlen(appData.highlightSquareColor);
2030         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2031         if (vTo.addr == NULL) {
2032           appData.monoMode = True;
2033           forceMono = True;
2034         } else {
2035           highlightSquareColor = *(Pixel *) vTo.addr;
2036         }
2037     }
2038
2039     if (!appData.monoMode) {
2040         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
2041         vFrom.size = strlen(appData.premoveHighlightColor);
2042         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2043         if (vTo.addr == NULL) {
2044           appData.monoMode = True;
2045           forceMono = True;
2046         } else {
2047           premoveHighlightColor = *(Pixel *) vTo.addr;
2048         }
2049     }
2050
2051     if (forceMono) {
2052       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2053               programName);
2054       
2055       if (appData.bitmapDirectory == NULL ||
2056               appData.bitmapDirectory[0] == NULLCHAR)
2057             appData.bitmapDirectory = DEF_BITMAP_DIR;
2058     }
2059
2060     if (appData.lowTimeWarning && !appData.monoMode) {
2061       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2062       vFrom.size = strlen(appData.lowTimeWarningColor);
2063       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2064       if (vTo.addr == NULL) 
2065                 appData.monoMode = True;
2066       else
2067                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2068     }
2069
2070     if (appData.monoMode && appData.debugMode) {
2071         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2072                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2073                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2074     }
2075
2076     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2077         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2078         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2079         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2080         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2081         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2082         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2083         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2084         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2085         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2086       {
2087           if (appData.colorize) {
2088               fprintf(stderr,
2089                       _("%s: can't parse color names; disabling colorization\n"),
2090                       programName);
2091           }
2092           appData.colorize = FALSE;
2093       }
2094     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2095     textColors[ColorNone].attr = 0;
2096
2097     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2098
2099     /*
2100      * widget hierarchy
2101      */
2102     if (tinyLayout) {
2103         layoutName = "tinyLayout";
2104     } else if (smallLayout) {
2105         layoutName = "smallLayout";
2106     } else {
2107         layoutName = "normalLayout";
2108     }
2109     /* Outer layoutWidget is there only to provide a name for use in
2110        resources that depend on the layout style */
2111     layoutWidget =
2112       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2113                             layoutArgs, XtNumber(layoutArgs));
2114     formWidget =
2115       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2116                             formArgs, XtNumber(formArgs));
2117     XtSetArg(args[0], XtNdefaultDistance, &sep);
2118     XtGetValues(formWidget, args, 1);
2119
2120     j = 0;
2121     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2122     XtSetArg(args[0], XtNtop,    XtChainTop);
2123     XtSetArg(args[1], XtNbottom, XtChainTop);
2124     XtSetArg(args[2], XtNright,  XtChainLeft);
2125     XtSetValues(menuBarWidget, args, 3);
2126
2127     widgetList[j++] = whiteTimerWidget =
2128       XtCreateWidget("whiteTime", labelWidgetClass,
2129                      formWidget, timerArgs, XtNumber(timerArgs));
2130     XtSetArg(args[0], XtNfont, clockFontStruct);
2131     XtSetArg(args[1], XtNtop,    XtChainTop);
2132     XtSetArg(args[2], XtNbottom, XtChainTop);
2133     XtSetValues(whiteTimerWidget, args, 3);
2134
2135     widgetList[j++] = blackTimerWidget =
2136       XtCreateWidget("blackTime", labelWidgetClass,
2137                      formWidget, timerArgs, XtNumber(timerArgs));
2138     XtSetArg(args[0], XtNfont, clockFontStruct);
2139     XtSetArg(args[1], XtNtop,    XtChainTop);
2140     XtSetArg(args[2], XtNbottom, XtChainTop);
2141     XtSetValues(blackTimerWidget, args, 3);
2142
2143     if (appData.titleInWindow) {
2144         widgetList[j++] = titleWidget =
2145           XtCreateWidget("title", labelWidgetClass, formWidget,
2146                          titleArgs, XtNumber(titleArgs));
2147         XtSetArg(args[0], XtNtop,    XtChainTop);
2148         XtSetArg(args[1], XtNbottom, XtChainTop);
2149         XtSetValues(titleWidget, args, 2);
2150     }
2151
2152     if (appData.showButtonBar) {
2153       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2154       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2155       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2156       XtSetArg(args[2], XtNtop,    XtChainTop);
2157       XtSetArg(args[3], XtNbottom, XtChainTop);
2158       XtSetValues(buttonBarWidget, args, 4);
2159     }
2160
2161     widgetList[j++] = messageWidget =
2162       XtCreateWidget("message", labelWidgetClass, formWidget,
2163                      messageArgs, XtNumber(messageArgs));
2164     XtSetArg(args[0], XtNtop,    XtChainTop);
2165     XtSetArg(args[1], XtNbottom, XtChainTop);
2166     XtSetValues(messageWidget, args, 2);
2167
2168     widgetList[j++] = boardWidget =
2169       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2170                      XtNumber(boardArgs));
2171
2172     XtManageChildren(widgetList, j);
2173
2174     timerWidth = (boardWidth - sep) / 2;
2175     XtSetArg(args[0], XtNwidth, timerWidth);
2176     XtSetValues(whiteTimerWidget, args, 1);
2177     XtSetValues(blackTimerWidget, args, 1);
2178
2179     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2180     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2181     XtGetValues(whiteTimerWidget, args, 2);
2182
2183     if (appData.showButtonBar) {
2184       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2185       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2186       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2187     }
2188
2189     /*
2190      * formWidget uses these constraints but they are stored
2191      * in the children.
2192      */
2193     i = 0;
2194     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2195     XtSetValues(menuBarWidget, args, i);
2196     if (appData.titleInWindow) {
2197         if (smallLayout) {
2198             i = 0;
2199             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2200             XtSetValues(whiteTimerWidget, args, i);
2201             i = 0;
2202             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2203             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2204             XtSetValues(blackTimerWidget, args, i);
2205             i = 0;
2206             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2207             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2208             XtSetValues(titleWidget, args, i);
2209             i = 0;
2210             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2211             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2212             XtSetValues(messageWidget, args, i);
2213             if (appData.showButtonBar) {
2214               i = 0;
2215               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2216               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2217               XtSetValues(buttonBarWidget, args, i);
2218             }
2219         } else {
2220             i = 0;
2221             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2222             XtSetValues(whiteTimerWidget, args, i);
2223             i = 0;
2224             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2225             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2226             XtSetValues(blackTimerWidget, args, i);
2227             i = 0;
2228             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2229             XtSetValues(titleWidget, args, i);
2230             i = 0;
2231             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2232             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2233             XtSetValues(messageWidget, args, i);
2234             if (appData.showButtonBar) {
2235               i = 0;
2236               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2237               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2238               XtSetValues(buttonBarWidget, args, i);
2239             }
2240         }
2241     } else {
2242         i = 0;
2243         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2244         XtSetValues(whiteTimerWidget, args, i);
2245         i = 0;
2246         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2247         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2248         XtSetValues(blackTimerWidget, args, i);
2249         i = 0;
2250         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2251         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2252         XtSetValues(messageWidget, args, i);
2253         if (appData.showButtonBar) {
2254           i = 0;
2255           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2256           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2257           XtSetValues(buttonBarWidget, args, i);
2258         }
2259     }
2260     i = 0;
2261     XtSetArg(args[0], XtNfromVert, messageWidget);
2262     XtSetArg(args[1], XtNtop,    XtChainTop);
2263     XtSetArg(args[2], XtNbottom, XtChainBottom);
2264     XtSetArg(args[3], XtNleft,   XtChainLeft);
2265     XtSetArg(args[4], XtNright,  XtChainRight);
2266     XtSetValues(boardWidget, args, 5);
2267
2268     XtRealizeWidget(shellWidget);
2269
2270     if(wpMain.x > 0) {
2271       XtSetArg(args[0], XtNx, wpMain.x);
2272       XtSetArg(args[1], XtNy, wpMain.y);
2273       XtSetValues(shellWidget, args, 2);
2274     }
2275
2276     /*
2277      * Correct the width of the message and title widgets.
2278      * It is not known why some systems need the extra fudge term.
2279      * The value "2" is probably larger than needed.
2280      */
2281     XawFormDoLayout(formWidget, False);
2282
2283 #define WIDTH_FUDGE 2
2284     i = 0;
2285     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2286     XtSetArg(args[i], XtNheight, &h);  i++;
2287     XtGetValues(messageWidget, args, i);
2288     if (appData.showButtonBar) {
2289       i = 0;
2290       XtSetArg(args[i], XtNwidth, &w);  i++;
2291       XtGetValues(buttonBarWidget, args, i);
2292       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2293     } else {
2294       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2295     }
2296
2297     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2298     if (gres != XtGeometryYes && appData.debugMode) {
2299       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2300               programName, gres, w, h, wr, hr);
2301     }
2302
2303     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2304     /* The size used for the child widget in layout lags one resize behind
2305        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2306     w--;
2307     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2308     if (gres != XtGeometryYes && appData.debugMode) {
2309       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2310               programName, gres, w, h, wr, hr);
2311     }
2312     /* !! end hack */
2313     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2314     XtSetArg(args[1], XtNright, XtChainRight);
2315     XtSetValues(messageWidget, args, 2);
2316
2317     if (appData.titleInWindow) {
2318         i = 0;
2319         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2320         XtSetArg(args[i], XtNheight, &h);  i++;
2321         XtGetValues(titleWidget, args, i);
2322         if (smallLayout) {
2323             w = boardWidth - 2*bor;
2324         } else {
2325             XtSetArg(args[0], XtNwidth, &w);
2326             XtGetValues(menuBarWidget, args, 1);
2327             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2328         }
2329
2330         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2331         if (gres != XtGeometryYes && appData.debugMode) {
2332             fprintf(stderr,
2333                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2334                     programName, gres, w, h, wr, hr);
2335         }
2336     }
2337     XawFormDoLayout(formWidget, True);
2338
2339     xBoardWindow = XtWindow(boardWidget);
2340
2341     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2342     //       not need to go into InitDrawingSizes().
2343 #endif
2344
2345     /*
2346      * Create X checkmark bitmap and initialize option menu checks.
2347      */
2348     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2349                checkmark_bits, checkmark_width, checkmark_height);
2350     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2351     if (appData.alwaysPromoteToQueen) {
2352         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2353                     args, 1);
2354     }
2355     if (appData.animateDragging) {
2356         XtSetValues(XtNameToWidget(menuBarWidget,
2357                                    "menuOptions.Animate Dragging"),
2358                     args, 1);
2359     }
2360     if (appData.animate) {
2361         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2362                     args, 1);
2363     }
2364     if (appData.autoComment) {
2365         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2366                     args, 1);
2367     }
2368     if (appData.autoCallFlag) {
2369         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2370                     args, 1);
2371     }
2372     if (appData.autoFlipView) {
2373         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2374                     args, 1);
2375     }
2376     if (appData.autoObserve) {
2377         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2378                     args, 1);
2379     }
2380     if (appData.autoRaiseBoard) {
2381         XtSetValues(XtNameToWidget(menuBarWidget,
2382                                    "menuOptions.Auto Raise Board"), args, 1);
2383     }
2384     if (appData.autoSaveGames) {
2385         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2386                     args, 1);
2387     }
2388     if (appData.saveGameFile[0] != NULLCHAR) {
2389         /* Can't turn this off from menu */
2390         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2391                     args, 1);
2392         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2393                        False);
2394
2395     }
2396     if (appData.blindfold) {
2397         XtSetValues(XtNameToWidget(menuBarWidget,
2398                                    "menuOptions.Blindfold"), args, 1);
2399     }
2400     if (appData.flashCount > 0) {
2401         XtSetValues(XtNameToWidget(menuBarWidget,
2402                                    "menuOptions.Flash Moves"),
2403                     args, 1);
2404     }
2405     if (appData.getMoveList) {
2406         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2407                     args, 1);
2408     }
2409 #if HIGHDRAG
2410     if (appData.highlightDragging) {
2411         XtSetValues(XtNameToWidget(menuBarWidget,
2412                                    "menuOptions.Highlight Dragging"),
2413                     args, 1);
2414     }
2415 #endif
2416     if (appData.highlightLastMove) {
2417         XtSetValues(XtNameToWidget(menuBarWidget,
2418                                    "menuOptions.Highlight Last Move"),
2419                     args, 1);
2420     }
2421     if (appData.icsAlarm) {
2422         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2423                     args, 1);
2424     }
2425     if (appData.ringBellAfterMoves) {
2426         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2427                     args, 1);
2428     }
2429     if (appData.oldSaveStyle) {
2430         XtSetValues(XtNameToWidget(menuBarWidget,
2431                                    "menuOptions.Old Save Style"), args, 1);
2432     }
2433     if (appData.periodicUpdates) {
2434         XtSetValues(XtNameToWidget(menuBarWidget,
2435                                    "menuOptions.Periodic Updates"), args, 1);
2436     }
2437     if (appData.ponderNextMove) {
2438         XtSetValues(XtNameToWidget(menuBarWidget,
2439                                    "menuOptions.Ponder Next Move"), args, 1);
2440     }
2441     if (appData.popupExitMessage) {
2442         XtSetValues(XtNameToWidget(menuBarWidget,
2443                                    "menuOptions.Popup Exit Message"), args, 1);
2444     }
2445     if (appData.popupMoveErrors) {
2446         XtSetValues(XtNameToWidget(menuBarWidget,
2447                                    "menuOptions.Popup Move Errors"), args, 1);
2448     }
2449     if (appData.premove) {
2450         XtSetValues(XtNameToWidget(menuBarWidget,
2451                                    "menuOptions.Premove"), args, 1);
2452     }
2453     if (appData.quietPlay) {
2454         XtSetValues(XtNameToWidget(menuBarWidget,
2455                                    "menuOptions.Quiet Play"), args, 1);
2456     }
2457     if (appData.showCoords) {
2458         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2459                     args, 1);
2460     }
2461     if (appData.hideThinkingFromHuman) {
2462         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2463                     args, 1);
2464     }
2465     if (appData.testLegality) {
2466         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2467                     args, 1);
2468     }
2469     if (saveSettingsOnExit) {
2470         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2471                     args, 1);
2472     }
2473
2474     /*
2475      * Create an icon.
2476      */
2477     ReadBitmap(&wIconPixmap, "icon_white.bm",
2478                icon_white_bits, icon_white_width, icon_white_height);
2479     ReadBitmap(&bIconPixmap, "icon_black.bm",
2480                icon_black_bits, icon_black_width, icon_black_height);
2481     iconPixmap = wIconPixmap;
2482     i = 0;
2483     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2484     XtSetValues(shellWidget, args, i);
2485
2486     /*
2487      * Create a cursor for the board widget.
2488      */
2489     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2490     XChangeWindowAttributes(xDisplay, xBoardWindow,
2491                             CWCursor, &window_attributes);
2492
2493     /*
2494      * Inhibit shell resizing.
2495      */
2496     shellArgs[0].value = (XtArgVal) &w;
2497     shellArgs[1].value = (XtArgVal) &h;
2498     XtGetValues(shellWidget, shellArgs, 2);
2499     shellArgs[4].value = shellArgs[2].value = w;
2500     shellArgs[5].value = shellArgs[3].value = h;
2501     XtSetValues(shellWidget, &shellArgs[2], 4);
2502     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2503     marginH =  h - boardHeight;
2504
2505     CatchDeleteWindow(shellWidget, "QuitProc");
2506
2507     CreateGCs();
2508     CreateGrid();
2509 #if HAVE_LIBXPM
2510     if (appData.bitmapDirectory[0] != NULLCHAR) {
2511       CreatePieces();
2512     } else {
2513       CreateXPMPieces();
2514     }
2515 #else
2516     CreateXIMPieces();
2517     /* Create regular pieces */
2518     if (!useImages) CreatePieces();
2519 #endif
2520
2521     CreatePieceMenus();
2522
2523     if (appData.animate || appData.animateDragging)
2524       CreateAnimVars();
2525
2526     XtAugmentTranslations(formWidget,
2527                           XtParseTranslationTable(globalTranslations));
2528     XtAugmentTranslations(boardWidget,
2529                           XtParseTranslationTable(boardTranslations));
2530     XtAugmentTranslations(whiteTimerWidget,
2531                           XtParseTranslationTable(whiteTranslations));
2532     XtAugmentTranslations(blackTimerWidget,
2533                           XtParseTranslationTable(blackTranslations));
2534
2535     /* Why is the following needed on some versions of X instead
2536      * of a translation? */
2537     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2538                       (XtEventHandler) EventProc, NULL);
2539     /* end why */
2540
2541     /* [AS] Restore layout */
2542     if( wpMoveHistory.visible ) {
2543       HistoryPopUp();
2544     }
2545
2546     if( wpEvalGraph.visible ) 
2547       {
2548         EvalGraphPopUp();
2549       };
2550     
2551     if( wpEngineOutput.visible ) {
2552       EngineOutputPopUp();
2553     }
2554
2555     InitBackEnd2();
2556
2557     if (errorExitStatus == -1) {
2558         if (appData.icsActive) {
2559             /* We now wait until we see "login:" from the ICS before
2560                sending the logon script (problems with timestamp otherwise) */
2561             /*ICSInitScript();*/
2562             if (appData.icsInputBox) ICSInputBoxPopUp();
2563         }
2564
2565     #ifdef SIGWINCH
2566     signal(SIGWINCH, TermSizeSigHandler);
2567     #endif
2568         signal(SIGINT, IntSigHandler);
2569         signal(SIGTERM, IntSigHandler);
2570         if (*appData.cmailGameName != NULLCHAR) {
2571             signal(SIGUSR1, CmailSigHandler);
2572         }
2573     }
2574     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2575     InitPosition(TRUE);
2576     XtSetKeyboardFocus(shellWidget, formWidget);
2577
2578     XtAppMainLoop(appContext);
2579     if (appData.debugMode) fclose(debugFP); // [DM] debug
2580     return 0;
2581 }
2582
2583 void
2584 ShutDownFrontEnd()
2585 {
2586     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2587         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2588     }
2589     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2590     unlink(gameCopyFilename);
2591     unlink(gamePasteFilename);
2592 }
2593
2594 RETSIGTYPE TermSizeSigHandler(int sig)
2595 {
2596     update_ics_width();
2597 }
2598
2599 RETSIGTYPE
2600 IntSigHandler(sig)
2601      int sig;
2602 {
2603     ExitEvent(sig);
2604 }
2605
2606 RETSIGTYPE
2607 CmailSigHandler(sig)
2608      int sig;
2609 {
2610     int dummy = 0;
2611     int error;
2612
2613     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2614
2615     /* Activate call-back function CmailSigHandlerCallBack()             */
2616     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2617
2618     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2619 }
2620
2621 void
2622 CmailSigHandlerCallBack(isr, closure, message, count, error)
2623      InputSourceRef isr;
2624      VOIDSTAR closure;
2625      char *message;
2626      int count;
2627      int error;
2628 {
2629     BoardToTop();
2630     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2631 }
2632 /**** end signal code ****/
2633
2634
2635 void
2636 ICSInitScript()
2637 {
2638     FILE *f;
2639     char buf[MSG_SIZ];
2640     char *p;
2641
2642     f = fopen(appData.icsLogon, "r");
2643     if (f == NULL) {
2644         p = getenv("HOME");
2645         if (p != NULL) {
2646             strcpy(buf, p);
2647             strcat(buf, "/");
2648             strcat(buf, appData.icsLogon);
2649             f = fopen(buf, "r");
2650         }
2651     }
2652     if (f != NULL)
2653       ProcessICSInitScript(f);
2654 }
2655
2656 void
2657 ResetFrontEnd()
2658 {
2659     CommentPopDown();
2660     EditCommentPopDown();
2661     TagsPopDown();
2662     return;
2663 }
2664
2665 typedef struct {
2666     char *name;
2667     Boolean value;
2668 } Enables;
2669
2670 void
2671 GreyRevert(grey)
2672      Boolean grey;
2673 {
2674     Widget w;
2675     if (!menuBarWidget) return;
2676     w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2677     if (w == NULL) {
2678       DisplayError("menuStep.Revert", 0);
2679     } else {
2680       XtSetSensitive(w, !grey);
2681     }
2682     w = XtNameToWidget(menuBarWidget, "menuStep.Annotate");
2683     if (w == NULL) {
2684       DisplayError("menuStep.Annotate", 0);
2685     } else {
2686       XtSetSensitive(w, !grey);
2687     }
2688 }
2689
2690 void
2691 SetMenuEnables(enab)
2692      Enables *enab;
2693 {
2694   Widget w;
2695   if (!menuBarWidget) return;
2696   while (enab->name != NULL) {
2697     w = XtNameToWidget(menuBarWidget, enab->name);
2698     if (w == NULL) {
2699       DisplayError(enab->name, 0);
2700     } else {
2701       XtSetSensitive(w, enab->value);
2702     }
2703     enab++;
2704   }
2705 }
2706
2707 Enables icsEnables[] = {
2708     { "menuFile.Mail Move", False },
2709     { "menuFile.Reload CMail Message", False },
2710     { "menuMode.Machine Black", False },
2711     { "menuMode.Machine White", False },
2712     { "menuMode.Analysis Mode", False },
2713     { "menuMode.Analyze File", False },
2714     { "menuMode.Two Machines", False },
2715 #ifndef ZIPPY
2716     { "menuHelp.Hint", False },
2717     { "menuHelp.Book", False },
2718     { "menuStep.Move Now", False },
2719     { "menuOptions.Periodic Updates", False },
2720     { "menuOptions.Hide Thinking", False },
2721     { "menuOptions.Ponder Next Move", False },
2722 #endif
2723     { "menuStep.Annotate", False },
2724     { NULL, False }
2725 };
2726
2727 Enables ncpEnables[] = {
2728     { "menuFile.Mail Move", False },
2729     { "menuFile.Reload CMail Message", False },
2730     { "menuMode.Machine White", False },
2731     { "menuMode.Machine Black", False },
2732     { "menuMode.Analysis Mode", False },
2733     { "menuMode.Analyze File", False },
2734     { "menuMode.Two Machines", False },
2735     { "menuMode.ICS Client", False },
2736     { "menuMode.ICS Input Box", False },
2737     { "Action", False },
2738     { "menuStep.Revert", False },
2739     { "menuStep.Annotate", False },
2740     { "menuStep.Move Now", False },
2741     { "menuStep.Retract Move", False },
2742     { "menuOptions.Auto Comment", False },
2743     { "menuOptions.Auto Flag", False },
2744     { "menuOptions.Auto Flip View", False },
2745     { "menuOptions.Auto Observe", False },
2746     { "menuOptions.Auto Raise Board", False },
2747     { "menuOptions.Get Move List", False },
2748     { "menuOptions.ICS Alarm", False },
2749     { "menuOptions.Move Sound", False },
2750     { "menuOptions.Quiet Play", False },
2751     { "menuOptions.Hide Thinking", False },
2752     { "menuOptions.Periodic Updates", False },
2753     { "menuOptions.Ponder Next Move", False },
2754     { "menuHelp.Hint", False },
2755     { "menuHelp.Book", False },
2756     { NULL, False }
2757 };
2758
2759 Enables gnuEnables[] = {
2760     { "menuMode.ICS Client", False },
2761     { "menuMode.ICS Input Box", False },
2762     { "menuAction.Accept", False },
2763     { "menuAction.Decline", False },
2764     { "menuAction.Rematch", False },
2765     { "menuAction.Adjourn", False },
2766     { "menuAction.Stop Examining", False },
2767     { "menuAction.Stop Observing", False },
2768     { "menuAction.Upload to Examine", False },
2769     { "menuStep.Revert", False },
2770     { "menuStep.Annotate", False },
2771     { "menuOptions.Auto Comment", False },
2772     { "menuOptions.Auto Observe", False },
2773     { "menuOptions.Auto Raise Board", False },
2774     { "menuOptions.Get Move List", False },
2775     { "menuOptions.Premove", False },
2776     { "menuOptions.Quiet Play", False },
2777
2778     /* The next two options rely on SetCmailMode being called *after*    */
2779     /* SetGNUMode so that when GNU is being used to give hints these     */
2780     /* menu options are still available                                  */
2781
2782     { "menuFile.Mail Move", False },
2783     { "menuFile.Reload CMail Message", False },
2784     { NULL, False }
2785 };
2786
2787 Enables cmailEnables[] = {
2788     { "Action", True },
2789     { "menuAction.Call Flag", False },
2790     { "menuAction.Draw", True },
2791     { "menuAction.Adjourn", False },
2792     { "menuAction.Abort", False },
2793     { "menuAction.Stop Observing", False },
2794     { "menuAction.Stop Examining", False },
2795     { "menuFile.Mail Move", True },
2796     { "menuFile.Reload CMail Message", True },
2797     { NULL, False }
2798 };
2799
2800 Enables trainingOnEnables[] = {
2801   { "menuMode.Edit Comment", False },
2802   { "menuMode.Pause", False },
2803   { "menuStep.Forward", False },
2804   { "menuStep.Backward", False },
2805   { "menuStep.Forward to End", False },
2806   { "menuStep.Back to Start", False },
2807   { "menuStep.Move Now", False },
2808   { "menuStep.Truncate Game", False },
2809   { NULL, False }
2810 };
2811
2812 Enables trainingOffEnables[] = {
2813   { "menuMode.Edit Comment", True },
2814   { "menuMode.Pause", True },
2815   { "menuStep.Forward", True },
2816   { "menuStep.Backward", True },
2817   { "menuStep.Forward to End", True },
2818   { "menuStep.Back to Start", True },
2819   { "menuStep.Move Now", True },
2820   { "menuStep.Truncate Game", True },
2821   { NULL, False }
2822 };
2823
2824 Enables machineThinkingEnables[] = {
2825   { "menuFile.Load Game", False },
2826   { "menuFile.Load Next Game", False },
2827   { "menuFile.Load Previous Game", False },
2828   { "menuFile.Reload Same Game", False },
2829   { "menuFile.Paste Game", False },
2830   { "menuFile.Load Position", False },
2831   { "menuFile.Load Next Position", False },
2832   { "menuFile.Load Previous Position", False },
2833   { "menuFile.Reload Same Position", False },
2834   { "menuFile.Paste Position", False },
2835   { "menuMode.Machine White", False },
2836   { "menuMode.Machine Black", False },
2837   { "menuMode.Two Machines", False },
2838   { "menuStep.Retract Move", False },
2839   { NULL, False }
2840 };
2841
2842 Enables userThinkingEnables[] = {
2843   { "menuFile.Load Game", True },
2844   { "menuFile.Load Next Game", True },
2845   { "menuFile.Load Previous Game", True },
2846   { "menuFile.Reload Same Game", True },
2847   { "menuFile.Paste Game", True },
2848   { "menuFile.Load Position", True },
2849   { "menuFile.Load Next Position", True },
2850   { "menuFile.Load Previous Position", True },
2851   { "menuFile.Reload Same Position", True },
2852   { "menuFile.Paste Position", True },
2853   { "menuMode.Machine White", True },
2854   { "menuMode.Machine Black", True },
2855   { "menuMode.Two Machines", True },
2856   { "menuStep.Retract Move", True },
2857   { NULL, False }
2858 };
2859
2860 void SetICSMode()
2861 {
2862   SetMenuEnables(icsEnables);
2863
2864 #ifdef ZIPPY
2865   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2866      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2867 #endif
2868 }
2869
2870 void
2871 SetNCPMode()
2872 {
2873   SetMenuEnables(ncpEnables);
2874 }
2875
2876 void
2877 SetGNUMode()
2878 {
2879   SetMenuEnables(gnuEnables);
2880 }
2881
2882 void
2883 SetCmailMode()
2884 {
2885   SetMenuEnables(cmailEnables);
2886 }
2887
2888 void
2889 SetTrainingModeOn()
2890 {
2891   SetMenuEnables(trainingOnEnables);
2892   if (appData.showButtonBar) {
2893     XtSetSensitive(buttonBarWidget, False);
2894   }
2895   CommentPopDown();
2896 }
2897
2898 void
2899 SetTrainingModeOff()
2900 {
2901   SetMenuEnables(trainingOffEnables);
2902   if (appData.showButtonBar) {
2903     XtSetSensitive(buttonBarWidget, True);
2904   }
2905 }
2906
2907 void
2908 SetUserThinkingEnables()
2909 {
2910   if (appData.noChessProgram) return;
2911   SetMenuEnables(userThinkingEnables);
2912 }
2913
2914 void
2915 SetMachineThinkingEnables()
2916 {
2917   if (appData.noChessProgram) return;
2918   SetMenuEnables(machineThinkingEnables);
2919   switch (gameMode) {
2920   case MachinePlaysBlack:
2921   case MachinePlaysWhite:
2922   case TwoMachinesPlay:
2923     XtSetSensitive(XtNameToWidget(menuBarWidget,
2924                                   ModeToWidgetName(gameMode)), True);
2925     break;
2926   default:
2927     break;
2928   }
2929 }
2930
2931 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2932 #define HISTORY_SIZE 64\r
2933 static char *history[HISTORY_SIZE];\r
2934 int histIn = 0, histP = 0;\r
2935 \r
2936 void\r
2937 SaveInHistory(char *cmd)\r
2938 {\r
2939   if (history[histIn] != NULL) {\r
2940     free(history[histIn]);\r
2941     history[histIn] = NULL;\r
2942   }\r
2943   if (*cmd == NULLCHAR) return;\r
2944   history[histIn] = StrSave(cmd);\r
2945   histIn = (histIn + 1) % HISTORY_SIZE;\r
2946   if (history[histIn] != NULL) {\r
2947     free(history[histIn]);\r
2948     history[histIn] = NULL;\r
2949   }\r
2950   histP = histIn;\r
2951 }\r
2952 \r
2953 char *\r
2954 PrevInHistory(char *cmd)\r
2955 {\r
2956   int newhp;\r
2957   if (histP == histIn) {\r
2958     if (history[histIn] != NULL) free(history[histIn]);\r
2959     history[histIn] = StrSave(cmd);\r
2960   }\r
2961   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
2962   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
2963   histP = newhp;\r
2964   return history[histP];\r
2965 }\r
2966 \r
2967 char *\r
2968 NextInHistory()\r
2969 {\r
2970   if (histP == histIn) return NULL;\r
2971   histP = (histP + 1) % HISTORY_SIZE;\r
2972   return history[histP];   \r
2973 }
2974 // end of borrowed code\r
2975 \r
2976 #define Abs(n) ((n)<0 ? -(n) : (n))
2977
2978 /*
2979  * Find a font that matches "pattern" that is as close as
2980  * possible to the targetPxlSize.  Prefer fonts that are k
2981  * pixels smaller to fonts that are k pixels larger.  The
2982  * pattern must be in the X Consortium standard format,
2983  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2984  * The return value should be freed with XtFree when no
2985  * longer needed.
2986  */
2987 char *FindFont(pattern, targetPxlSize)
2988      char *pattern;
2989      int targetPxlSize;
2990 {
2991     char **fonts, *p, *best, *scalable, *scalableTail;
2992     int i, j, nfonts, minerr, err, pxlSize;
2993
2994 #ifdef ENABLE_NLS
2995     char **missing_list;
2996     int missing_count;
2997     char *def_string, *base_fnt_lst, strInt[3];
2998     XFontSet fntSet;
2999     XFontStruct **fnt_list;
3000
3001     base_fnt_lst = calloc(1, strlen(pattern) + 3);
3002     sprintf(strInt, "%d", targetPxlSize);
3003     p = strstr(pattern, "--");
3004     strncpy(base_fnt_lst, pattern, p - pattern + 2);
3005     strcat(base_fnt_lst, strInt);
3006     strcat(base_fnt_lst, strchr(p + 2, '-'));
3007
3008     if ((fntSet = XCreateFontSet(xDisplay,
3009                                  base_fnt_lst,
3010                                  &missing_list,
3011                                  &missing_count,
3012                                  &def_string)) == NULL) {
3013
3014        fprintf(stderr, _("Unable to create font set.\n"));
3015        exit (2);
3016     }
3017
3018     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3019 #else
3020     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3021     if (nfonts < 1) {
3022         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3023                 programName, pattern);
3024         exit(2);
3025     }
3026 #endif
3027
3028     best = fonts[0];
3029     scalable = NULL;
3030     minerr = 999999;
3031     for (i=0; i<nfonts; i++) {
3032         j = 0;
3033         p = fonts[i];
3034         if (*p != '-') continue;
3035         while (j < 7) {
3036             if (*p == NULLCHAR) break;
3037             if (*p++ == '-') j++;
3038         }
3039         if (j < 7) continue;
3040         pxlSize = atoi(p);
3041         if (pxlSize == 0) {
3042             scalable = fonts[i];
3043             scalableTail = p;
3044         } else {
3045             err = pxlSize - targetPxlSize;
3046             if (Abs(err) < Abs(minerr) ||
3047                 (minerr > 0 && err < 0 && -err == minerr)) {
3048                 best = fonts[i];
3049                 minerr = err;
3050             }
3051         }
3052     }
3053     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3054         /* If the error is too big and there is a scalable font,
3055            use the scalable font. */
3056         int headlen = scalableTail - scalable;
3057         p = (char *) XtMalloc(strlen(scalable) + 10);
3058         while (isdigit(*scalableTail)) scalableTail++;
3059         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3060     } else {
3061         p = (char *) XtMalloc(strlen(best) + 1);
3062         strcpy(p, best);
3063     }
3064     if (appData.debugMode) {
3065         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
3066                 pattern, targetPxlSize, p);
3067     }
3068 #ifdef ENABLE_NLS
3069     if (missing_count > 0)
3070        XFreeStringList(missing_list);
3071     XFreeFontSet(xDisplay, fntSet);
3072 #else
3073      XFreeFontNames(fonts);
3074 #endif
3075     return p;
3076 }
3077
3078 void CreateGCs()
3079 {
3080     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3081       | GCBackground | GCFunction | GCPlaneMask;
3082     XGCValues gc_values;
3083     GC copyInvertedGC;
3084
3085     gc_values.plane_mask = AllPlanes;
3086     gc_values.line_width = lineGap;
3087     gc_values.line_style = LineSolid;
3088     gc_values.function = GXcopy;
3089
3090     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3091     gc_values.background = XBlackPixel(xDisplay, xScreen);
3092     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3093
3094     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3095     gc_values.background = XWhitePixel(xDisplay, xScreen);
3096     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3097     XSetFont(xDisplay, coordGC, coordFontID);
3098
3099     // [HGM] make font for holdings counts (white on black0
3100     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3101     gc_values.background = XBlackPixel(xDisplay, xScreen);
3102     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3103     XSetFont(xDisplay, countGC, countFontID);
3104
3105     if (appData.monoMode) {
3106         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3107         gc_values.background = XWhitePixel(xDisplay, xScreen);
3108         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3109
3110         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3111         gc_values.background = XBlackPixel(xDisplay, xScreen);
3112         lightSquareGC = wbPieceGC
3113           = XtGetGC(shellWidget, value_mask, &gc_values);
3114
3115         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3116         gc_values.background = XWhitePixel(xDisplay, xScreen);
3117         darkSquareGC = bwPieceGC
3118           = XtGetGC(shellWidget, value_mask, &gc_values);
3119
3120         if (DefaultDepth(xDisplay, xScreen) == 1) {
3121             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3122             gc_values.function = GXcopyInverted;
3123             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3124             gc_values.function = GXcopy;
3125             if (XBlackPixel(xDisplay, xScreen) == 1) {
3126                 bwPieceGC = darkSquareGC;
3127                 wbPieceGC = copyInvertedGC;
3128             } else {
3129                 bwPieceGC = copyInvertedGC;
3130                 wbPieceGC = lightSquareGC;
3131             }
3132         }
3133     } else {
3134         gc_values.foreground = highlightSquareColor;
3135         gc_values.background = highlightSquareColor;
3136         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3137
3138         gc_values.foreground = premoveHighlightColor;
3139         gc_values.background = premoveHighlightColor;
3140         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3141
3142         gc_values.foreground = lightSquareColor;
3143         gc_values.background = darkSquareColor;
3144         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3145
3146         gc_values.foreground = darkSquareColor;
3147         gc_values.background = lightSquareColor;
3148         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3149
3150         gc_values.foreground = jailSquareColor;
3151         gc_values.background = jailSquareColor;
3152         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3153
3154         gc_values.foreground = whitePieceColor;
3155         gc_values.background = darkSquareColor;
3156         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3157
3158         gc_values.foreground = whitePieceColor;
3159         gc_values.background = lightSquareColor;
3160         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3161
3162         gc_values.foreground = whitePieceColor;
3163         gc_values.background = jailSquareColor;
3164         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3165
3166         gc_values.foreground = blackPieceColor;
3167         gc_values.background = darkSquareColor;
3168         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3169
3170         gc_values.foreground = blackPieceColor;
3171         gc_values.background = lightSquareColor;
3172         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3173
3174         gc_values.foreground = blackPieceColor;
3175         gc_values.background = jailSquareColor;
3176         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3177     }
3178 }
3179
3180 void loadXIM(xim, xmask, filename, dest, mask)
3181      XImage *xim;
3182      XImage *xmask;
3183      char *filename;
3184      Pixmap *dest;
3185      Pixmap *mask;
3186 {
3187     int x, y, w, h, p;
3188     FILE *fp;
3189     Pixmap temp;
3190     XGCValues   values;
3191     GC maskGC;
3192
3193     fp = fopen(filename, "rb");
3194     if (!fp) {
3195         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3196         exit(1);
3197     }
3198
3199     w = fgetc(fp);
3200     h = fgetc(fp);
3201
3202     for (y=0; y<h; ++y) {
3203         for (x=0; x<h; ++x) {
3204             p = fgetc(fp);
3205
3206             switch (p) {
3207               case 0:
3208                 XPutPixel(xim, x, y, blackPieceColor);
3209                 if (xmask)
3210                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3211                 break;
3212               case 1:
3213                 XPutPixel(xim, x, y, darkSquareColor);
3214                 if (xmask)
3215                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3216                 break;
3217               case 2:
3218                 XPutPixel(xim, x, y, whitePieceColor);
3219                 if (xmask)
3220                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3221                 break;
3222               case 3:
3223                 XPutPixel(xim, x, y, lightSquareColor);
3224                 if (xmask)
3225                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3226                 break;
3227             }
3228         }
3229     }
3230
3231     /* create Pixmap of piece */
3232     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3233                           w, h, xim->depth);
3234     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3235               0, 0, 0, 0, w, h);
3236
3237     /* create Pixmap of clipmask
3238        Note: We assume the white/black pieces have the same
3239              outline, so we make only 6 masks. This is okay
3240              since the XPM clipmask routines do the same. */
3241     if (xmask) {
3242       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3243                             w, h, xim->depth);
3244       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3245               0, 0, 0, 0, w, h);
3246
3247       /* now create the 1-bit version */
3248       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3249                           w, h, 1);
3250
3251       values.foreground = 1;
3252       values.background = 0;
3253
3254       /* Don't use XtGetGC, not read only */
3255       maskGC = XCreateGC(xDisplay, *mask,
3256                     GCForeground | GCBackground, &values);
3257       XCopyPlane(xDisplay, temp, *mask, maskGC,
3258                   0, 0, squareSize, squareSize, 0, 0, 1);
3259       XFreePixmap(xDisplay, temp);
3260     }
3261 }
3262
3263
3264 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3265
3266 void CreateXIMPieces()
3267 {
3268     int piece, kind;
3269     char buf[MSG_SIZ];
3270     u_int ss;
3271     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3272     XImage *ximtemp;
3273
3274     ss = squareSize;
3275
3276     /* The XSynchronize calls were copied from CreatePieces.
3277        Not sure if needed, but can't hurt */
3278     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3279                                      buffering bug */
3280
3281     /* temp needed by loadXIM() */
3282     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3283                  0, 0, ss, ss, AllPlanes, XYPixmap);
3284
3285     if (strlen(appData.pixmapDirectory) == 0) {
3286       useImages = 0;
3287     } else {
3288         useImages = 1;
3289         if (appData.monoMode) {
3290           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3291                             0, 2);
3292           ExitEvent(2);
3293         }
3294         fprintf(stderr, _("\nLoading XIMs...\n"));
3295         /* Load pieces */
3296         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3297             fprintf(stderr, "%d", piece+1);
3298             for (kind=0; kind<4; kind++) {
3299                 fprintf(stderr, ".");
3300                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3301                         ExpandPathName(appData.pixmapDirectory),
3302                         piece <= (int) WhiteKing ? "" : "w",
3303                         pieceBitmapNames[piece],
3304                         ximkind[kind], ss);
3305                 ximPieceBitmap[kind][piece] =
3306                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3307                             0, 0, ss, ss, AllPlanes, XYPixmap);
3308                 if (appData.debugMode)
3309                   fprintf(stderr, _("(File:%s:) "), buf);
3310                 loadXIM(ximPieceBitmap[kind][piece],
3311                         ximtemp, buf,
3312                         &(xpmPieceBitmap2[kind][piece]),
3313                         &(ximMaskPm2[piece]));
3314                 if(piece <= (int)WhiteKing)
3315                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3316             }
3317             fprintf(stderr," ");
3318         }
3319         /* Load light and dark squares */
3320         /* If the LSQ and DSQ pieces don't exist, we will
3321            draw them with solid squares. */
3322         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3323         if (access(buf, 0) != 0) {
3324             useImageSqs = 0;
3325         } else {
3326             useImageSqs = 1;
3327             fprintf(stderr, _("light square "));
3328             ximLightSquare=
3329               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3330                         0, 0, ss, ss, AllPlanes, XYPixmap);
3331             if (appData.debugMode)
3332               fprintf(stderr, _("(File:%s:) "), buf);
3333
3334             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3335             fprintf(stderr, _("dark square "));
3336             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3337                     ExpandPathName(appData.pixmapDirectory), ss);
3338             if (appData.debugMode)
3339               fprintf(stderr, _("(File:%s:) "), buf);
3340             ximDarkSquare=
3341               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3342                         0, 0, ss, ss, AllPlanes, XYPixmap);
3343             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3344             xpmJailSquare = xpmLightSquare;
3345         }
3346         fprintf(stderr, _("Done.\n"));
3347     }
3348     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3349 }
3350
3351 #if HAVE_LIBXPM
3352 void CreateXPMPieces()
3353 {
3354     int piece, kind, r;
3355     char buf[MSG_SIZ];
3356     u_int ss = squareSize;
3357     XpmAttributes attr;
3358     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3359     XpmColorSymbol symbols[4];
3360
3361     /* The XSynchronize calls were copied from CreatePieces.
3362        Not sure if needed, but can't hurt */
3363     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3364
3365     /* Setup translations so piece colors match square colors */
3366     symbols[0].name = "light_piece";
3367     symbols[0].value = appData.whitePieceColor;
3368     symbols[1].name = "dark_piece";
3369     symbols[1].value = appData.blackPieceColor;
3370     symbols[2].name = "light_square";
3371     symbols[2].value = appData.lightSquareColor;
3372     symbols[3].name = "dark_square";
3373     symbols[3].value = appData.darkSquareColor;
3374
3375     attr.valuemask = XpmColorSymbols;
3376     attr.colorsymbols = symbols;
3377     attr.numsymbols = 4;
3378
3379     if (appData.monoMode) {
3380       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3381                         0, 2);
3382       ExitEvent(2);
3383     }
3384     if (strlen(appData.pixmapDirectory) == 0) {
3385         XpmPieces* pieces = builtInXpms;
3386         useImages = 1;
3387         /* Load pieces */
3388         while (pieces->size != squareSize && pieces->size) pieces++;
3389         if (!pieces->size) {
3390           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3391           exit(1);
3392         }
3393         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3394             for (kind=0; kind<4; kind++) {
3395
3396                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3397                                                pieces->xpm[piece][kind],
3398                                                &(xpmPieceBitmap2[kind][piece]),
3399                                                NULL, &attr)) != 0) {
3400                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3401                           r, buf);
3402                   exit(1);
3403                 }
3404                 if(piece <= (int) WhiteKing)
3405                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3406             }
3407         }
3408         useImageSqs = 0;
3409         xpmJailSquare = xpmLightSquare;
3410     } else {
3411         useImages = 1;
3412
3413         fprintf(stderr, _("\nLoading XPMs...\n"));
3414
3415         /* Load pieces */
3416         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3417             fprintf(stderr, "%d ", piece+1);
3418             for (kind=0; kind<4; kind++) {
3419               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3420                         ExpandPathName(appData.pixmapDirectory),
3421                         piece > (int) WhiteKing ? "w" : "",
3422                         pieceBitmapNames[piece],
3423                         xpmkind[kind], ss);
3424                 if (appData.debugMode) {
3425                     fprintf(stderr, _("(File:%s:) "), buf);
3426                 }
3427                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3428                                            &(xpmPieceBitmap2[kind][piece]),
3429                                            NULL, &attr)) != 0) {
3430                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3431                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3432                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3433                                 ExpandPathName(appData.pixmapDirectory),
3434                                 xpmkind[kind], ss);
3435                         if (appData.debugMode) {
3436                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3437                         }
3438                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3439                                                 &(xpmPieceBitmap2[kind][piece]),
3440                                                 NULL, &attr);
3441                     }
3442                     if (r != 0) {
3443                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3444                                 r, buf);
3445                         exit(1);
3446                     }
3447                 }
3448                 if(piece <= (int) WhiteKing) 
3449                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3450             }
3451         }
3452         /* Load light and dark squares */
3453         /* If the LSQ and DSQ pieces don't exist, we will
3454            draw them with solid squares. */
3455         fprintf(stderr, _("light square "));
3456         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3457         if (access(buf, 0) != 0) {
3458             useImageSqs = 0;
3459         } else {
3460             useImageSqs = 1;
3461             if (appData.debugMode)
3462               fprintf(stderr, _("(File:%s:) "), buf);
3463
3464             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3465                                        &xpmLightSquare, NULL, &attr)) != 0) {
3466                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3467                 exit(1);
3468             }
3469             fprintf(stderr, _("dark square "));
3470             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3471                     ExpandPathName(appData.pixmapDirectory), ss);
3472             if (appData.debugMode) {
3473                 fprintf(stderr, _("(File:%s:) "), buf);
3474             }
3475             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3476                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3477                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3478                 exit(1);
3479             }
3480         }
3481         xpmJailSquare = xpmLightSquare;
3482         fprintf(stderr, _("Done.\n"));
3483     }
3484     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3485                                       buffering bug */
3486 }
3487 #endif /* HAVE_LIBXPM */
3488
3489 #if HAVE_LIBXPM
3490 /* No built-in bitmaps */
3491 void CreatePieces()
3492 {
3493     int piece, kind;
3494     char buf[MSG_SIZ];
3495     u_int ss = squareSize;
3496
3497     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3498                                      buffering bug */
3499
3500     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3501         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3502             sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3503                     pieceBitmapNames[piece],
3504                     ss, kind == SOLID ? 's' : 'o');
3505             ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3506             if(piece <= (int)WhiteKing)
3507                 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3508         }
3509     }
3510
3511     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3512                                       buffering bug */
3513 }
3514 #else
3515 /* With built-in bitmaps */
3516 void CreatePieces()
3517 {
3518     BuiltInBits* bib = builtInBits;
3519     int piece, kind;
3520     char buf[MSG_SIZ];
3521     u_int ss = squareSize;
3522
3523     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3524                                      buffering bug */
3525
3526     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3527
3528     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3529         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3530             sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3531                     pieceBitmapNames[piece],
3532                     ss, kind == SOLID ? 's' : 'o');
3533             ReadBitmap(&pieceBitmap2[kind][piece], buf,
3534                        bib->bits[kind][piece], ss, ss);
3535             if(piece <= (int)WhiteKing)
3536                 pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3537         }
3538     }
3539
3540     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3541                                       buffering bug */
3542 }
3543 #endif
3544
3545 void ReadBitmap(pm, name, bits, wreq, hreq)
3546      Pixmap *pm;
3547      String name;
3548      unsigned char bits[];
3549      u_int wreq, hreq;
3550 {
3551     int x_hot, y_hot;
3552     u_int w, h;
3553     int errcode;
3554     char msg[MSG_SIZ], fullname[MSG_SIZ];
3555
3556     if (*appData.bitmapDirectory != NULLCHAR) {
3557         strcpy(fullname, appData.bitmapDirectory);
3558         strcat(fullname, "/");
3559         strcat(fullname, name);
3560         errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3561                                   &w, &h, pm, &x_hot, &y_hot);
3562     fprintf(stderr, "load %s\n", name);
3563         if (errcode != BitmapSuccess) {
3564             switch (errcode) {
3565               case BitmapOpenFailed:
3566                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3567                 break;
3568               case BitmapFileInvalid:
3569                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3570                 break;
3571               case BitmapNoMemory:
3572                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3573                         fullname);
3574                 break;
3575               default:
3576                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3577                         errcode, fullname);
3578                 break;
3579             }
3580             fprintf(stderr, _("%s: %s...using built-in\n"),
3581                     programName, msg);
3582         } else if (w != wreq || h != hreq) {
3583             fprintf(stderr,
3584                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3585                     programName, fullname, w, h, wreq, hreq);
3586         } else {
3587             return;
3588         }
3589     }
3590     if (bits != NULL) {
3591         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3592                                     wreq, hreq);
3593     }
3594 }
3595
3596 void CreateGrid()
3597 {
3598     int i, j;
3599
3600     if (lineGap == 0) return;
3601
3602     /* [HR] Split this into 2 loops for non-square boards. */
3603
3604     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3605         gridSegments[i].x1 = 0;
3606         gridSegments[i].x2 =
3607           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3608         gridSegments[i].y1 = gridSegments[i].y2
3609           = lineGap / 2 + (i * (squareSize + lineGap));
3610     }
3611
3612     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3613         gridSegments[j + i].y1 = 0;
3614         gridSegments[j + i].y2 =
3615           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3616         gridSegments[j + i].x1 = gridSegments[j + i].x2
3617           = lineGap / 2 + (j * (squareSize + lineGap));
3618     }
3619 }
3620
3621 static void MenuBarSelect(w, addr, index)
3622      Widget w;
3623      caddr_t addr;
3624      caddr_t index;
3625 {
3626     XtActionProc proc = (XtActionProc) addr;
3627
3628     (proc)(NULL, NULL, NULL, NULL);
3629 }
3630
3631 void CreateMenuBarPopup(parent, name, mb)
3632      Widget parent;
3633      String name;
3634      Menu *mb;
3635 {
3636     int j;
3637     Widget menu, entry;
3638     MenuItem *mi;
3639     Arg args[16];
3640
3641     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3642                               parent, NULL, 0);
3643     j = 0;
3644     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3645     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3646     mi = mb->mi;
3647     while (mi->string != NULL) {
3648         if (strcmp(mi->string, "----") == 0) {
3649             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3650                                           menu, args, j);
3651         } else {
3652           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3653             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3654                                           menu, args, j+1);
3655             XtAddCallback(entry, XtNcallback,
3656                           (XtCallbackProc) MenuBarSelect,
3657                           (caddr_t) mi->proc);
3658         }
3659         mi++;
3660     }
3661 }
3662
3663 Widget CreateMenuBar(mb)
3664      Menu *mb;
3665 {
3666     int j;
3667     Widget anchor, menuBar;
3668     Arg args[16];
3669     char menuName[MSG_SIZ];
3670
3671     j = 0;
3672     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3673     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3674     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3675     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3676                              formWidget, args, j);
3677
3678     while (mb->name != NULL) {
3679         strcpy(menuName, "menu");
3680         strcat(menuName, mb->name);
3681         j = 0;
3682         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3683         if (tinyLayout) {
3684             char shortName[2];
3685             shortName[0] = _(mb->name)[0];
3686             shortName[1] = NULLCHAR;
3687             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3688         }
3689       else {
3690           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3691       }
3692
3693         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3694         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3695                                        menuBar, args, j);
3696         CreateMenuBarPopup(menuBar, menuName, mb);
3697         mb++;
3698     }
3699     return menuBar;
3700 }
3701
3702 Widget CreateButtonBar(mi)
3703      MenuItem *mi;
3704 {
3705     int j;
3706     Widget button, buttonBar;
3707     Arg args[16];
3708
3709     j = 0;
3710     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3711     if (tinyLayout) {
3712         XtSetArg(args[j], XtNhSpace, 0); j++;
3713     }
3714     XtSetArg(args[j], XtNborderWidth, 0); j++;
3715     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3716     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3717                                formWidget, args, j);
3718
3719     while (mi->string != NULL) {
3720         j = 0;
3721         if (tinyLayout) {
3722             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3723             XtSetArg(args[j], XtNborderWidth, 0); j++;
3724         }
3725       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3726         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3727                                        buttonBar, args, j);
3728         XtAddCallback(button, XtNcallback,
3729                       (XtCallbackProc) MenuBarSelect,
3730                       (caddr_t) mi->proc);
3731         mi++;
3732     }
3733     return buttonBar;
3734 }
3735
3736 Widget
3737 CreatePieceMenu(name, color)
3738      char *name;
3739      int color;
3740 {
3741     int i;
3742     Widget entry, menu;
3743     Arg args[16];
3744     ChessSquare selection;
3745
3746     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3747                               boardWidget, args, 0);
3748
3749     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3750         String item = pieceMenuStrings[color][i];
3751
3752         if (strcmp(item, "----") == 0) {
3753             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3754                                           menu, NULL, 0);
3755         } else {
3756           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3757             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3758                                 menu, args, 1);
3759             selection = pieceMenuTranslation[color][i];
3760             XtAddCallback(entry, XtNcallback,
3761                           (XtCallbackProc) PieceMenuSelect,
3762                           (caddr_t) selection);
3763             if (selection == WhitePawn || selection == BlackPawn) {
3764                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3765                 XtSetValues(menu, args, 1);
3766             }
3767         }
3768     }
3769     return menu;
3770 }
3771
3772 void
3773 CreatePieceMenus()
3774 {
3775     int i;
3776     Widget entry;
3777     Arg args[16];
3778     ChessSquare selection;
3779
3780     whitePieceMenu = CreatePieceMenu("menuW", 0);
3781     blackPieceMenu = CreatePieceMenu("menuB", 1);
3782
3783     XtRegisterGrabAction(PieceMenuPopup, True,
3784                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3785                          GrabModeAsync, GrabModeAsync);
3786
3787     XtSetArg(args[0], XtNlabel, _("Drop"));
3788     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3789                                   boardWidget, args, 1);
3790     for (i = 0; i < DROP_MENU_SIZE; i++) {
3791         String item = dropMenuStrings[i];
3792
3793         if (strcmp(item, "----") == 0) {
3794             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3795                                           dropMenu, NULL, 0);
3796         } else {
3797           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3798             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3799                                 dropMenu, args, 1);
3800             selection = dropMenuTranslation[i];
3801             XtAddCallback(entry, XtNcallback,
3802                           (XtCallbackProc) DropMenuSelect,
3803                           (caddr_t) selection);
3804         }
3805     }
3806 }
3807
3808 void SetupDropMenu()
3809 {
3810     int i, j, count;
3811     char label[32];
3812     Arg args[16];
3813     Widget entry;
3814     char* p;
3815
3816     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3817         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3818         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3819                    dmEnables[i].piece);
3820         XtSetSensitive(entry, p != NULL || !appData.testLegality
3821                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3822                                        && !appData.icsActive));
3823         count = 0;
3824         while (p && *p++ == dmEnables[i].piece) count++;
3825         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3826         j = 0;
3827         XtSetArg(args[j], XtNlabel, label); j++;
3828         XtSetValues(entry, args, j);
3829     }
3830 }
3831
3832 void PieceMenuPopup(w, event, params, num_params)
3833      Widget w;
3834      XEvent *event;
3835      String *params;
3836      Cardinal *num_params;
3837 {
3838     String whichMenu; int menuNr;
3839     if (event->type == ButtonRelease)
3840         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY); 
3841     else if (event->type == ButtonPress)
3842         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3843     switch(menuNr) {
3844       case 0: whichMenu = params[0]; break;
3845       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3846       case 2:
3847       case -1: if (errorUp) ErrorPopDown();
3848       default: return;
3849     }
3850     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3851 }
3852
3853 static void PieceMenuSelect(w, piece, junk)
3854      Widget w;
3855      ChessSquare piece;
3856      caddr_t junk;
3857 {
3858     if (pmFromX < 0 || pmFromY < 0) return;
3859     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3860 }
3861
3862 static void DropMenuSelect(w, piece, junk)
3863      Widget w;
3864      ChessSquare piece;
3865      caddr_t junk;
3866 {
3867     if (pmFromX < 0 || pmFromY < 0) return;
3868     DropMenuEvent(piece, pmFromX, pmFromY);
3869 }
3870
3871 void WhiteClock(w, event, prms, nprms)
3872      Widget w;
3873      XEvent *event;
3874      String *prms;
3875      Cardinal *nprms;
3876 {
3877     if (gameMode == EditPosition || gameMode == IcsExamining) {
3878         SetWhiteToPlayEvent();
3879     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3880         CallFlagEvent();
3881     }
3882 }
3883
3884 void BlackClock(w, event, prms, nprms)
3885      Widget w;
3886      XEvent *event;
3887      String *prms;
3888      Cardinal *nprms;
3889 {
3890     if (gameMode == EditPosition || gameMode == IcsExamining) {
3891         SetBlackToPlayEvent();
3892     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3893         CallFlagEvent();
3894     }
3895 }
3896
3897
3898 /*
3899  * If the user selects on a border boundary, return -1; if off the board,
3900  *   return -2.  Otherwise map the event coordinate to the square.
3901  */
3902 int EventToSquare(x, limit)
3903      int x;
3904 {
3905     if (x <= 0)
3906       return -2;
3907     if (x < lineGap)
3908       return -1;
3909     x -= lineGap;
3910     if ((x % (squareSize + lineGap)) >= squareSize)
3911       return -1;
3912     x /= (squareSize + lineGap);
3913     if (x >= limit)
3914       return -2;
3915     return x;
3916 }
3917
3918 static void do_flash_delay(msec)
3919      unsigned long msec;
3920 {
3921     TimeDelay(msec);
3922 }
3923
3924 static void drawHighlight(file, rank, gc)
3925      int file, rank;
3926      GC gc;
3927 {
3928     int x, y;
3929
3930     if (lineGap == 0 || appData.blindfold) return;
3931
3932     if (flipView) {
3933         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3934           (squareSize + lineGap);
3935         y = lineGap/2 + rank * (squareSize + lineGap);
3936     } else {
3937         x = lineGap/2 + file * (squareSize + lineGap);
3938         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
3939           (squareSize + lineGap);
3940     }
3941
3942     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3943                    squareSize+lineGap, squareSize+lineGap);
3944 }
3945
3946 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3947 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3948
3949 void
3950 SetHighlights(fromX, fromY, toX, toY)
3951      int fromX, fromY, toX, toY;
3952 {
3953     if (hi1X != fromX || hi1Y != fromY) {
3954         if (hi1X >= 0 && hi1Y >= 0) {
3955             drawHighlight(hi1X, hi1Y, lineGC);
3956         }
3957     } // [HGM] first erase both, then draw new!
3958     if (hi2X != toX || hi2Y != toY) {
3959         if (hi2X >= 0 && hi2Y >= 0) {
3960             drawHighlight(hi2X, hi2Y, lineGC);
3961         }
3962     }
3963     if (hi1X != fromX || hi1Y != fromY) {
3964         if (fromX >= 0 && fromY >= 0) {
3965             drawHighlight(fromX, fromY, highlineGC);
3966         }
3967     }
3968     if (hi2X != toX || hi2Y != toY) {
3969         if (toX >= 0 && toY >= 0) {
3970             drawHighlight(toX, toY, highlineGC);
3971         }
3972     }
3973     hi1X = fromX;
3974     hi1Y = fromY;
3975     hi2X = toX;
3976     hi2Y = toY;
3977 }
3978
3979 void
3980 ClearHighlights()
3981 {
3982     SetHighlights(-1, -1, -1, -1);
3983 }
3984
3985
3986 void
3987 SetPremoveHighlights(fromX, fromY, toX, toY)
3988      int fromX, fromY, toX, toY;
3989 {
3990     if (pm1X != fromX || pm1Y != fromY) {
3991         if (pm1X >= 0 && pm1Y >= 0) {
3992             drawHighlight(pm1X, pm1Y, lineGC);
3993         }
3994         if (fromX >= 0 && fromY >= 0) {
3995             drawHighlight(fromX, fromY, prelineGC);
3996         }
3997     }
3998     if (pm2X != toX || pm2Y != toY) {
3999         if (pm2X >= 0 && pm2Y >= 0) {
4000             drawHighlight(pm2X, pm2Y, lineGC);
4001         }
4002         if (toX >= 0 && toY >= 0) {
4003             drawHighlight(toX, toY, prelineGC);
4004         }
4005     }
4006     pm1X = fromX;
4007     pm1Y = fromY;
4008     pm2X = toX;
4009     pm2Y = toY;
4010 }
4011
4012 void
4013 ClearPremoveHighlights()
4014 {
4015   SetPremoveHighlights(-1, -1, -1, -1);
4016 }
4017
4018 static void BlankSquare(x, y, color, piece, dest)
4019      int x, y, color;
4020      ChessSquare piece;
4021      Drawable dest;
4022 {
4023     if (useImages && useImageSqs) {
4024         Pixmap pm;
4025         switch (color) {
4026           case 1: /* light */
4027             pm = xpmLightSquare;
4028             break;
4029           case 0: /* dark */
4030             pm = xpmDarkSquare;
4031             break;
4032           case 2: /* neutral */
4033           default:
4034             pm = xpmJailSquare;
4035             break;
4036         }
4037         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4038                   squareSize, squareSize, x, y);
4039     } else {
4040         GC gc;
4041         switch (color) {
4042           case 1: /* light */
4043             gc = lightSquareGC;
4044             break;
4045           case 0: /* dark */
4046             gc = darkSquareGC;
4047             break;
4048           case 2: /* neutral */
4049           default:
4050             gc = jailSquareGC;
4051             break;
4052         }
4053         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
4054     }
4055 }
4056
4057 /*
4058    I split out the routines to draw a piece so that I could
4059    make a generic flash routine.
4060 */
4061 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4062      ChessSquare piece;
4063      int square_color, x, y;
4064      Drawable dest;
4065 {
4066     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4067     switch (square_color) {
4068       case 1: /* light */
4069       case 2: /* neutral */
4070       default:
4071         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4072                   ? *pieceToOutline(piece)
4073                   : *pieceToSolid(piece),
4074                   dest, bwPieceGC, 0, 0,
4075                   squareSize, squareSize, x, y);
4076         break;
4077       case 0: /* dark */
4078         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4079                   ? *pieceToSolid(piece)
4080                   : *pieceToOutline(piece),
4081                   dest, wbPieceGC, 0, 0,
4082                   squareSize, squareSize, x, y);
4083         break;
4084     }
4085 }
4086
4087 static void monoDrawPiece(piece, square_color, x, y, dest)
4088      ChessSquare piece;
4089      int square_color, x, y;
4090      Drawable dest;
4091 {
4092     switch (square_color) {
4093       case 1: /* light */
4094       case 2: /* neutral */
4095       default:
4096         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4097                    ? *pieceToOutline(piece)
4098                    : *pieceToSolid(piece),
4099                    dest, bwPieceGC, 0, 0,
4100                    squareSize, squareSize, x, y, 1);
4101         break;
4102       case 0: /* dark */
4103         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4104                    ? *pieceToSolid(piece)
4105                    : *pieceToOutline(piece),
4106                    dest, wbPieceGC, 0, 0,
4107                    squareSize, squareSize, x, y, 1);
4108         break;
4109     }
4110 }
4111
4112 static void colorDrawPiece(piece, square_color, x, y, dest)
4113      ChessSquare piece;
4114      int square_color, x, y;
4115      Drawable dest;
4116 {
4117     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4118     switch (square_color) {
4119       case 1: /* light */
4120         XCopyPlane(xDisplay, *pieceToSolid(piece),
4121                    dest, (int) piece < (int) BlackPawn
4122                    ? wlPieceGC : blPieceGC, 0, 0,
4123                    squareSize, squareSize, x, y, 1);
4124         break;
4125       case 0: /* dark */
4126         XCopyPlane(xDisplay, *pieceToSolid(piece),
4127                    dest, (int) piece < (int) BlackPawn
4128                    ? wdPieceGC : bdPieceGC, 0, 0,
4129                    squareSize, squareSize, x, y, 1);
4130         break;
4131       case 2: /* neutral */
4132       default:
4133         XCopyPlane(xDisplay, *pieceToSolid(piece),
4134                    dest, (int) piece < (int) BlackPawn
4135                    ? wjPieceGC : bjPieceGC, 0, 0,
4136                    squareSize, squareSize, x, y, 1);
4137         break;
4138     }
4139 }
4140
4141 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4142      ChessSquare piece;
4143      int square_color, x, y;
4144      Drawable dest;
4145 {
4146     int kind;
4147
4148     switch (square_color) {
4149       case 1: /* light */
4150       case 2: /* neutral */
4151       default:
4152         if ((int)piece < (int) BlackPawn) {
4153             kind = 0;
4154         } else {
4155             kind = 2;
4156             piece -= BlackPawn;
4157         }
4158         break;
4159       case 0: /* dark */
4160         if ((int)piece < (int) BlackPawn) {
4161             kind = 1;
4162         } else {
4163             kind = 3;
4164             piece -= BlackPawn;
4165         }
4166         break;
4167     }
4168     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4169               dest, wlPieceGC, 0, 0,
4170               squareSize, squareSize, x, y);
4171 }
4172
4173 typedef void (*DrawFunc)();
4174
4175 DrawFunc ChooseDrawFunc()
4176 {
4177     if (appData.monoMode) {
4178         if (DefaultDepth(xDisplay, xScreen) == 1) {
4179             return monoDrawPiece_1bit;
4180         } else {
4181             return monoDrawPiece;
4182         }
4183     } else {
4184         if (useImages)
4185           return colorDrawPieceImage;
4186         else
4187           return colorDrawPiece;
4188     }
4189 }
4190
4191 /* [HR] determine square color depending on chess variant. */
4192 static int SquareColor(row, column)
4193      int row, column;
4194 {
4195     int square_color;
4196
4197     if (gameInfo.variant == VariantXiangqi) {
4198         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4199             square_color = 1;
4200         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4201             square_color = 0;
4202         } else if (row <= 4) {
4203             square_color = 0;
4204         } else {
4205             square_color = 1;
4206         }
4207     } else {
4208         square_color = ((column + row) % 2) == 1;
4209     }
4210
4211     /* [hgm] holdings: next line makes all holdings squares light */
4212     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
4213
4214     return square_color;
4215 }
4216
4217 void DrawSquare(row, column, piece, do_flash)
4218      int row, column, do_flash;
4219      ChessSquare piece;
4220 {
4221     int square_color, x, y, direction, font_ascent, font_descent;
4222     int i;
4223     char string[2];
4224     XCharStruct overall;
4225     DrawFunc drawfunc;
4226     int flash_delay;
4227
4228     /* Calculate delay in milliseconds (2-delays per complete flash) */
4229     flash_delay = 500 / appData.flashRate;
4230
4231     if (flipView) {
4232         x = lineGap + ((BOARD_WIDTH-1)-column) *
4233           (squareSize + lineGap);
4234         y = lineGap + row * (squareSize + lineGap);
4235     } else {
4236         x = lineGap + column * (squareSize + lineGap);
4237         y = lineGap + ((BOARD_HEIGHT-1)-row) *
4238           (squareSize + lineGap);
4239     }
4240
4241     if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
4242
4243     square_color = SquareColor(row, column);
4244
4245     if ( // [HGM] holdings: blank out area between board and holdings
4246                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
4247               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
4248                   || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
4249                         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
4250
4251                         // [HGM] print piece counts next to holdings
4252                         string[1] = NULLCHAR;
4253                         if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) && piece > 1 ) {
4254                             string[0] = '0' + piece;
4255                             XTextExtents(countFontStruct, string, 1, &direction,
4256                                  &font_ascent, &font_descent, &overall);
4257                             if (appData.monoMode) {
4258                                 XDrawImageString(xDisplay, xBoardWindow, countGC,
4259                                                  x + squareSize - overall.width - 2,
4260                                                  y + font_ascent + 1, string, 1);
4261                             } else {
4262                                 XDrawString(xDisplay, xBoardWindow, countGC,
4263                                             x + squareSize - overall.width - 2,
4264                                             y + font_ascent + 1, string, 1);