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