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