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