Move some back-endish routines from xboard.c to dialogs.c
[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 "dialogs.h"
209 #include "gettext.h"
210
211
212 #ifdef __EMX__
213 #ifndef HAVE_USLEEP
214 #define HAVE_USLEEP
215 #endif
216 #define usleep(t)   _sleep2(((t)+500)/1000)
217 #endif
218
219 #ifdef ENABLE_NLS
220 # define  _(s) gettext (s)
221 # define N_(s) gettext_noop (s)
222 #else
223 # define  _(s) (s)
224 # define N_(s)  s
225 #endif
226
227 int main P((int argc, char **argv));
228 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
229                 char *init_path, char *filter, char *mode, int (*show_entry)(), char **name_return));
230 RETSIGTYPE CmailSigHandler P((int sig));
231 RETSIGTYPE IntSigHandler P((int sig));
232 RETSIGTYPE TermSizeSigHandler P((int sig));
233 static void CreateGCs P((int redo));
234 static void CreateAnyPieces P((void));
235 void CreateXIMPieces P((void));
236 void CreateXPMPieces P((void));
237 void CreateXPMBoard P((char *s, int n));
238 void CreatePieces P((void));
239 void CreatePieceMenus P((void));
240 Widget CreateMenuBar P((Menu *mb, int boardWidth));
241 Widget CreateButtonBar P ((MenuItem *mi));
242 #if ENABLE_NLS
243 char *InsertPxlSize P((char *pattern, int targetPxlSize));
244 XFontSet CreateFontSet P((char *base_fnt_lst));
245 #else
246 char *FindFont P((char *pattern, int targetPxlSize));
247 #endif
248 void PieceMenuPopup P((Widget w, XEvent *event,
249                        String *params, Cardinal *num_params));
250 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
251 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
252 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
253                    u_int wreq, u_int hreq));
254 void CreateGrid P((void));
255 int EventToSquare P((int x, int limit));
256 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
257 void DelayedDrag P((void));
258 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
259 void HandleUserMove P((Widget w, XEvent *event,
260                      String *prms, Cardinal *nprms));
261 void AnimateUserMove P((Widget w, XEvent * event,
262                      String * params, Cardinal * nParams));
263 void HandlePV P((Widget w, XEvent * event,
264                      String * params, Cardinal * nParams));
265 void SelectPV P((Widget w, XEvent * event,
266                      String * params, Cardinal * nParams));
267 void StopPV P((Widget w, XEvent * event,
268                      String * params, Cardinal * nParams));
269 void WhiteClock P((Widget w, XEvent *event,
270                    String *prms, Cardinal *nprms));
271 void BlackClock P((Widget w, XEvent *event,
272                    String *prms, Cardinal *nprms));
273 void DrawPositionProc P((Widget w, XEvent *event,
274                      String *prms, Cardinal *nprms));
275 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
276                      Board board));
277 void CommentClick P((Widget w, XEvent * event,
278                    String * params, Cardinal * nParams));
279 void CommentPopUp P((char *title, char *label));
280 void CommentPopDown P((void));
281 void ICSInputBoxPopUp P((void));
282 void ICSInputBoxPopDown P((void));
283 void FileNamePopUp P((char *label, char *def, char *filter,
284                       FileProc proc, char *openMode));
285 void FileNamePopDown P((void));
286 void FileNameCallback P((Widget w, XtPointer client_data,
287                          XtPointer call_data));
288 void FileNameAction P((Widget w, XEvent *event,
289                        String *prms, Cardinal *nprms));
290 void AskQuestionReplyAction P((Widget w, XEvent *event,
291                           String *prms, Cardinal *nprms));
292 void AskQuestionProc P((Widget w, XEvent *event,
293                           String *prms, Cardinal *nprms));
294 void AskQuestionPopDown P((void));
295 void PromotionPopDown P((void));
296 void PromotionCallback P((Widget w, XtPointer client_data,
297                           XtPointer call_data));
298 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
299 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
300 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 static 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 ICSInitScript P((void));
310 static char *ExpandPathName P((char *path));
311 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
312 void GameListOptionsPopDown P(());
313 void GenericPopDown P(());
314 void update_ics_width P(());
315 int get_term_width P(());
316 int CopyMemoProc P(());
317 void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
318 Boolean IsDrawArrowEnabled P(());
319
320 /*
321 * XBoard depends on Xt R4 or higher
322 */
323 int xtVersion = XtSpecificationRelease;
324
325 int xScreen;
326 Display *xDisplay;
327 Window xBoardWindow;
328 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
329   jailSquareColor, highlightSquareColor, premoveHighlightColor;
330 Pixel lowTimeWarningColor;
331 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
332   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
333   wjPieceGC, bjPieceGC, prelineGC, countGC;
334 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
335 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
336   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
337   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
338   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
339   ICSInputShell, fileNameShell, askQuestionShell;
340 Widget historyShell, evalGraphShell, gameListShell;
341 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
342 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
343 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
344 #if ENABLE_NLS
345 XFontSet fontSet, clockFontSet;
346 #else
347 Font clockFontID;
348 XFontStruct *clockFontStruct;
349 #endif
350 Font coordFontID, countFontID;
351 XFontStruct *coordFontStruct, *countFontStruct;
352 XtAppContext appContext;
353 char *layoutName;
354 char *oldICSInteractionTitle;
355
356 FileProc fileProc;
357 char *fileOpenMode;
358 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
359
360 Position commentX = -1, commentY = -1;
361 Dimension commentW, commentH;
362 typedef unsigned int BoardSize;
363 BoardSize boardSize;
364 Boolean chessProgram;
365
366 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
367 int smallLayout = 0, tinyLayout = 0,
368   marginW, marginH, // [HGM] for run-time resizing
369   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
370   ICSInputBoxUp = False, askQuestionUp = False,
371   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
372   errorUp = False, errorExitStatus = -1, defaultLineGap;
373 Dimension textHeight;
374 Pixel timerForegroundPixel, timerBackgroundPixel;
375 Pixel buttonForegroundPixel, buttonBackgroundPixel;
376 char *chessDir, *programName, *programVersion;
377 Boolean alwaysOnTop = False;
378 char *icsTextMenuString;
379 char *icsNames;
380 char *firstChessProgramNames;
381 char *secondChessProgramNames;
382
383 WindowPlacement wpMain;
384 WindowPlacement wpConsole;
385 WindowPlacement wpComment;
386 WindowPlacement wpMoveHistory;
387 WindowPlacement wpEvalGraph;
388 WindowPlacement wpEngineOutput;
389 WindowPlacement wpGameList;
390 WindowPlacement wpTags;
391
392
393 #define SOLID 0
394 #define OUTLINE 1
395 Pixmap pieceBitmap[2][(int)BlackPawn];
396 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
397 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
398 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
399 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
400 Pixmap xpmBoardBitmap[2];
401 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
402 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
403 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
404 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
405 XImage *ximLightSquare, *ximDarkSquare;
406 XImage *xim_Cross;
407
408 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
409 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
410
411 #define White(piece) ((int)(piece) < (int)BlackPawn)
412
413 /* Bitmaps for use as masks when drawing XPM pieces.
414    Need one for each black and white piece.             */
415 static Pixmap xpmMask[BlackKing + 1];
416
417 /* This magic number is the number of intermediate frames used
418    in each half of the animation. For short moves it's reduced
419    by 1. The total number of frames will be factor * 2 + 1.  */
420 #define kFactor    4
421
422 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
423
424 #define PAUSE_BUTTON "P"
425 MenuItem buttonBar[] = {
426     {"<<", "<<", ToStartEvent},
427     {"<", "<", BackwardEvent},
428     {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
429     {">", ">", ForwardEvent},
430     {">>", ">>", ToEndEvent},
431     {NULL, NULL, NULL}
432 };
433
434 #define PIECE_MENU_SIZE 18
435 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
436     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
437       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
438       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
439       N_("Empty square"), N_("Clear board") },
440     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
441       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
442       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
443       N_("Empty square"), N_("Clear board") }
444 };
445 /* must be in same order as pieceMenuStrings! */
446 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
447     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
448         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
449         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
450         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
451     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
452         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
453         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
454         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
455 };
456
457 #define DROP_MENU_SIZE 6
458 String dropMenuStrings[DROP_MENU_SIZE] = {
459     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
460   };
461 /* must be in same order as dropMenuStrings! */
462 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
463     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
464     WhiteRook, WhiteQueen
465 };
466
467 typedef struct {
468     char piece;
469     char* widget;
470 } DropMenuEnables;
471
472 DropMenuEnables dmEnables[] = {
473     { 'P', "Pawn" },
474     { 'N', "Knight" },
475     { 'B', "Bishop" },
476     { 'R', "Rook" },
477     { 'Q', "Queen" }
478 };
479
480 Arg shellArgs[] = {
481     { XtNwidth, 0 },
482     { XtNheight, 0 },
483     { XtNminWidth, 0 },
484     { XtNminHeight, 0 },
485     { XtNmaxWidth, 0 },
486     { XtNmaxHeight, 0 }
487 };
488
489 Arg layoutArgs[] = {
490     { XtNborderWidth, 0 },
491     { XtNdefaultDistance, 0 },
492 };
493
494 Arg formArgs[] = {
495     { XtNborderWidth, 0 },
496     { XtNresizable, (XtArgVal) True },
497 };
498
499 Arg boardArgs[] = {
500     { XtNborderWidth, 0 },
501     { XtNwidth, 0 },
502     { XtNheight, 0 }
503 };
504
505 Arg titleArgs[] = {
506     { XtNjustify, (XtArgVal) XtJustifyRight },
507     { XtNlabel, (XtArgVal) "..." },
508     { XtNresizable, (XtArgVal) True },
509     { XtNresize, (XtArgVal) False }
510 };
511
512 Arg messageArgs[] = {
513     { XtNjustify, (XtArgVal) XtJustifyLeft },
514     { XtNlabel, (XtArgVal) "..." },
515     { XtNresizable, (XtArgVal) True },
516     { XtNresize, (XtArgVal) False }
517 };
518
519 Arg timerArgs[] = {
520     { XtNborderWidth, 0 },
521     { XtNjustify, (XtArgVal) XtJustifyLeft }
522 };
523
524 XtResource clientResources[] = {
525     { "flashCount", "flashCount", XtRInt, sizeof(int),
526         XtOffset(AppDataPtr, flashCount), XtRImmediate,
527         (XtPointer) FLASH_COUNT  },
528 };
529
530 XrmOptionDescRec shellOptions[] = {
531     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
532     { "-flash", "flashCount", XrmoptionNoArg, "3" },
533     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
534 };
535
536 XtActionsRec boardActions[] = {
537     { "DrawPosition", DrawPositionProc },
538     { "HandleUserMove", HandleUserMove },
539     { "AnimateUserMove", AnimateUserMove },
540     { "HandlePV", HandlePV },
541     { "SelectPV", SelectPV },
542     { "StopPV", StopPV },
543     { "FileNameAction", FileNameAction },
544     { "AskQuestionProc", AskQuestionProc },
545     { "AskQuestionReplyAction", AskQuestionReplyAction },
546     { "PieceMenuPopup", PieceMenuPopup },
547     { "WhiteClock", WhiteClock },
548     { "BlackClock", BlackClock },
549     { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
550     { "QuitProc", QuitWrapper },
551     { "ManProc", ManInner },
552     { "TempBackwardProc", TempBackwardProc },
553     { "TempForwardProc", TempForwardProc },
554     { "CommentClick", (XtActionProc) CommentClick },
555     { "CommentPopDown", (XtActionProc) CommentPopDown },
556     { "TagsPopDown", (XtActionProc) TagsPopDown },
557     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
558     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
559     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
560     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
561     { "GameListPopDown", (XtActionProc) GameListPopDown },
562     { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
563     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
564     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
565     { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
566     { "GenericPopDown", (XtActionProc) GenericPopDown },
567     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
568     { "SelectMove", (XtActionProc) SelectMove },
569     { "LoadSelectedProc", LoadSelectedProc },
570     { "SetFilterProc", SetFilterProc },
571     { "TypeInProc", TypeInProc },
572     { "EnterKeyProc", EnterKeyProc },
573     { "UpKeyProc", UpKeyProc },
574     { "DownKeyProc", DownKeyProc },
575 };
576
577 char globalTranslations[] =
578   ":<Key>F9: MenuItem(ResignProc) \n \
579    :Ctrl<Key>n: MenuItem(NewGame) \n \
580    :Meta<Key>V: MenuItem(NewVariant) \n \
581    :Ctrl<Key>o: MenuItem(LoadGame) \n \
582    :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
583    :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
584    :Ctrl<Key>Down: LoadSelectedProc(3) \n \
585    :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
586    :Ctrl<Key>s: MenuItem(SaveGame) \n \
587    :Ctrl<Key>c: MenuItem(CopyGame) \n \
588    :Ctrl<Key>v: MenuItem(PasteGame) \n \
589    :Ctrl<Key>O: MenuItem(LoadPosition) \n \
590    :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
591    :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
592    :Ctrl<Key>S: MenuItem(SavePosition) \n \
593    :Ctrl<Key>C: MenuItem(CopyPosition) \n \
594    :Ctrl<Key>V: MenuItem(PastePosition) \n \
595    :Ctrl<Key>q: MenuItem(Exit) \n \
596    :Ctrl<Key>w: MenuItem(MachineWhite) \n \
597    :Ctrl<Key>b: MenuItem(MachineBlack) \n \
598    :Ctrl<Key>t: MenuItem(TwoMachines) \n \
599    :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
600    :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
601    :Ctrl<Key>e: MenuItem(EditGame) \n \
602    :Ctrl<Key>E: MenuItem(EditPosition) \n \
603    :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
604    :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
605    :Meta<Key>G: MenuItem(ShowGameList) \n \
606    :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
607    :<Key>Pause: MenuItem(Pause) \n \
608    :<Key>F3: MenuItem(Accept) \n \
609    :<Key>F4: MenuItem(Decline) \n \
610    :<Key>F12: MenuItem(Rematch) \n \
611    :<Key>F5: MenuItem(CallFlag) \n \
612    :<Key>F6: MenuItem(Draw) \n \
613    :<Key>F7: MenuItem(Adjourn) \n \
614    :<Key>F8: MenuItem(Abort) \n \
615    :<Key>F10: MenuItem(StopObserving) \n \
616    :<Key>F11: MenuItem(StopExamining) \n \
617    :Ctrl<Key>d: MenuItem(DebugProc) \n \
618    :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
619    :Meta<Key>End: MenuItem(ToEnd) \n \
620    :Meta<Key>Right: MenuItem(Forward) \n \
621    :Meta<Key>Home: MenuItem(ToStart) \n \
622    :Meta<Key>Left: MenuItem(Backward) \n \
623    :<Key>Left: MenuItem(Backward) \n \
624    :<Key>Right: MenuItem(Forward) \n \
625    :<Key>Home: MenuItem(Revert) \n \
626    :<Key>End: MenuItem(TruncateGame) \n \
627    :Ctrl<Key>m: MenuItem(MoveNow) \n \
628    :Ctrl<Key>x: MenuItem(RetractMove) \n \
629    :Meta<Key>J: MenuItem(Adjudications) \n \
630    :Meta<Key>U: MenuItem(CommonEngine) \n \
631    :Meta<Key>T: MenuItem(TimeControl) \n \
632    :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
633 #ifndef OPTIONSDIALOG
634     "\
635    :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
636    :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
637    :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
638    :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
639    :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
640 #endif
641    "\
642    :<Key>F1: MenuItem(Manual) \n \
643    :<Key>F2: MenuItem(FlipView) \n \
644    :<KeyDown>Return: TempBackwardProc() \n \
645    :<KeyUp>Return: TempForwardProc() \n";
646
647 char boardTranslations[] =
648    "<Btn1Down>: HandleUserMove(0) \n \
649    Shift<Btn1Up>: HandleUserMove(1) \n \
650    <Btn1Up>: HandleUserMove(0) \n \
651    <Btn1Motion>: AnimateUserMove() \n \
652    <Btn3Motion>: HandlePV() \n \
653    <Btn2Motion>: HandlePV() \n \
654    <Btn3Up>: PieceMenuPopup(menuB) \n \
655    <Btn2Up>: PieceMenuPopup(menuB) \n \
656    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
657                  PieceMenuPopup(menuB) \n \
658    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
659                  PieceMenuPopup(menuW) \n \
660    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
661                  PieceMenuPopup(menuW) \n \
662    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
663                  PieceMenuPopup(menuB) \n";
664
665 char whiteTranslations[] =
666    "Shift<BtnDown>: WhiteClock(1)\n \
667    <BtnDown>: WhiteClock(0)\n";
668 char blackTranslations[] =
669    "Shift<BtnDown>: BlackClock(1)\n \
670    <BtnDown>: BlackClock(0)\n";
671
672 char ICSInputTranslations[] =
673     "<Key>Up: UpKeyProc() \n "
674     "<Key>Down: DownKeyProc() \n "
675     "<Key>Return: EnterKeyProc() \n";
676
677 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
678 //             as the widget is destroyed before the up-click can call extend-end
679 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
680
681 String xboardResources[] = {
682     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
683     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
684     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
685     NULL
686   };
687
688
689 /* Max possible square size */
690 #define MAXSQSIZE 256
691
692 static int xpm_avail[MAXSQSIZE];
693
694 #ifdef HAVE_DIR_STRUCT
695
696 /* Extract piece size from filename */
697 static int
698 xpm_getsize (char *name, int len, char *ext)
699 {
700     char *p, *d;
701     char buf[10];
702
703     if (len < 4)
704       return 0;
705
706     if ((p=strchr(name, '.')) == NULL ||
707         StrCaseCmp(p+1, ext) != 0)
708       return 0;
709
710     p = name + 3;
711     d = buf;
712
713     while (*p && isdigit(*p))
714       *(d++) = *(p++);
715
716     *d = 0;
717     return atoi(buf);
718 }
719
720 /* Setup xpm_avail */
721 static int
722 xpm_getavail (char *dirname, char *ext)
723 {
724     DIR *dir;
725     struct dirent *ent;
726     int  i;
727
728     for (i=0; i<MAXSQSIZE; ++i)
729       xpm_avail[i] = 0;
730
731     if (appData.debugMode)
732       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
733
734     dir = opendir(dirname);
735     if (!dir)
736       {
737           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
738                   programName, dirname);
739           exit(1);
740       }
741
742     while ((ent=readdir(dir)) != NULL) {
743         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
744         if (i > 0 && i < MAXSQSIZE)
745           xpm_avail[i] = 1;
746     }
747
748     closedir(dir);
749
750     return 0;
751 }
752
753 void
754 xpm_print_avail (FILE *fp, char *ext)
755 {
756     int i;
757
758     fprintf(fp, _("Available `%s' sizes:\n"), ext);
759     for (i=1; i<MAXSQSIZE; ++i) {
760         if (xpm_avail[i])
761           printf("%d\n", i);
762     }
763 }
764
765 /* Return XPM piecesize closest to size */
766 int
767 xpm_closest_to (char *dirname, int size, char *ext)
768 {
769     int i;
770     int sm_diff = MAXSQSIZE;
771     int sm_index = 0;
772     int diff;
773
774     xpm_getavail(dirname, ext);
775
776     if (appData.debugMode)
777       xpm_print_avail(stderr, ext);
778
779     for (i=1; i<MAXSQSIZE; ++i) {
780         if (xpm_avail[i]) {
781             diff = size - i;
782             diff = (diff<0) ? -diff : diff;
783             if (diff < sm_diff) {
784                 sm_diff = diff;
785                 sm_index = i;
786             }
787         }
788     }
789
790     if (!sm_index) {
791         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
792         exit(1);
793     }
794
795     return sm_index;
796 }
797 #else   /* !HAVE_DIR_STRUCT */
798 /* If we are on a system without a DIR struct, we can't
799    read the directory, so we can't collect a list of
800    filenames, etc., so we can't do any size-fitting. */
801 int
802 xpm_closest_to (char *dirname, int size, char *ext)
803 {
804     fprintf(stderr, _("\
805 Warning: No DIR structure found on this system --\n\
806          Unable to autosize for XPM/XIM pieces.\n\
807    Please report this error to %s.\n\
808    Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
809     return size;
810 }
811 #endif /* HAVE_DIR_STRUCT */
812
813 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
814                              "magenta", "cyan", "white" };
815 typedef struct {
816     int attr, bg, fg;
817 } TextColors;
818 TextColors textColors[(int)NColorClasses];
819
820 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
821 static int
822 parse_color (char *str, int which)
823 {
824     char *p, buf[100], *d;
825     int i;
826
827     if (strlen(str) > 99)       /* watch bounds on buf */
828       return -1;
829
830     p = str;
831     d = buf;
832     for (i=0; i<which; ++i) {
833         p = strchr(p, ',');
834         if (!p)
835           return -1;
836         ++p;
837     }
838
839     /* Could be looking at something like:
840        black, , 1
841        .. in which case we want to stop on a comma also */
842     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
843       ++p;
844
845     if (*p == ',') {
846         return -1;              /* Use default for empty field */
847     }
848
849     if (which == 2 || isdigit(*p))
850       return atoi(p);
851
852     while (*p && isalpha(*p))
853       *(d++) = *(p++);
854
855     *d = 0;
856
857     for (i=0; i<8; ++i) {
858         if (!StrCaseCmp(buf, cnames[i]))
859           return which? (i+40) : (i+30);
860     }
861     if (!StrCaseCmp(buf, "default")) return -1;
862
863     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
864     return -2;
865 }
866
867 static int
868 parse_cpair (ColorClass cc, char *str)
869 {
870     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
871         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
872                 programName, str);
873         return -1;
874     }
875
876     /* bg and attr are optional */
877     textColors[(int)cc].bg = parse_color(str, 1);
878     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
879         textColors[(int)cc].attr = 0;
880     }
881     return 0;
882 }
883
884
885 /* Arrange to catch delete-window events */
886 Atom wm_delete_window;
887 void
888 CatchDeleteWindow (Widget w, String procname)
889 {
890   char buf[MSG_SIZ];
891   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
892   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
893   XtAugmentTranslations(w, XtParseTranslationTable(buf));
894 }
895
896 void
897 BoardToTop ()
898 {
899   Arg args[16];
900   XtSetArg(args[0], XtNiconic, False);
901   XtSetValues(shellWidget, args, 1);
902
903   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
904 }
905
906 //---------------------------------------------------------------------------------------------------------
907 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
908 #define XBOARD True
909 #define JAWS_ARGS
910 #define CW_USEDEFAULT (1<<31)
911 #define ICS_TEXT_MENU_SIZE 90
912 #define DEBUG_FILE "xboard.debug"
913 #define SetCurrentDirectory chdir
914 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
915 #define OPTCHAR "-"
916 #define SEPCHAR " "
917
918 // these two must some day move to frontend.h, when they are implemented
919 Boolean GameListIsUp();
920
921 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
922 #include "args.h"
923
924 // front-end part of option handling
925
926 // [HGM] This platform-dependent table provides the location for storing the color info
927 extern char *crWhite, * crBlack;
928
929 void *
930 colorVariable[] = {
931   &appData.whitePieceColor,
932   &appData.blackPieceColor,
933   &appData.lightSquareColor,
934   &appData.darkSquareColor,
935   &appData.highlightSquareColor,
936   &appData.premoveHighlightColor,
937   &appData.lowTimeWarningColor,
938   NULL,
939   NULL,
940   NULL,
941   NULL,
942   NULL,
943   &crWhite,
944   &crBlack,
945   NULL
946 };
947
948 // [HGM] font: keep a font for each square size, even non-stndard ones
949 #define NUM_SIZES 18
950 #define MAX_SIZE 130
951 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
952 char *fontTable[NUM_FONTS][MAX_SIZE];
953
954 void
955 ParseFont (char *name, int number)
956 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
957   int size;
958   if(sscanf(name, "size%d:", &size)) {
959     // [HGM] font: font is meant for specific boardSize (likely from settings file);
960     //       defer processing it until we know if it matches our board size
961     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
962         fontTable[number][size] = strdup(strchr(name, ':')+1);
963         fontValid[number][size] = True;
964     }
965     return;
966   }
967   switch(number) {
968     case 0: // CLOCK_FONT
969         appData.clockFont = strdup(name);
970       break;
971     case 1: // MESSAGE_FONT
972         appData.font = strdup(name);
973       break;
974     case 2: // COORD_FONT
975         appData.coordFont = strdup(name);
976       break;
977     default:
978       return;
979   }
980   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
981 }
982
983 void
984 SetFontDefaults ()
985 { // only 2 fonts currently
986   appData.clockFont = CLOCK_FONT_NAME;
987   appData.coordFont = COORD_FONT_NAME;
988   appData.font  =   DEFAULT_FONT_NAME;
989 }
990
991 void
992 CreateFonts ()
993 { // no-op, until we identify the code for this already in XBoard and move it here
994 }
995
996 void
997 ParseColor (int n, char *name)
998 { // in XBoard, just copy the color-name string
999   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1000 }
1001
1002 void
1003 ParseTextAttribs (ColorClass cc, char *s)
1004 {
1005     (&appData.colorShout)[cc] = strdup(s);
1006 }
1007
1008 void
1009 ParseBoardSize (void *addr, char *name)
1010 {
1011     appData.boardSize = strdup(name);
1012 }
1013
1014 void
1015 LoadAllSounds ()
1016 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1017 }
1018
1019 void
1020 SetCommPortDefaults ()
1021 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1022 }
1023
1024 // [HGM] args: these three cases taken out to stay in front-end
1025 void
1026 SaveFontArg (FILE *f, ArgDescriptor *ad)
1027 {
1028   char *name;
1029   int i, n = (int)(intptr_t)ad->argLoc;
1030   switch(n) {
1031     case 0: // CLOCK_FONT
1032         name = appData.clockFont;
1033       break;
1034     case 1: // MESSAGE_FONT
1035         name = appData.font;
1036       break;
1037     case 2: // COORD_FONT
1038         name = appData.coordFont;
1039       break;
1040     default:
1041       return;
1042   }
1043   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1044     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1045         fontTable[n][squareSize] = strdup(name);
1046         fontValid[n][squareSize] = True;
1047         break;
1048   }
1049   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1050     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1051 }
1052
1053 void
1054 ExportSounds ()
1055 { // nothing to do, as the sounds are at all times represented by their text-string names already
1056 }
1057
1058 void
1059 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1060 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1061         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1062 }
1063
1064 void
1065 SaveColor (FILE *f, ArgDescriptor *ad)
1066 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1067         if(colorVariable[(int)(intptr_t)ad->argLoc])
1068         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1069 }
1070
1071 void
1072 SaveBoardSize (FILE *f, char *name, void *addr)
1073 { // wrapper to shield back-end from BoardSize & sizeInfo
1074   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1075 }
1076
1077 void
1078 ParseCommPortSettings (char *s)
1079 { // no such option in XBoard (yet)
1080 }
1081
1082 extern Widget engineOutputShell;
1083 int frameX, frameY;
1084
1085 void
1086 GetActualPlacement (Widget wg, WindowPlacement *wp)
1087 {
1088   Arg args[16];
1089   Dimension w, h;
1090   Position x, y;
1091   XWindowAttributes winAt;
1092   Window win, dummy;
1093   int i, rx, ry;
1094
1095   if(!wg) return;
1096
1097     win = XtWindow(wg);
1098     XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1099     XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1100     wp->x = rx - winAt.x;
1101     wp->y = ry - winAt.y;
1102     wp->height = winAt.height;
1103     wp->width = winAt.width;
1104     frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1105 }
1106
1107 void
1108 GetWindowCoords ()
1109 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1110   // In XBoard this will have to wait until awareness of window parameters is implemented
1111   GetActualPlacement(shellWidget, &wpMain);
1112   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1113   if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1114   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1115   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1116   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1117   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1118 }
1119
1120 void
1121 PrintCommPortSettings (FILE *f, char *name)
1122 { // This option does not exist in XBoard
1123 }
1124
1125 int
1126 MySearchPath (char *installDir, char *name, char *fullname)
1127 { // just append installDir and name. Perhaps ExpandPath should be used here?
1128   name = ExpandPathName(name);
1129   if(name && name[0] == '/')
1130     safeStrCpy(fullname, name, MSG_SIZ );
1131   else {
1132     sprintf(fullname, "%s%c%s", installDir, '/', name);
1133   }
1134   return 1;
1135 }
1136
1137 int
1138 MyGetFullPathName (char *name, char *fullname)
1139 { // should use ExpandPath?
1140   name = ExpandPathName(name);
1141   safeStrCpy(fullname, name, MSG_SIZ );
1142   return 1;
1143 }
1144
1145 void
1146 EnsureOnScreen (int *x, int *y, int minX, int minY)
1147 {
1148   return;
1149 }
1150
1151 int
1152 MainWindowUp ()
1153 { // [HGM] args: allows testing if main window is realized from back-end
1154   return xBoardWindow != 0;
1155 }
1156
1157 void
1158 PopUpStartupDialog ()
1159 {  // start menu not implemented in XBoard
1160 }
1161
1162 char *
1163 ConvertToLine (int argc, char **argv)
1164 {
1165   static char line[128*1024], buf[1024];
1166   int i;
1167
1168   line[0] = NULLCHAR;
1169   for(i=1; i<argc; i++)
1170     {
1171       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1172           && argv[i][0] != '{' )
1173         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1174       else
1175         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1176       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1177     }
1178
1179   line[strlen(line)-1] = NULLCHAR;
1180   return line;
1181 }
1182
1183 //--------------------------------------------------------------------------------------------
1184
1185 #ifdef IDSIZES
1186   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1187 #else
1188 #define BoardSize int
1189 void
1190 InitDrawingSizes (BoardSize boardSize, int flags)
1191 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1192     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1193     Arg args[16];
1194     XtGeometryResult gres;
1195     int i;
1196     static Dimension oldWidth, oldHeight;
1197     static VariantClass oldVariant;
1198     static int oldDual = -1, oldMono = -1;
1199
1200     if(!formWidget) return;
1201
1202     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1203     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1204     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1205
1206   if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1207     /*
1208      * Enable shell resizing.
1209      */
1210     shellArgs[0].value = (XtArgVal) &w;
1211     shellArgs[1].value = (XtArgVal) &h;
1212     XtGetValues(shellWidget, shellArgs, 2);
1213
1214     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1215     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1216     XtSetValues(shellWidget, &shellArgs[2], 4);
1217
1218     XtSetArg(args[0], XtNdefaultDistance, &sep);
1219     XtGetValues(formWidget, args, 1);
1220
1221     oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1222     CreateGrid();
1223     hOffset = boardWidth + 10;
1224     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1225         secondSegments[i] = gridSegments[i];
1226         secondSegments[i].x1 += hOffset;
1227         secondSegments[i].x2 += hOffset;
1228     }
1229
1230     XtSetArg(args[0], XtNwidth, boardWidth);
1231     XtSetArg(args[1], XtNheight, boardHeight);
1232     XtSetValues(boardWidget, args, 2);
1233
1234     timerWidth = (boardWidth - sep) / 2;
1235     XtSetArg(args[0], XtNwidth, timerWidth);
1236     XtSetValues(whiteTimerWidget, args, 1);
1237     XtSetValues(blackTimerWidget, args, 1);
1238
1239     XawFormDoLayout(formWidget, False);
1240
1241     if (appData.titleInWindow) {
1242         i = 0;
1243         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1244         XtSetArg(args[i], XtNheight, &h);  i++;
1245         XtGetValues(titleWidget, args, i);
1246         if (smallLayout) {
1247             w = boardWidth - 2*bor;
1248         } else {
1249             XtSetArg(args[0], XtNwidth, &w);
1250             XtGetValues(menuBarWidget, args, 1);
1251             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1252         }
1253
1254         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1255         if (gres != XtGeometryYes && appData.debugMode) {
1256             fprintf(stderr,
1257                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1258                     programName, gres, w, h, wr, hr);
1259         }
1260     }
1261
1262     XawFormDoLayout(formWidget, True);
1263
1264     /*
1265      * Inhibit shell resizing.
1266      */
1267     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1268     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1269     shellArgs[4].value = shellArgs[2].value = w;
1270     shellArgs[5].value = shellArgs[3].value = h;
1271     XtSetValues(shellWidget, &shellArgs[0], 6);
1272
1273     XSync(xDisplay, False);
1274     DelayedDrag();
1275   }
1276
1277     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1278     // (only for xpm)
1279
1280   if(gameInfo.variant != oldVariant) { // and only if variant changed
1281
1282     if(useImages) {
1283       for(i=0; i<4; i++) {
1284         int p;
1285         for(p=0; p<=(int)WhiteKing; p++)
1286            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1287         if(gameInfo.variant == VariantShogi) {
1288            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1289            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1290            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1291            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1292            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1293         }
1294 #ifdef GOTHIC
1295         if(gameInfo.variant == VariantGothic) {
1296            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1297         }
1298 #endif
1299         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1300            xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
1301            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1302         }
1303 #if !HAVE_LIBXPM
1304         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1305         for(p=0; p<=(int)WhiteKing; p++)
1306            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1307         if(gameInfo.variant == VariantShogi) {
1308            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1309            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1310            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1311            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1312            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1313         }
1314 #ifdef GOTHIC
1315         if(gameInfo.variant == VariantGothic) {
1316            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1317         }
1318 #endif
1319         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1320            ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
1321            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1322         }
1323 #endif
1324       }
1325     } else {
1326       for(i=0; i<2; i++) {
1327         int p;
1328         for(p=0; p<=(int)WhiteKing; p++)
1329            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1330         if(gameInfo.variant == VariantShogi) {
1331            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1332            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1333            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1334            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1335            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1336         }
1337 #ifdef GOTHIC
1338         if(gameInfo.variant == VariantGothic) {
1339            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1340         }
1341 #endif
1342         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1343            pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
1344            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1345         }
1346       }
1347     }
1348     oldMono = -10; // kludge to force recreation of animation masks
1349     oldVariant = gameInfo.variant;
1350   }
1351 #if HAVE_LIBXPM
1352   if(appData.monoMode != oldMono)
1353     CreateAnimVars();
1354 #endif
1355   oldMono = appData.monoMode;
1356 }
1357 #endif
1358
1359 void
1360 ParseIcsTextColors ()
1361 {   // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1362     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1363         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1364         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
1365         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
1366         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1367         parse_cpair(ColorTell, appData.colorTell) < 0 ||
1368         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
1369         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
1370         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
1371         parse_cpair(ColorNormal, appData.colorNormal) < 0)
1372       {
1373           if (appData.colorize) {
1374               fprintf(stderr,
1375                       _("%s: can't parse color names; disabling colorization\n"),
1376                       programName);
1377           }
1378           appData.colorize = FALSE;
1379       }
1380 }
1381
1382 static int
1383 MakeOneColor (char *name, Pixel *color)
1384 {
1385     XrmValue vFrom, vTo;
1386     if (!appData.monoMode) {
1387         vFrom.addr = (caddr_t) name;
1388         vFrom.size = strlen(name);
1389         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1390         if (vTo.addr == NULL) {
1391           appData.monoMode = True;
1392           return True;
1393         } else {
1394           *color = *(Pixel *) vTo.addr;
1395         }
1396     }
1397     return False;
1398 }
1399
1400 static int
1401 MakeColors ()
1402 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1403     int forceMono = False;
1404
1405     forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1406     forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1407     forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1408     forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1409     forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1410     forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1411
1412     return forceMono;
1413 }
1414
1415 static void
1416 CreateAnyPieces ()
1417 {   // [HGM] taken out of main
1418 #if HAVE_LIBXPM
1419     if (appData.monoMode && // [HGM] no sense to go on to certain doom
1420        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1421             appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1422
1423     if (appData.bitmapDirectory[0] != NULLCHAR) {
1424       CreatePieces();
1425     } else {
1426       CreateXPMPieces();
1427       CreateXPMBoard(appData.liteBackTextureFile, 1);
1428       CreateXPMBoard(appData.darkBackTextureFile, 0);
1429     }
1430 #else
1431     CreateXIMPieces();
1432     /* Create regular pieces */
1433     if (!useImages) CreatePieces();
1434 #endif
1435 }
1436
1437 void
1438 InitDrawingParams ()
1439 {
1440     MakeColors(); CreateGCs(True);
1441     CreateAnyPieces();
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 #define Abs(n) ((n)<0 ? -(n) : (n))
2196
2197 #ifdef ENABLE_NLS
2198 char *
2199 InsertPxlSize (char *pattern, int targetPxlSize)
2200 {
2201     char *base_fnt_lst, strInt[12], *p, *q;
2202     int alternatives, i, len, strIntLen;
2203
2204     /*
2205      * Replace the "*" (if present) in the pixel-size slot of each
2206      * alternative with the targetPxlSize.
2207      */
2208     p = pattern;
2209     alternatives = 1;
2210     while ((p = strchr(p, ',')) != NULL) {
2211       alternatives++;
2212       p++;
2213     }
2214     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2215     strIntLen = strlen(strInt);
2216     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2217
2218     p = pattern;
2219     q = base_fnt_lst;
2220     while (alternatives--) {
2221       char *comma = strchr(p, ',');
2222       for (i=0; i<14; i++) {
2223         char *hyphen = strchr(p, '-');
2224         if (!hyphen) break;
2225         if (comma && hyphen > comma) break;
2226         len = hyphen + 1 - p;
2227         if (i == 7 && *p == '*' && len == 2) {
2228           p += len;
2229           memcpy(q, strInt, strIntLen);
2230           q += strIntLen;
2231           *q++ = '-';
2232         } else {
2233           memcpy(q, p, len);
2234           p += len;
2235           q += len;
2236         }
2237       }
2238       if (!comma) break;
2239       len = comma + 1 - p;
2240       memcpy(q, p, len);
2241       p += len;
2242       q += len;
2243     }
2244     strcpy(q, p);
2245
2246     return base_fnt_lst;
2247 }
2248
2249 XFontSet
2250 CreateFontSet (char *base_fnt_lst)
2251 {
2252     XFontSet fntSet;
2253     char **missing_list;
2254     int missing_count;
2255     char *def_string;
2256
2257     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2258                             &missing_list, &missing_count, &def_string);
2259     if (appData.debugMode) {
2260       int i, count;
2261       XFontStruct **font_struct_list;
2262       char **font_name_list;
2263       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2264       if (fntSet) {
2265         fprintf(debugFP, " got list %s, locale %s\n",
2266                 XBaseFontNameListOfFontSet(fntSet),
2267                 XLocaleOfFontSet(fntSet));
2268         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2269         for (i = 0; i < count; i++) {
2270           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2271         }
2272       }
2273       for (i = 0; i < missing_count; i++) {
2274         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2275       }
2276     }
2277     if (fntSet == NULL) {
2278       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2279       exit(2);
2280     }
2281     return fntSet;
2282 }
2283 #else // not ENABLE_NLS
2284 /*
2285  * Find a font that matches "pattern" that is as close as
2286  * possible to the targetPxlSize.  Prefer fonts that are k
2287  * pixels smaller to fonts that are k pixels larger.  The
2288  * pattern must be in the X Consortium standard format,
2289  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2290  * The return value should be freed with XtFree when no
2291  * longer needed.
2292  */
2293 char *
2294 FindFont (char *pattern, int targetPxlSize)
2295 {
2296     char **fonts, *p, *best, *scalable, *scalableTail;
2297     int i, j, nfonts, minerr, err, pxlSize;
2298
2299     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2300     if (nfonts < 1) {
2301         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2302                 programName, pattern);
2303         exit(2);
2304     }
2305
2306     best = fonts[0];
2307     scalable = NULL;
2308     minerr = 999999;
2309     for (i=0; i<nfonts; i++) {
2310         j = 0;
2311         p = fonts[i];
2312         if (*p != '-') continue;
2313         while (j < 7) {
2314             if (*p == NULLCHAR) break;
2315             if (*p++ == '-') j++;
2316         }
2317         if (j < 7) continue;
2318         pxlSize = atoi(p);
2319         if (pxlSize == 0) {
2320             scalable = fonts[i];
2321             scalableTail = p;
2322         } else {
2323             err = pxlSize - targetPxlSize;
2324             if (Abs(err) < Abs(minerr) ||
2325                 (minerr > 0 && err < 0 && -err == minerr)) {
2326                 best = fonts[i];
2327                 minerr = err;
2328             }
2329         }
2330     }
2331     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2332         /* If the error is too big and there is a scalable font,
2333            use the scalable font. */
2334         int headlen = scalableTail - scalable;
2335         p = (char *) XtMalloc(strlen(scalable) + 10);
2336         while (isdigit(*scalableTail)) scalableTail++;
2337         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2338     } else {
2339         p = (char *) XtMalloc(strlen(best) + 2);
2340         safeStrCpy(p, best, strlen(best)+1 );
2341     }
2342     if (appData.debugMode) {
2343         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2344                 pattern, targetPxlSize, p);
2345     }
2346     XFreeFontNames(fonts);
2347     return p;
2348 }
2349 #endif
2350
2351 void
2352 DeleteGCs ()
2353 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2354     // must be called before all non-first callse to CreateGCs()
2355     XtReleaseGC(shellWidget, highlineGC);
2356     XtReleaseGC(shellWidget, lightSquareGC);
2357     XtReleaseGC(shellWidget, darkSquareGC);
2358     XtReleaseGC(shellWidget, lineGC);
2359     if (appData.monoMode) {
2360         if (DefaultDepth(xDisplay, xScreen) == 1) {
2361             XtReleaseGC(shellWidget, wbPieceGC);
2362         } else {
2363             XtReleaseGC(shellWidget, bwPieceGC);
2364         }
2365     } else {
2366         XtReleaseGC(shellWidget, prelineGC);
2367         XtReleaseGC(shellWidget, jailSquareGC);
2368         XtReleaseGC(shellWidget, wdPieceGC);
2369         XtReleaseGC(shellWidget, wlPieceGC);
2370         XtReleaseGC(shellWidget, wjPieceGC);
2371         XtReleaseGC(shellWidget, bdPieceGC);
2372         XtReleaseGC(shellWidget, blPieceGC);
2373         XtReleaseGC(shellWidget, bjPieceGC);
2374     }
2375 }
2376
2377 static GC
2378 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2379 {
2380     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2381       | GCBackground | GCFunction | GCPlaneMask;
2382     gc_values->foreground = foreground;
2383     gc_values->background = background;
2384     return XtGetGC(shellWidget, value_mask, gc_values);
2385 }
2386
2387 static void
2388 CreateGCs (int redo)
2389 {
2390     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2391       | GCBackground | GCFunction | GCPlaneMask;
2392     XGCValues gc_values;
2393     GC copyInvertedGC;
2394     Pixel white = XWhitePixel(xDisplay, xScreen);
2395     Pixel black = XBlackPixel(xDisplay, xScreen);
2396
2397     gc_values.plane_mask = AllPlanes;
2398     gc_values.line_width = lineGap;
2399     gc_values.line_style = LineSolid;
2400     gc_values.function = GXcopy;
2401
2402   if(redo) {
2403     DeleteGCs(); // called a second time; clean up old GCs first
2404   } else { // [HGM] grid and font GCs created on first call only
2405     coordGC = CreateOneGC(&gc_values, black, white);
2406     XSetFont(xDisplay, coordGC, coordFontID);
2407
2408     // [HGM] make font for holdings counts (white on black)
2409     countGC = CreateOneGC(&gc_values, white, black);
2410     XSetFont(xDisplay, countGC, countFontID);
2411   }
2412     lineGC = CreateOneGC(&gc_values, black, black);
2413
2414     if (appData.monoMode) {
2415
2416         highlineGC = CreateOneGC(&gc_values, white, white);
2417         lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2418         darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2419
2420         if (DefaultDepth(xDisplay, xScreen) == 1) {
2421             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2422             gc_values.function = GXcopyInverted;
2423             copyInvertedGC = CreateOneGC(&gc_values, black, white);
2424             gc_values.function = GXcopy;
2425             if (XBlackPixel(xDisplay, xScreen) == 1) {
2426                 bwPieceGC = darkSquareGC;
2427                 wbPieceGC = copyInvertedGC;
2428             } else {
2429                 bwPieceGC = copyInvertedGC;
2430                 wbPieceGC = lightSquareGC;
2431             }
2432         }
2433     } else {
2434
2435         highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2436         prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2437         lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2438         darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2439         jailSquareGC = CreateOneGC(&gc_values, jailSquareColor, jailSquareColor);
2440         wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2441         wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2442         wjPieceGC = CreateOneGC(&gc_values, whitePieceColor, jailSquareColor);
2443         bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2444         blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2445         bjPieceGC = CreateOneGC(&gc_values, blackPieceColor, jailSquareColor);
2446     }
2447 }
2448
2449 void
2450 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2451 {
2452     int x, y, w, h, p;
2453     FILE *fp;
2454     Pixmap temp;
2455     XGCValues   values;
2456     GC maskGC;
2457
2458     fp = fopen(filename, "rb");
2459     if (!fp) {
2460         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2461         exit(1);
2462     }
2463
2464     w = fgetc(fp);
2465     h = fgetc(fp);
2466
2467     for (y=0; y<h; ++y) {
2468         for (x=0; x<h; ++x) {
2469             p = fgetc(fp);
2470
2471             switch (p) {
2472               case 0:
2473                 XPutPixel(xim, x, y, blackPieceColor);
2474                 if (xmask)
2475                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2476                 break;
2477               case 1:
2478                 XPutPixel(xim, x, y, darkSquareColor);
2479                 if (xmask)
2480                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2481                 break;
2482               case 2:
2483                 XPutPixel(xim, x, y, whitePieceColor);
2484                 if (xmask)
2485                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2486                 break;
2487               case 3:
2488                 XPutPixel(xim, x, y, lightSquareColor);
2489                 if (xmask)
2490                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2491                 break;
2492             }
2493         }
2494     }
2495
2496     fclose(fp);
2497
2498     /* create Pixmap of piece */
2499     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2500                           w, h, xim->depth);
2501     XPutImage(xDisplay, *dest, lightSquareGC, xim,
2502               0, 0, 0, 0, w, h);
2503
2504     /* create Pixmap of clipmask
2505        Note: We assume the white/black pieces have the same
2506              outline, so we make only 6 masks. This is okay
2507              since the XPM clipmask routines do the same. */
2508     if (xmask) {
2509       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2510                             w, h, xim->depth);
2511       XPutImage(xDisplay, temp, lightSquareGC, xmask,
2512               0, 0, 0, 0, w, h);
2513
2514       /* now create the 1-bit version */
2515       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2516                           w, h, 1);
2517
2518       values.foreground = 1;
2519       values.background = 0;
2520
2521       /* Don't use XtGetGC, not read only */
2522       maskGC = XCreateGC(xDisplay, *mask,
2523                     GCForeground | GCBackground, &values);
2524       XCopyPlane(xDisplay, temp, *mask, maskGC,
2525                   0, 0, squareSize, squareSize, 0, 0, 1);
2526       XFreePixmap(xDisplay, temp);
2527     }
2528 }
2529
2530
2531 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2532
2533 void
2534 CreateXIMPieces ()
2535 {
2536     int piece, kind;
2537     char buf[MSG_SIZ];
2538     u_int ss;
2539     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2540     XImage *ximtemp;
2541
2542     ss = squareSize;
2543
2544     /* The XSynchronize calls were copied from CreatePieces.
2545        Not sure if needed, but can't hurt */
2546     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2547                                      buffering bug */
2548
2549     /* temp needed by loadXIM() */
2550     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2551                  0, 0, ss, ss, AllPlanes, XYPixmap);
2552
2553     if (strlen(appData.pixmapDirectory) == 0) {
2554       useImages = 0;
2555     } else {
2556         useImages = 1;
2557         if (appData.monoMode) {
2558           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2559                             0, 2);
2560           ExitEvent(2);
2561         }
2562         fprintf(stderr, _("\nLoading XIMs...\n"));
2563         /* Load pieces */
2564         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2565             fprintf(stderr, "%d", piece+1);
2566             for (kind=0; kind<4; kind++) {
2567                 fprintf(stderr, ".");
2568                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2569                         ExpandPathName(appData.pixmapDirectory),
2570                         piece <= (int) WhiteKing ? "" : "w",
2571                         pieceBitmapNames[piece],
2572                         ximkind[kind], ss);
2573                 ximPieceBitmap[kind][piece] =
2574                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2575                             0, 0, ss, ss, AllPlanes, XYPixmap);
2576                 if (appData.debugMode)
2577                   fprintf(stderr, _("(File:%s:) "), buf);
2578                 loadXIM(ximPieceBitmap[kind][piece],
2579                         ximtemp, buf,
2580                         &(xpmPieceBitmap2[kind][piece]),
2581                         &(ximMaskPm2[piece]));
2582                 if(piece <= (int)WhiteKing)
2583                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2584             }
2585             fprintf(stderr," ");
2586         }
2587         /* Load light and dark squares */
2588         /* If the LSQ and DSQ pieces don't exist, we will
2589            draw them with solid squares. */
2590         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2591         if (access(buf, 0) != 0) {
2592             useImageSqs = 0;
2593         } else {
2594             useImageSqs = 1;
2595             fprintf(stderr, _("light square "));
2596             ximLightSquare=
2597               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2598                         0, 0, ss, ss, AllPlanes, XYPixmap);
2599             if (appData.debugMode)
2600               fprintf(stderr, _("(File:%s:) "), buf);
2601
2602             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2603             fprintf(stderr, _("dark square "));
2604             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2605                     ExpandPathName(appData.pixmapDirectory), ss);
2606             if (appData.debugMode)
2607               fprintf(stderr, _("(File:%s:) "), buf);
2608             ximDarkSquare=
2609               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2610                         0, 0, ss, ss, AllPlanes, XYPixmap);
2611             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2612             xpmJailSquare = xpmLightSquare;
2613         }
2614         fprintf(stderr, _("Done.\n"));
2615     }
2616     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2617 }
2618
2619 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2620
2621 #if HAVE_LIBXPM
2622 void
2623 CreateXPMBoard (char *s, int kind)
2624 {
2625     XpmAttributes attr;
2626     attr.valuemask = 0;
2627     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2628     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2629         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2630     }
2631 }
2632
2633 void
2634 FreeXPMPieces ()
2635 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2636     // thisroutine has to be called t free the old piece pixmaps
2637     int piece, kind;
2638     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2639         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2640     if(useImageSqs) {
2641         XFreePixmap(xDisplay, xpmLightSquare);
2642         XFreePixmap(xDisplay, xpmDarkSquare);
2643     }
2644 }
2645
2646 void
2647 CreateXPMPieces ()
2648 {
2649     int piece, kind, r;
2650     char buf[MSG_SIZ];
2651     u_int ss = squareSize;
2652     XpmAttributes attr;
2653     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2654     XpmColorSymbol symbols[4];
2655     static int redo = False;
2656
2657     if(redo) FreeXPMPieces(); else redo = 1;
2658
2659     /* The XSynchronize calls were copied from CreatePieces.
2660        Not sure if needed, but can't hurt */
2661     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2662
2663     /* Setup translations so piece colors match square colors */
2664     symbols[0].name = "light_piece";
2665     symbols[0].value = appData.whitePieceColor;
2666     symbols[1].name = "dark_piece";
2667     symbols[1].value = appData.blackPieceColor;
2668     symbols[2].name = "light_square";
2669     symbols[2].value = appData.lightSquareColor;
2670     symbols[3].name = "dark_square";
2671     symbols[3].value = appData.darkSquareColor;
2672
2673     attr.valuemask = XpmColorSymbols;
2674     attr.colorsymbols = symbols;
2675     attr.numsymbols = 4;
2676
2677     if (appData.monoMode) {
2678       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2679                         0, 2);
2680       ExitEvent(2);
2681     }
2682     if (strlen(appData.pixmapDirectory) == 0) {
2683         XpmPieces* pieces = builtInXpms;
2684         useImages = 1;
2685         /* Load pieces */
2686         while (pieces->size != squareSize && pieces->size) pieces++;
2687         if (!pieces->size) {
2688           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2689           exit(1);
2690         }
2691         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2692             for (kind=0; kind<4; kind++) {
2693
2694                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2695                                                pieces->xpm[piece][kind],
2696                                                &(xpmPieceBitmap2[kind][piece]),
2697                                                NULL, &attr)) != 0) {
2698                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2699                           r, buf);
2700                   exit(1);
2701                 }
2702                 if(piece <= (int) WhiteKing)
2703                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2704             }
2705         }
2706         useImageSqs = 0;
2707         xpmJailSquare = xpmLightSquare;
2708     } else {
2709         useImages = 1;
2710
2711         fprintf(stderr, _("\nLoading XPMs...\n"));
2712
2713         /* Load pieces */
2714         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2715             fprintf(stderr, "%d ", piece+1);
2716             for (kind=0; kind<4; kind++) {
2717               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2718                         ExpandPathName(appData.pixmapDirectory),
2719                         piece > (int) WhiteKing ? "w" : "",
2720                         pieceBitmapNames[piece],
2721                         xpmkind[kind], ss);
2722                 if (appData.debugMode) {
2723                     fprintf(stderr, _("(File:%s:) "), buf);
2724                 }
2725                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2726                                            &(xpmPieceBitmap2[kind][piece]),
2727                                            NULL, &attr)) != 0) {
2728                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2729                       // [HGM] missing: read of unorthodox piece failed; substitute King.
2730                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2731                                 ExpandPathName(appData.pixmapDirectory),
2732                                 xpmkind[kind], ss);
2733                         if (appData.debugMode) {
2734                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
2735                         }
2736                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2737                                                 &(xpmPieceBitmap2[kind][piece]),
2738                                                 NULL, &attr);
2739                     }
2740                     if (r != 0) {
2741                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2742                                 r, buf);
2743                         exit(1);
2744                     }
2745                 }
2746                 if(piece <= (int) WhiteKing)
2747                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2748             }
2749         }
2750         /* Load light and dark squares */
2751         /* If the LSQ and DSQ pieces don't exist, we will
2752            draw them with solid squares. */
2753         fprintf(stderr, _("light square "));
2754         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2755         if (access(buf, 0) != 0) {
2756             useImageSqs = 0;
2757         } else {
2758             useImageSqs = 1;
2759             if (appData.debugMode)
2760               fprintf(stderr, _("(File:%s:) "), buf);
2761
2762             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2763                                        &xpmLightSquare, NULL, &attr)) != 0) {
2764                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2765                 exit(1);
2766             }
2767             fprintf(stderr, _("dark square "));
2768             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2769                     ExpandPathName(appData.pixmapDirectory), ss);
2770             if (appData.debugMode) {
2771                 fprintf(stderr, _("(File:%s:) "), buf);
2772             }
2773             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2774                                        &xpmDarkSquare, NULL, &attr)) != 0) {
2775                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2776                 exit(1);
2777             }
2778         }
2779         xpmJailSquare = xpmLightSquare;
2780         fprintf(stderr, _("Done.\n"));
2781     }
2782     oldVariant = -1; // kludge to force re-makig of animation masks
2783     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2784                                       buffering bug */
2785 }
2786 #endif /* HAVE_LIBXPM */
2787
2788 #if HAVE_LIBXPM
2789 /* No built-in bitmaps */
2790 void CreatePieces()
2791 {
2792     int piece, kind;
2793     char buf[MSG_SIZ];
2794     u_int ss = squareSize;
2795
2796     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2797                                      buffering bug */
2798
2799     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2800         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2801           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2802                    pieceBitmapNames[piece],
2803                    ss, kind == SOLID ? 's' : 'o');
2804           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2805           if(piece <= (int)WhiteKing)
2806             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2807         }
2808     }
2809
2810     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2811                                       buffering bug */
2812 }
2813 #else
2814 /* With built-in bitmaps */
2815 void
2816 CreatePieces ()
2817 {
2818     BuiltInBits* bib = builtInBits;
2819     int piece, kind;
2820     char buf[MSG_SIZ];
2821     u_int ss = squareSize;
2822
2823     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2824                                      buffering bug */
2825
2826     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2827
2828     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2829         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2830           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2831                    pieceBitmapNames[piece],
2832                    ss, kind == SOLID ? 's' : 'o');
2833           ReadBitmap(&pieceBitmap2[kind][piece], buf,
2834                      bib->bits[kind][piece], ss, ss);
2835           if(piece <= (int)WhiteKing)
2836             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2837         }
2838     }
2839
2840     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2841                                       buffering bug */
2842 }
2843 #endif
2844
2845 void
2846 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2847 {
2848     int x_hot, y_hot;
2849     u_int w, h;
2850     int errcode;
2851     char msg[MSG_SIZ], fullname[MSG_SIZ];
2852
2853     if (*appData.bitmapDirectory != NULLCHAR) {
2854       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2855       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2856       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2857       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2858                                 &w, &h, pm, &x_hot, &y_hot);
2859       fprintf(stderr, "load %s\n", name);
2860         if (errcode != BitmapSuccess) {
2861             switch (errcode) {
2862               case BitmapOpenFailed:
2863                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2864                 break;
2865               case BitmapFileInvalid:
2866                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2867                 break;
2868               case BitmapNoMemory:
2869                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2870                         fullname);
2871                 break;
2872               default:
2873                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2874                         errcode, fullname);
2875                 break;
2876             }
2877             fprintf(stderr, _("%s: %s...using built-in\n"),
2878                     programName, msg);
2879         } else if (w != wreq || h != hreq) {
2880             fprintf(stderr,
2881                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2882                     programName, fullname, w, h, wreq, hreq);
2883         } else {
2884             return;
2885         }
2886     }
2887     if (bits != NULL) {
2888         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2889                                     wreq, hreq);
2890     }
2891 }
2892
2893 void
2894 CreateGrid ()
2895 {
2896     int i, j;
2897
2898     if (lineGap == 0) return;
2899
2900     /* [HR] Split this into 2 loops for non-square boards. */
2901
2902     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2903         gridSegments[i].x1 = 0;
2904         gridSegments[i].x2 =
2905           lineGap + BOARD_WIDTH * (squareSize + lineGap);
2906         gridSegments[i].y1 = gridSegments[i].y2
2907           = lineGap / 2 + (i * (squareSize + lineGap));
2908     }
2909
2910     for (j = 0; j < BOARD_WIDTH + 1; j++) {
2911         gridSegments[j + i].y1 = 0;
2912         gridSegments[j + i].y2 =
2913           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2914         gridSegments[j + i].x1 = gridSegments[j + i].x2
2915           = lineGap / 2 + (j * (squareSize + lineGap));
2916     }
2917 }
2918
2919 int nrOfMenuItems = 7;
2920 Widget menuWidget[150];
2921 MenuListItem menuItemList[150] = {
2922     { "LoadNextGameProc", LoadNextGameProc },
2923     { "LoadPrevGameProc", LoadPrevGameProc },
2924     { "ReloadGameProc", ReloadGameProc },
2925     { "ReloadPositionProc", ReloadPositionProc },
2926 #ifndef OPTIONSDIALOG
2927     { "AlwaysQueenProc", AlwaysQueenProc },
2928     { "AnimateDraggingProc", AnimateDraggingProc },
2929     { "AnimateMovingProc", AnimateMovingProc },
2930     { "AutoflagProc", AutoflagProc },
2931     { "AutoflipProc", AutoflipProc },
2932     { "BlindfoldProc", BlindfoldProc },
2933     { "FlashMovesProc", FlashMovesProc },
2934 #if HIGHDRAG
2935     { "HighlightDraggingProc", HighlightDraggingProc },
2936 #endif
2937     { "HighlightLastMoveProc", HighlightLastMoveProc },
2938 //    { "IcsAlarmProc", IcsAlarmProc },
2939     { "MoveSoundProc", MoveSoundProc },
2940     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2941     { "PopupExitMessageProc", PopupExitMessageProc },
2942     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2943 //    { "PremoveProc", PremoveProc },
2944     { "ShowCoordsProc", ShowCoordsProc },
2945     { "ShowThinkingProc", ShowThinkingProc },
2946     { "HideThinkingProc", HideThinkingProc },
2947     { "TestLegalityProc", TestLegalityProc },
2948 #endif
2949     { "AboutGameProc", AboutGameEvent },
2950     { "DebugProc", DebugProc },
2951     { "NothingProc", NothingProc },
2952   {NULL, NothingProc}
2953 };
2954
2955 void
2956 MarkMenuItem (char *menuRef, int state)
2957 {
2958     int nr = MenuToNumber(menuRef);
2959     if(nr >= 0) {
2960         Arg args[2];
2961         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2962         XtSetValues(menuWidget[nr], args, 1);
2963     }
2964 }
2965
2966 void
2967 EnableMenuItem (char *menuRef, int state)
2968 {
2969     int nr = MenuToNumber(menuRef);
2970     if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2971 }
2972
2973 void
2974 EnableButtonBar (int state)
2975 {
2976     XtSetSensitive(buttonBarWidget, state);
2977 }
2978
2979
2980 void
2981 SetMenuEnables (Enables *enab)
2982 {
2983   while (enab->name != NULL) {
2984     EnableMenuItem(enab->name, enab->value);
2985     enab++;
2986   }
2987 }
2988
2989 int
2990 Equal(char *p, char *s)
2991 {   // compare strings skipping spaces in second
2992     while(*s) {
2993         if(*s == ' ') { s++; continue; }
2994         if(*s++ != *p++) return 0;
2995     }
2996     return !*p;
2997 }
2998
2999 void
3000 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3001 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
3002     int i;
3003     if(*nprms == 0) return;
3004     for(i=0; menuItemList[i].name; i++) {
3005         if(Equal(prms[0], menuItemList[i].name)) {
3006             (menuItemList[i].proc) ();
3007             return;
3008         }
3009     }
3010 }
3011
3012 static void
3013 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
3014 {
3015     MenuProc *proc = (MenuProc *) addr;
3016
3017     (proc)();
3018 }
3019
3020 static void
3021 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
3022 {
3023     RecentEngineEvent((int) (intptr_t) addr);
3024 }
3025
3026 // some stuff that must remain in front-end
3027 static Widget mainBar, currentMenu;
3028 static int wtot, nr = 0, widths[10];
3029
3030 void
3031 AppendMenuItem (char *text, char *name, MenuProc *action)
3032 {
3033     int j;
3034     Widget entry;
3035     Arg args[16];
3036
3037     j = 0;
3038     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3039     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3040
3041         if (strcmp(text, "----") == 0) {
3042           entry = XtCreateManagedWidget(text, smeLineObjectClass,
3043                                           currentMenu, args, j);
3044         } else {
3045           XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3046             entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3047                                           currentMenu, args, j+1);
3048             XtAddCallback(entry, XtNcallback,
3049                           (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3050                           (caddr_t) action);
3051             menuWidget[nrOfMenuItems] = entry;
3052         }
3053 }
3054
3055 void
3056 CreateMenuButton (char *name, Menu *mb)
3057 {   // create menu button on main bar, and shell for pull-down list
3058     int i, j;
3059     Arg args[16];
3060     Dimension w;
3061
3062         j = 0;
3063         XtSetArg(args[j], XtNmenuName, XtNewString(name));  j++;
3064         XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name)));  j++;
3065         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3066         mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3067                                        mainBar, args, j);
3068     currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3069                               mainBar, NULL, 0);
3070         j = 0;
3071         XtSetArg(args[j], XtNwidth, &w);                   j++;
3072         XtGetValues(mb->subMenu, args, j);
3073         wtot += mb->textWidth = widths[nr++] = w;
3074 }
3075
3076 Widget
3077 CreateMenuBar (Menu *mb, int boardWidth)
3078 {
3079     int i, j;
3080     Arg args[16];
3081     char menuName[MSG_SIZ];
3082     Dimension w;
3083     Menu *ma = mb;
3084
3085     // create bar itself
3086     j = 0;
3087     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3088     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3089     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3090     mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3091                              formWidget, args, j);
3092
3093     CreateMainMenus(mb); // put menus in bar according to description in back-end
3094
3095     // size buttons to make menu bar fit, clipping menu names where necessary
3096     while(wtot > boardWidth - 40) {
3097         int wmax=0, imax=0;
3098         for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3099         widths[imax]--;
3100         wtot--;
3101     }
3102     for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3103         j = 0;
3104         XtSetArg(args[j], XtNwidth, widths[i]);                   j++;
3105         XtSetValues(ma[i].subMenu, args, j);
3106     }
3107
3108     return mainBar;
3109 }
3110
3111 Widget
3112 CreateButtonBar (MenuItem *mi)
3113 {
3114     int j;
3115     Widget button, buttonBar;
3116     Arg args[16];
3117
3118     j = 0;
3119     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3120     if (tinyLayout) {
3121         XtSetArg(args[j], XtNhSpace, 0); j++;
3122     }
3123     XtSetArg(args[j], XtNborderWidth, 0); j++;
3124     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3125     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3126                                formWidget, args, j);
3127
3128     while (mi->string != NULL) {
3129         j = 0;
3130         if (tinyLayout) {
3131             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3132             XtSetArg(args[j], XtNborderWidth, 0); j++;
3133         }
3134       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3135         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3136                                        buttonBar, args, j);
3137         XtAddCallback(button, XtNcallback,
3138                       (XtCallbackProc) MenuBarSelect,
3139                       (caddr_t) mi->proc);
3140         mi++;
3141     }
3142     return buttonBar;
3143 }
3144
3145 Widget
3146 CreatePieceMenu (char *name, int color)
3147 {
3148     int i;
3149     Widget entry, menu;
3150     Arg args[16];
3151     ChessSquare selection;
3152
3153     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3154                               boardWidget, args, 0);
3155
3156     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3157         String item = pieceMenuStrings[color][i];
3158
3159         if (strcmp(item, "----") == 0) {
3160             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3161                                           menu, NULL, 0);
3162         } else {
3163           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3164             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3165                                 menu, args, 1);
3166             selection = pieceMenuTranslation[color][i];
3167             XtAddCallback(entry, XtNcallback,
3168                           (XtCallbackProc) PieceMenuSelect,
3169                           (caddr_t) selection);
3170             if (selection == WhitePawn || selection == BlackPawn) {
3171                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3172                 XtSetValues(menu, args, 1);
3173             }
3174         }
3175     }
3176     return menu;
3177 }
3178
3179 void
3180 CreatePieceMenus ()
3181 {
3182     int i;
3183     Widget entry;
3184     Arg args[16];
3185     ChessSquare selection;
3186
3187     whitePieceMenu = CreatePieceMenu("menuW", 0);
3188     blackPieceMenu = CreatePieceMenu("menuB", 1);
3189
3190     if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3191     XtRegisterGrabAction(PieceMenuPopup, True,
3192                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3193                          GrabModeAsync, GrabModeAsync);
3194
3195     XtSetArg(args[0], XtNlabel, _("Drop"));
3196     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3197                                   boardWidget, args, 1);
3198     for (i = 0; i < DROP_MENU_SIZE; i++) {
3199         String item = dropMenuStrings[i];
3200
3201         if (strcmp(item, "----") == 0) {
3202             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3203                                           dropMenu, NULL, 0);
3204         } else {
3205           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3206             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3207                                 dropMenu, args, 1);
3208             selection = dropMenuTranslation[i];
3209             XtAddCallback(entry, XtNcallback,
3210                           (XtCallbackProc) DropMenuSelect,
3211                           (caddr_t) selection);
3212         }
3213     }
3214 }
3215
3216 void
3217 SetupDropMenu ()
3218 {
3219     int i, j, count;
3220     char label[32];
3221     Arg args[16];
3222     Widget entry;
3223     char* p;
3224
3225     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3226         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3227         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3228                    dmEnables[i].piece);
3229         XtSetSensitive(entry, p != NULL || !appData.testLegality
3230                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3231                                        && !appData.icsActive));
3232         count = 0;
3233         while (p && *p++ == dmEnables[i].piece) count++;
3234         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3235         j = 0;
3236         XtSetArg(args[j], XtNlabel, label); j++;
3237         XtSetValues(entry, args, j);
3238     }
3239 }
3240
3241 void
3242 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3243 {
3244     String whichMenu; int menuNr = -2;
3245     shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3246     if (event->type == ButtonRelease)
3247         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3248     else if (event->type == ButtonPress)
3249         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3250     switch(menuNr) {
3251       case 0: whichMenu = params[0]; break;
3252       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3253       case 2:
3254       case -1: if (errorUp) ErrorPopDown();
3255       default: return;
3256     }
3257     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3258 }
3259
3260 static void
3261 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3262 {
3263     if (pmFromX < 0 || pmFromY < 0) return;
3264     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3265 }
3266
3267 static void
3268 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3269 {
3270     if (pmFromX < 0 || pmFromY < 0) return;
3271     DropMenuEvent(piece, pmFromX, pmFromY);
3272 }
3273
3274 void
3275 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3276 {
3277     shiftKey = prms[0][0] & 1;
3278     ClockClick(0);
3279 }
3280
3281 void
3282 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3283 {
3284     shiftKey = prms[0][0] & 1;
3285     ClockClick(1);
3286 }
3287
3288
3289 static void
3290 do_flash_delay (unsigned long msec)
3291 {
3292     TimeDelay(msec);
3293 }
3294
3295 void
3296 DrawBorder (int x, int y, int type)
3297 {
3298     GC gc = lineGC;
3299
3300     if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3301
3302     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3303                    squareSize+lineGap, squareSize+lineGap);
3304 }
3305
3306 static int
3307 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
3308 {
3309     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3310     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3311     *x0 = 0; *y0 = 0;
3312     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3313     if(textureW[kind] < W*squareSize)
3314         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3315     else
3316         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3317     if(textureH[kind] < H*squareSize)
3318         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3319     else
3320         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3321     return 1;
3322 }
3323
3324 static void
3325 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3326 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3327     int x0, y0;
3328     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3329         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3330                   squareSize, squareSize, x*fac, y*fac);
3331     } else
3332     if (useImages && useImageSqs) {
3333         Pixmap pm;
3334         switch (color) {
3335           case 1: /* light */
3336             pm = xpmLightSquare;
3337             break;
3338           case 0: /* dark */
3339             pm = xpmDarkSquare;
3340             break;
3341           case 2: /* neutral */
3342           default:
3343             pm = xpmJailSquare;
3344             break;
3345         }
3346         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3347                   squareSize, squareSize, x*fac, y*fac);
3348     } else {
3349         GC gc;
3350         switch (color) {
3351           case 1: /* light */
3352             gc = lightSquareGC;
3353             break;
3354           case 0: /* dark */
3355             gc = darkSquareGC;
3356             break;
3357           case 2: /* neutral */
3358           default:
3359             gc = jailSquareGC;
3360             break;
3361         }
3362         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3363     }
3364 }
3365
3366 /*
3367    I split out the routines to draw a piece so that I could
3368    make a generic flash routine.
3369 */
3370 static void
3371 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3372 {
3373     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3374     switch (square_color) {
3375       case 1: /* light */
3376       case 2: /* neutral */
3377       default:
3378         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3379                   ? *pieceToOutline(piece)
3380                   : *pieceToSolid(piece),
3381                   dest, bwPieceGC, 0, 0,
3382                   squareSize, squareSize, x, y);
3383         break;
3384       case 0: /* dark */
3385         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3386                   ? *pieceToSolid(piece)
3387                   : *pieceToOutline(piece),
3388                   dest, wbPieceGC, 0, 0,
3389                   squareSize, squareSize, x, y);
3390         break;
3391     }
3392 }
3393
3394 static void
3395 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3396 {
3397     switch (square_color) {
3398       case 1: /* light */
3399       case 2: /* neutral */
3400       default:
3401         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3402                    ? *pieceToOutline(piece)
3403                    : *pieceToSolid(piece),
3404                    dest, bwPieceGC, 0, 0,
3405                    squareSize, squareSize, x, y, 1);
3406         break;
3407       case 0: /* dark */
3408         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3409                    ? *pieceToSolid(piece)
3410                    : *pieceToOutline(piece),
3411                    dest, wbPieceGC, 0, 0,
3412                    squareSize, squareSize, x, y, 1);
3413         break;
3414     }
3415 }
3416
3417 static void
3418 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3419 {
3420     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3421     switch (square_color) {
3422       case 1: /* light */
3423         XCopyPlane(xDisplay, *pieceToSolid(piece),
3424                    dest, (int) piece < (int) BlackPawn
3425                    ? wlPieceGC : blPieceGC, 0, 0,
3426                    squareSize, squareSize, x, y, 1);
3427         break;
3428       case 0: /* dark */
3429         XCopyPlane(xDisplay, *pieceToSolid(piece),
3430                    dest, (int) piece < (int) BlackPawn
3431                    ? wdPieceGC : bdPieceGC, 0, 0,
3432                    squareSize, squareSize, x, y, 1);
3433         break;
3434       case 2: /* neutral */
3435       default:
3436         XCopyPlane(xDisplay, *pieceToSolid(piece),
3437                    dest, (int) piece < (int) BlackPawn
3438                    ? wjPieceGC : bjPieceGC, 0, 0,
3439                    squareSize, squareSize, x, y, 1);
3440         break;
3441     }
3442 }
3443
3444 static void
3445 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3446 {
3447     int kind, p = piece;
3448
3449     switch (square_color) {
3450       case 1: /* light */
3451       case 2: /* neutral */
3452       default:
3453         if ((int)piece < (int) BlackPawn) {
3454             kind = 0;
3455         } else {
3456             kind = 2;
3457             piece -= BlackPawn;
3458         }
3459         break;
3460       case 0: /* dark */
3461         if ((int)piece < (int) BlackPawn) {
3462             kind = 1;
3463         } else {
3464             kind = 3;
3465             piece -= BlackPawn;
3466         }
3467         break;
3468     }
3469     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3470     if(useTexture & square_color+1) {
3471         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3472         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3473         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3474         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3475         XSetClipMask(xDisplay, wlPieceGC, None);
3476         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3477     } else
3478     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3479               dest, wlPieceGC, 0, 0,
3480               squareSize, squareSize, x, y);
3481 }
3482
3483 typedef void (*DrawFunc)();
3484
3485 DrawFunc
3486 ChooseDrawFunc ()
3487 {
3488     if (appData.monoMode) {
3489         if (DefaultDepth(xDisplay, xScreen) == 1) {
3490             return monoDrawPiece_1bit;
3491         } else {
3492             return monoDrawPiece;
3493         }
3494     } else {
3495         if (useImages)
3496           return colorDrawPieceImage;
3497         else
3498           return colorDrawPiece;
3499     }
3500 }
3501
3502 void
3503 DrawDot (int marker, int x, int y, int r)
3504 {
3505         if(appData.monoMode) {
3506             XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3507                     x, y, r, r, 0, 64*360);
3508             XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3509                     x, y, r, r, 0, 64*360);
3510         } else
3511         XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3512                     x, y, r, r, 0, 64*360);
3513 }
3514
3515 void
3516 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3517 {   // basic front-end board-draw function: takes care of everything that can be in square:
3518     // piece, background, coordinate/count, marker dot
3519     int direction, font_ascent, font_descent;
3520     XCharStruct overall;
3521     DrawFunc drawfunc;
3522
3523     if (piece == EmptySquare) {
3524         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3525     } else {
3526         drawfunc = ChooseDrawFunc();
3527         drawfunc(piece, square_color, x, y, xBoardWindow);
3528     }
3529
3530     if(align) { // square carries inscription (coord or piece count)
3531         int xx = x, yy = y;
3532         GC hGC = align < 3 ? coordGC : countGC;
3533         // first calculate where it goes
3534         XTextExtents(countFontStruct, string, 1, &direction,
3535                          &font_ascent, &font_descent, &overall);
3536         if (align == 1) {
3537             xx += squareSize - overall.width - 2;
3538             yy += squareSize - font_descent - 1;
3539         } else if (align == 2) {
3540             xx += 2, yy += font_ascent + 1;
3541         } else if (align == 3) {
3542             xx += squareSize - overall.width - 2;
3543             yy += font_ascent + 1;
3544         } else if (align == 4) {
3545             xx += 2, yy += font_ascent + 1;
3546         }
3547         // then draw it
3548         if (appData.monoMode) {
3549             XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3550         } else {
3551             XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3552         }
3553     }
3554
3555     if(marker) { // print fat marker dot, if requested
3556         DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3557     }
3558 }
3559
3560 void
3561 FlashDelay (int flash_delay)
3562 {
3563         XSync(xDisplay, False);
3564         if(flash_delay) do_flash_delay(flash_delay);
3565 }
3566
3567 double
3568 Fraction (int x, int start, int stop)
3569 {
3570    double f = ((double) x - start)/(stop - start);
3571    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3572    return f;
3573 }
3574
3575 static WindowPlacement wpNew;
3576
3577 void
3578 CoDrag (Widget sh, WindowPlacement *wp)
3579 {
3580     Arg args[16];
3581     int j=0, touch=0, fudge = 2;
3582     GetActualPlacement(sh, wp);
3583     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
3584     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
3585     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3586     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
3587     if(!touch ) return; // only windows that touch co-move
3588     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3589         int heightInc = wpNew.height - wpMain.height;
3590         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3591         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3592         wp->y += fracTop * heightInc;
3593         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3594         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3595     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3596         int widthInc = wpNew.width - wpMain.width;
3597         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3598         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3599         wp->y += fracLeft * widthInc;
3600         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3601         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3602     }
3603     wp->x += wpNew.x - wpMain.x;
3604     wp->y += wpNew.y - wpMain.y;
3605     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3606     if(touch == 3) wp->y += wpNew.height - wpMain.height;
3607     XtSetArg(args[j], XtNx, wp->x); j++;
3608     XtSetArg(args[j], XtNy, wp->y); j++;
3609     XtSetValues(sh, args, j);
3610 }
3611
3612 static XtIntervalId delayedDragID = 0;
3613
3614 void
3615 DragProc ()
3616 {
3617         GetActualPlacement(shellWidget, &wpNew);
3618         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3619            wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3620             return; // false alarm
3621         if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3622         if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3623         if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3624         if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3625         wpMain = wpNew;
3626         DrawPosition(True, NULL);
3627         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3628 }
3629
3630
3631 void
3632 DelayedDrag ()
3633 {
3634     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3635     delayedDragID =
3636       XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3637 }
3638
3639 /* Why is this needed on some versions of X? */
3640 void
3641 EventProc (Widget widget, caddr_t unused, XEvent *event)
3642 {
3643     if (!XtIsRealized(widget))
3644       return;
3645     switch (event->type) {
3646       case ConfigureNotify: // main window is being dragged: drag attached windows with it
3647         if(appData.useStickyWindows)
3648             DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3649         break;
3650       case Expose:
3651         if (event->xexpose.count > 0) return;  /* no clipping is done */
3652         DrawPosition(True, NULL);
3653         if(twoBoards) { // [HGM] dual: draw other board in other orientation
3654             flipView = !flipView; partnerUp = !partnerUp;
3655             DrawPosition(True, NULL);
3656             flipView = !flipView; partnerUp = !partnerUp;
3657         }
3658         break;
3659       case MotionNotify:
3660         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3661       default:
3662         return;
3663     }
3664 }
3665 /* end why */
3666
3667 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3668 void
3669 DrawSeekAxis (int x, int y, int xTo, int yTo)
3670 {
3671       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3672 }
3673
3674 void
3675 DrawSeekBackground (int left, int top, int right, int bottom)
3676 {
3677     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3678 }
3679
3680 void
3681 DrawSeekText (char *buf, int x, int y)
3682 {
3683     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3684 }
3685
3686 void
3687 DrawSeekDot (int x, int y, int colorNr)
3688 {
3689     int square = colorNr & 0x80;
3690     GC color;
3691     colorNr &= 0x7F;
3692     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3693     if(square)
3694         XFillRectangle(xDisplay, xBoardWindow, color,
3695                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3696     else
3697         XFillArc(xDisplay, xBoardWindow, color,
3698                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3699 }
3700
3701 void
3702 DrawGrid (int second)
3703 {
3704           XDrawSegments(xDisplay, xBoardWindow, lineGC,
3705                         second ? secondSegments : // [HGM] dual
3706                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3707 }
3708
3709
3710 /*
3711  * event handler for redrawing the board
3712  */
3713 void
3714 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3715 {
3716     DrawPosition(True, NULL);
3717 }
3718
3719
3720 /*
3721  * event handler for parsing user moves
3722  */
3723 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3724 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3725 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3726 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
3727 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3728 //       and at the end FinishMove() to perform the move after optional promotion popups.
3729 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3730 void
3731 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3732 {
3733     if (w != boardWidget || errorExitStatus != -1) return;
3734     if(nprms) shiftKey = !strcmp(prms[0], "1");
3735
3736     if (promotionUp) {
3737         if (event->type == ButtonPress) {
3738             XtPopdown(promotionShell);
3739             XtDestroyWidget(promotionShell);
3740             promotionUp = False;
3741             ClearHighlights();
3742             fromX = fromY = -1;
3743         } else {
3744             return;
3745         }
3746     }
3747
3748     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3749     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
3750     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3751 }
3752
3753 void
3754 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3755 {
3756     if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3757     DragPieceMove(event->xmotion.x, event->xmotion.y);
3758 }
3759
3760 void
3761 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3762 {   // [HGM] pv: walk PV
3763     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3764 }
3765
3766 static int savedIndex;  /* gross that this is global */
3767
3768 void
3769 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3770 {
3771         String val;
3772         XawTextPosition index, dummy;
3773         Arg arg;
3774
3775         XawTextGetSelectionPos(w, &index, &dummy);
3776         XtSetArg(arg, XtNstring, &val);
3777         XtGetValues(w, &arg, 1);
3778         ReplaceComment(savedIndex, val);
3779         if(savedIndex != currentMove) ToNrEvent(savedIndex);
3780         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3781 }
3782
3783 void
3784 EditCommentPopUp (int index, char *title, char *text)
3785 {
3786     savedIndex = index;
3787     if (text == NULL) text = "";
3788     NewCommentPopup(title, text, index);
3789 }
3790
3791 void
3792 ICSInputBoxPopUp ()
3793 {
3794     InputBoxPopup();
3795 }
3796
3797 extern Option boxOptions[];
3798
3799 void
3800 ICSInputBoxPopDown ()
3801 {
3802     PopDown(InputBoxDlg);
3803 }
3804
3805 void
3806 CommentPopUp (char *title, char *text)
3807 {
3808     savedIndex = currentMove; // [HGM] vari
3809     NewCommentPopup(title, text, currentMove);
3810 }
3811
3812 void
3813 CommentPopDown ()
3814 {
3815     PopDown(CommentDlg);
3816 }
3817
3818 static char *openName;
3819 FILE *openFP;
3820
3821 void
3822 DelayedLoad ()
3823 {
3824   (void) (*fileProc)(openFP, 0, openName);
3825 }
3826
3827 void
3828 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3829 {
3830     fileProc = proc;            /* I can't see a way not */
3831     fileOpenMode = openMode;    /*   to use globals here */
3832     {   // [HGM] use file-selector dialog stolen from Ghostview
3833         int index; // this is not supported yet
3834         if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3835                            (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3836           // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3837           ScheduleDelayedEvent(&DelayedLoad, 50);
3838     }
3839 }
3840
3841 void
3842 FileNamePopDown ()
3843 {
3844     if (!filenameUp) return;
3845     XtPopdown(fileNameShell);
3846     XtDestroyWidget(fileNameShell);
3847     filenameUp = False;
3848     ModeHighlight();
3849 }
3850
3851 void
3852 FileNameCallback (Widget w, XtPointer client_data, XtPointer call_data)
3853 {
3854     String name;
3855     Arg args[16];
3856
3857     XtSetArg(args[0], XtNlabel, &name);
3858     XtGetValues(w, args, 1);
3859
3860     if (strcmp(name, _("cancel")) == 0) {
3861         FileNamePopDown();
3862         return;
3863     }
3864
3865     FileNameAction(w, NULL, NULL, NULL);
3866 }
3867
3868 void
3869 FileNameAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3870 {
3871     char buf[MSG_SIZ];
3872     String name;
3873     FILE *f;
3874     char *p, *fullname;
3875     int index;
3876
3877     name = XawDialogGetValueString(w = XtParent(w));
3878
3879     if ((name != NULL) && (*name != NULLCHAR)) {
3880         safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
3881         XtPopdown(w = XtParent(XtParent(w)));
3882         XtDestroyWidget(w);
3883         filenameUp = False;
3884
3885         p = strrchr(buf, ' ');
3886         if (p == NULL) {
3887             index = 0;
3888         } else {
3889             *p++ = NULLCHAR;
3890             index = atoi(p);
3891         }
3892         fullname = ExpandPathName(buf);
3893         if (!fullname) {
3894             ErrorPopUp(_("Error"), _("Can't open file"), FALSE);
3895         }
3896         else {
3897             f = fopen(fullname, fileOpenMode);
3898             if (f == NULL) {
3899                 DisplayError(_("Failed to open file"), errno);
3900             } else {
3901                 (void) (*fileProc)(f, index, buf);
3902             }
3903         }
3904         ModeHighlight();
3905         return;
3906     }
3907
3908     XtPopdown(w = XtParent(XtParent(w)));
3909     XtDestroyWidget(w);
3910     filenameUp = False;
3911     ModeHighlight();
3912 }
3913
3914 void
3915 PromotionPopUp ()
3916 {
3917     Arg args[16];
3918     Widget dialog, layout;
3919     Position x, y;
3920     Dimension bw_width, pw_width;
3921     int j;
3922     char *PromoChars = "wglcqrbnkac+=\0";
3923
3924     j = 0;
3925     XtSetArg(args[j], XtNwidth, &bw_width); j++;
3926     XtGetValues(boardWidget, args, j);
3927
3928     j = 0;
3929     XtSetArg(args[j], XtNresizable, True); j++;
3930     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3931     promotionShell =
3932       XtCreatePopupShell("Promotion", transientShellWidgetClass,
3933                          shellWidget, args, j);
3934     layout =
3935       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3936                             layoutArgs, XtNumber(layoutArgs));
3937
3938     j = 0;
3939     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3940     XtSetArg(args[j], XtNborderWidth, 0); j++;
3941     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3942                                    layout, args, j);
3943
3944   if(gameInfo.variant != VariantShogi) {
3945    if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3946       XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3947       XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3948       XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3949       XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3950     } else {
3951     XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3952     XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3953     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3954     XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3955     }
3956     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3957         gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3958         gameInfo.variant == VariantGiveaway) {
3959       XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3960     }
3961     if(gameInfo.variant == VariantCapablanca ||
3962        gameInfo.variant == VariantGothic ||
3963        gameInfo.variant == VariantCapaRandom) {
3964       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3965       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3966     }
3967   } else // [HGM] shogi
3968   {
3969       XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3970       XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3971   }
3972     XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3973
3974     XtRealizeWidget(promotionShell);
3975     CatchDeleteWindow(promotionShell, "PromotionPopDown");
3976
3977     j = 0;
3978     XtSetArg(args[j], XtNwidth, &pw_width); j++;
3979     XtGetValues(promotionShell, args, j);
3980
3981     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3982                       lineGap + squareSize/3 +
3983                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3984                        0 : 6*(squareSize + lineGap)), &x, &y);
3985
3986     j = 0;
3987     XtSetArg(args[j], XtNx, x); j++;
3988     XtSetArg(args[j], XtNy, y); j++;
3989     XtSetValues(promotionShell, args, j);
3990
3991     XtPopup(promotionShell, XtGrabNone);
3992
3993     promotionUp = True;
3994 }
3995
3996 void
3997 PromotionPopDown ()
3998 {
3999     if (!promotionUp) return;
4000     XtPopdown(promotionShell);
4001     XtDestroyWidget(promotionShell);
4002     promotionUp = False;
4003 }
4004
4005 void
4006 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4007 {
4008     int promoChar = * (const char *) client_data;
4009
4010     PromotionPopDown();
4011
4012     if (fromX == -1) return;
4013
4014     if (! promoChar) {
4015         fromX = fromY = -1;
4016         ClearHighlights();
4017         return;
4018     }
4019     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
4020
4021     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4022     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4023     fromX = fromY = -1;
4024 }
4025
4026
4027 void
4028 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
4029 {
4030     dialogError = errorUp = False;
4031     XtPopdown(w = XtParent(XtParent(XtParent(w))));
4032     XtDestroyWidget(w);
4033     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4034 }
4035
4036
4037 void
4038 ErrorPopDown ()
4039 {
4040     if (!errorUp) return;
4041     dialogError = errorUp = False;
4042     XtPopdown(errorShell);
4043     XtDestroyWidget(errorShell);
4044     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
4045 }
4046
4047 void
4048 ErrorPopUp (char *title, char *label, int modal)
4049 {
4050     Arg args[16];
4051     Widget dialog, layout;
4052     Position x, y;
4053     int xx, yy;
4054     Window junk;
4055     Dimension bw_width, pw_width;
4056     Dimension pw_height;
4057     int i;
4058
4059     i = 0;
4060     XtSetArg(args[i], XtNresizable, True);  i++;
4061     XtSetArg(args[i], XtNtitle, title); i++;
4062     errorShell =
4063       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
4064                          shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
4065     layout =
4066       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
4067                             layoutArgs, XtNumber(layoutArgs));
4068
4069     i = 0;
4070     XtSetArg(args[i], XtNlabel, label); i++;
4071     XtSetArg(args[i], XtNborderWidth, 0); i++;
4072     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
4073                                    layout, args, i);
4074
4075     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
4076
4077     XtRealizeWidget(errorShell);
4078     CatchDeleteWindow(errorShell, "ErrorPopDown");
4079
4080     i = 0;
4081     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
4082     XtGetValues(boardWidget, args, i);
4083     i = 0;
4084     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
4085     XtSetArg(args[i], XtNheight, &pw_height);  i++;
4086     XtGetValues(errorShell, args, i);
4087
4088 #ifdef NOTDEF
4089     /* This code seems to tickle an X bug if it is executed too soon
4090        after xboard starts up.  The coordinates get transformed as if
4091        the main window was positioned at (0, 0).
4092        */
4093     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
4094                       0 - pw_height + squareSize / 3, &x, &y);
4095 #else
4096     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
4097                           RootWindowOfScreen(XtScreen(boardWidget)),
4098                           (bw_width - pw_width) / 2,
4099                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
4100     x = xx;
4101     y = yy;
4102 #endif
4103     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4104
4105     i = 0;
4106     XtSetArg(args[i], XtNx, x);  i++;
4107     XtSetArg(args[i], XtNy, y);  i++;
4108     XtSetValues(errorShell, args, i);
4109
4110     errorUp = True;
4111     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
4112 }
4113
4114 /* Disable all user input other than deleting the window */
4115 static int frozen = 0;
4116
4117 void
4118 FreezeUI ()
4119 {
4120   if (frozen) return;
4121   /* Grab by a widget that doesn't accept input */
4122   XtAddGrab(messageWidget, TRUE, FALSE);
4123   frozen = 1;
4124 }
4125
4126 /* Undo a FreezeUI */
4127 void
4128 ThawUI ()
4129 {
4130   if (!frozen) return;
4131   XtRemoveGrab(messageWidget);
4132   frozen = 0;
4133 }
4134
4135 void
4136 ModeHighlight ()
4137 {
4138     Arg args[16];
4139     static int oldPausing = FALSE;
4140     static GameMode oldmode = (GameMode) -1;
4141     char *wname;
4142
4143     if (!boardWidget || !XtIsRealized(boardWidget)) return;
4144
4145     if (pausing != oldPausing) {
4146         oldPausing = pausing;
4147         MarkMenuItem("Pause", pausing);
4148
4149         if (appData.showButtonBar) {
4150           /* Always toggle, don't set.  Previous code messes up when
4151              invoked while the button is pressed, as releasing it
4152              toggles the state again. */
4153           {
4154             Pixel oldbg, oldfg;
4155             XtSetArg(args[0], XtNbackground, &oldbg);
4156             XtSetArg(args[1], XtNforeground, &oldfg);
4157             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4158                         args, 2);
4159             XtSetArg(args[0], XtNbackground, oldfg);
4160             XtSetArg(args[1], XtNforeground, oldbg);
4161           }
4162           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4163         }
4164     }
4165
4166     wname = ModeToWidgetName(oldmode);
4167     if (wname != NULL) {
4168         MarkMenuItem(wname, False);
4169     }
4170     wname = ModeToWidgetName(gameMode);
4171     if (wname != NULL) {
4172         MarkMenuItem(wname, True);
4173     }
4174     oldmode = gameMode;
4175     MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4176
4177     /* Maybe all the enables should be handled here, not just this one */
4178     EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4179 }
4180
4181
4182 /*
4183  * Button/menu procedures
4184  */
4185 int
4186 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4187 {
4188     cmailMsgLoaded = FALSE;
4189     if (gameNumber == 0) {
4190         int error = GameListBuild(f);
4191         if (error) {
4192             DisplayError(_("Cannot build game list"), error);
4193         } else if (!ListEmpty(&gameList) &&
4194                    ((ListGame *) gameList.tailPred)->number > 1) {
4195             GameListPopUp(f, title);
4196             return TRUE;
4197         }
4198         GameListDestroy();
4199         gameNumber = 1;
4200     }
4201     return LoadGame(f, gameNumber, title, FALSE);
4202 }
4203
4204 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4205 char *selected_fen_position=NULL;
4206
4207 Boolean
4208 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4209                        Atom *type_return, XtPointer *value_return,
4210                        unsigned long *length_return, int *format_return)
4211 {
4212   char *selection_tmp;
4213
4214   if (!selected_fen_position) return False; /* should never happen */
4215   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4216     /* note: since no XtSelectionDoneProc was registered, Xt will
4217      * automatically call XtFree on the value returned.  So have to
4218      * make a copy of it allocated with XtMalloc */
4219     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4220     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4221
4222     *value_return=selection_tmp;
4223     *length_return=strlen(selection_tmp);
4224     *type_return=*target;
4225     *format_return = 8; /* bits per byte */
4226     return True;
4227   } else if (*target == XA_TARGETS(xDisplay)) {
4228     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4229     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4230     targets_tmp[1] = XA_STRING;
4231     *value_return = targets_tmp;
4232     *type_return = XA_ATOM;
4233     *length_return = 2;
4234 #if 0
4235     // This code leads to a read of value_return out of bounds on 64-bit systems.
4236     // Other code which I have seen always sets *format_return to 32 independent of
4237     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4238     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4239     *format_return = 8 * sizeof(Atom);
4240     if (*format_return > 32) {
4241       *length_return *= *format_return / 32;
4242       *format_return = 32;
4243     }
4244 #else
4245     *format_return = 32;
4246 #endif
4247     return True;
4248   } else {
4249     return False;
4250   }
4251 }
4252
4253 /* note: when called from menu all parameters are NULL, so no clue what the
4254  * Widget which was clicked on was, or what the click event was
4255  */
4256 void
4257 CopyPositionProc ()
4258 {
4259     /*
4260      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4261      * have a notion of a position that is selected but not copied.
4262      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4263      */
4264     if(gameMode == EditPosition) EditPositionDone(TRUE);
4265     if (selected_fen_position) free(selected_fen_position);
4266     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4267     if (!selected_fen_position) return;
4268     XtOwnSelection(menuBarWidget, XA_PRIMARY,
4269                    CurrentTime,
4270                    SendPositionSelection,
4271                    NULL/* lose_ownership_proc */ ,
4272                    NULL/* transfer_done_proc */);
4273     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4274                    CurrentTime,
4275                    SendPositionSelection,
4276                    NULL/* lose_ownership_proc */ ,
4277                    NULL/* transfer_done_proc */);
4278 }
4279
4280 /* function called when the data to Paste is ready */
4281 static void
4282 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4283                  Atom *type, XtPointer value, unsigned long *len, int *format)
4284 {
4285   char *fenstr=value;
4286   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4287   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4288   EditPositionPasteFEN(fenstr);
4289   XtFree(value);
4290 }
4291
4292 /* called when Paste Position button is pressed,
4293  * all parameters will be NULL */
4294 void
4295 PastePositionProc ()
4296 {
4297     XtGetSelectionValue(menuBarWidget,
4298       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4299       /* (XtSelectionCallbackProc) */ PastePositionCB,
4300       NULL, /* client_data passed to PastePositionCB */
4301
4302       /* better to use the time field from the event that triggered the
4303        * call to this function, but that isn't trivial to get
4304        */
4305       CurrentTime
4306     );
4307     return;
4308 }
4309
4310 static Boolean
4311 SendGameSelection (Widget w, Atom *selection, Atom *target,
4312                    Atom *type_return, XtPointer *value_return,
4313                    unsigned long *length_return, int *format_return)
4314 {
4315   char *selection_tmp;
4316
4317   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4318     FILE* f = fopen(gameCopyFilename, "r");
4319     long len;
4320     size_t count;
4321     if (f == NULL) return False;
4322     fseek(f, 0, 2);
4323     len = ftell(f);
4324     rewind(f);
4325     selection_tmp = XtMalloc(len + 1);