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