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);
4326     count = fread(selection_tmp, 1, len, f);
4327     fclose(f);
4328     if (len != count) {
4329       XtFree(selection_tmp);
4330       return False;
4331     }
4332     selection_tmp[len] = NULLCHAR;
4333     *value_return = selection_tmp;
4334     *length_return = len;
4335     *type_return = *target;
4336     *format_return = 8; /* bits per byte */
4337     return True;
4338   } else if (*target == XA_TARGETS(xDisplay)) {
4339     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4340     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4341     targets_tmp[1] = XA_STRING;
4342     *value_return = targets_tmp;
4343     *type_return = XA_ATOM;
4344     *length_return = 2;
4345 #if 0
4346     // This code leads to a read of value_return out of bounds on 64-bit systems.
4347     // Other code which I have seen always sets *format_return to 32 independent of
4348     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4349     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4350     *format_return = 8 * sizeof(Atom);
4351     if (*format_return > 32) {
4352       *length_return *= *format_return / 32;
4353       *format_return = 32;
4354     }
4355 #else
4356     *format_return = 32;
4357 #endif
4358     return True;
4359   } else {
4360     return False;
4361   }
4362 }
4363
4364 void
4365 CopySomething ()
4366 {
4367   /*
4368    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4369    * have a notion of a game that is selected but not copied.
4370    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4371    */
4372   XtOwnSelection(menuBarWidget, XA_PRIMARY,
4373                  CurrentTime,
4374                  SendGameSelection,
4375                  NULL/* lose_ownership_proc */ ,
4376                  NULL/* transfer_done_proc */);
4377   XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4378                  CurrentTime,
4379                  SendGameSelection,
4380                  NULL/* lose_ownership_proc */ ,
4381                  NULL/* transfer_done_proc */);
4382 }
4383
4384 /* note: when called from menu all parameters are NULL, so no clue what the
4385  * Widget which was clicked on was, or what the click event was
4386  */
4387 /* function called when the data to Paste is ready */
4388 static void
4389 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4390              Atom *type, XtPointer value, unsigned long *len, int *format)
4391 {
4392   FILE* f;
4393   if (value == NULL || *len == 0) {
4394     return; /* nothing had been selected to copy */
4395   }
4396   f = fopen(gamePasteFilename, "w");
4397   if (f == NULL) {
4398     DisplayError(_("Can't open temp file"), errno);
4399     return;
4400   }
4401   fwrite(value, 1, *len, f);
4402   fclose(f);
4403   XtFree(value);
4404   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4405 }
4406
4407 /* called when Paste Game button is pressed,
4408  * all parameters will be NULL */
4409 void
4410 PasteGameProc ()
4411 {
4412     XtGetSelectionValue(menuBarWidget,
4413       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4414       /* (XtSelectionCallbackProc) */ PasteGameCB,
4415       NULL, /* client_data passed to PasteGameCB */
4416
4417       /* better to use the time field from the event that triggered the
4418        * call to this function, but that isn't trivial to get
4419        */
4420       CurrentTime
4421     );
4422     return;
4423 }
4424
4425
4426 void
4427 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4428 {
4429     QuitProc();
4430 }
4431
4432 static void
4433 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4434 {
4435     char buf[10], keys[32];
4436     KeySym sym;
4437     KeyCode metaL, metaR; //, ctrlL, ctrlR;
4438     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4439     XQueryKeymap(xDisplay,keys);
4440     metaL = XKeysymToKeycode(xDisplay, XK_Meta_L);
4441     metaR = XKeysymToKeycode(xDisplay, XK_Meta_R);
4442 //    ctrlL = XKeysymToKeycode(xDisplay, XK_Control_L);
4443 //    ctrlR = XKeysymToKeycode(xDisplay, XK_Control_R);
4444     if ( n == 1 && *buf >= 32 // printable
4445          && !(keys[metaL>>3]&1<<(metaL&7)) && !(keys[metaR>>3]&1<<(metaR&7)) // no alt key pressed
4446 //       && !(keys[ctrlL>>3]&1<<(ctrlL&7)) && !(keys[ctrlR>>3]&1<<(ctrlR&7)) // no ctrl key pressed
4447        ) BoxAutoPopUp (buf);
4448 }
4449
4450 static void
4451 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4452 {   // [HGM] input: let up-arrow recall previous line from history
4453     IcsKey(1);
4454 }
4455
4456 static void
4457 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4458 {   // [HGM] input: let down-arrow recall next line from history
4459     IcsKey(-1);
4460 }
4461
4462 static void
4463 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4464 {
4465     IcsKey(0);
4466 }
4467
4468 void
4469 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4470 {
4471         if (!TempBackwardActive) {
4472                 TempBackwardActive = True;
4473                 BackwardEvent();
4474         }
4475 }
4476
4477 void
4478 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4479 {
4480         /* Check to see if triggered by a key release event for a repeating key.
4481          * If so the next queued event will be a key press of the same key at the same time */
4482         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4483                 XEvent next;
4484                 XPeekEvent(xDisplay, &next);
4485                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4486                         next.xkey.keycode == event->xkey.keycode)
4487                                 return;
4488         }
4489     ForwardEvent();
4490         TempBackwardActive = False;
4491 }
4492
4493 void
4494 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4495 {   // called as key binding
4496     char buf[MSG_SIZ];
4497     String name;
4498     if (nprms && *nprms > 0)
4499       name = prms[0];
4500     else
4501       name = "xboard";
4502     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4503     system(buf);
4504 }
4505
4506 void
4507 DisplayMessage (char *message, char *extMessage)
4508 {
4509   /* display a message in the message widget */
4510
4511   char buf[MSG_SIZ];
4512   Arg arg;
4513
4514   if (extMessage)
4515     {
4516       if (*message)
4517         {
4518           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
4519           message = buf;
4520         }
4521       else
4522         {
4523           message = extMessage;
4524         };
4525     };
4526
4527     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4528
4529   /* need to test if messageWidget already exists, since this function
4530      can also be called during the startup, if for example a Xresource
4531      is not set up correctly */
4532   if(messageWidget)
4533     {
4534       XtSetArg(arg, XtNlabel, message);
4535       XtSetValues(messageWidget, &arg, 1);
4536     };
4537
4538   return;
4539 }
4540
4541 void
4542 SetWindowTitle (char *text, char *title, char *icon)
4543 {
4544     Arg args[16];
4545     int i;
4546     if (appData.titleInWindow) {
4547         i = 0;
4548         XtSetArg(args[i], XtNlabel, text);   i++;
4549         XtSetValues(titleWidget, args, i);
4550     }
4551     i = 0;
4552     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
4553     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
4554     XtSetValues(shellWidget, args, i);
4555     XSync(xDisplay, False);
4556 }
4557
4558
4559 static int
4560 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4561 {
4562     return 0;
4563 }
4564
4565 void
4566 DisplayIcsInteractionTitle (String message)
4567 {
4568   if (oldICSInteractionTitle == NULL) {
4569     /* Magic to find the old window title, adapted from vim */
4570     char *wina = getenv("WINDOWID");
4571     if (wina != NULL) {
4572       Window win = (Window) atoi(wina);
4573       Window root, parent, *children;
4574       unsigned int nchildren;
4575       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4576       for (;;) {
4577         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4578         if (!XQueryTree(xDisplay, win, &root, &parent,
4579                         &children, &nchildren)) break;
4580         if (children) XFree((void *)children);
4581         if (parent == root || parent == 0) break;
4582         win = parent;
4583       }
4584       XSetErrorHandler(oldHandler);
4585     }
4586     if (oldICSInteractionTitle == NULL) {
4587       oldICSInteractionTitle = "xterm";
4588     }
4589   }
4590   printf("\033]0;%s\007", message);
4591   fflush(stdout);
4592 }
4593
4594 char pendingReplyPrefix[MSG_SIZ];
4595 ProcRef pendingReplyPR;
4596
4597 void
4598 AskQuestionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4599 {
4600     if (*nprms != 4) {
4601         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4602                 *nprms);
4603         return;
4604     }
4605     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4606 }
4607
4608 void
4609 AskQuestionPopDown ()
4610 {
4611     if (!askQuestionUp) return;
4612     XtPopdown(askQuestionShell);
4613     XtDestroyWidget(askQuestionShell);
4614     askQuestionUp = False;
4615 }
4616
4617 void
4618 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4619 {
4620     char buf[MSG_SIZ];
4621     int err;
4622     String reply;
4623
4624     reply = XawDialogGetValueString(w = XtParent(w));
4625     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4626     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4627     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4628     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
4629     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4630     AskQuestionPopDown();
4631
4632     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4633 }
4634
4635 void
4636 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4637 {
4638     String name;
4639     Arg args[16];
4640
4641     XtSetArg(args[0], XtNlabel, &name);
4642     XtGetValues(w, args, 1);
4643
4644     if (strcmp(name, _("cancel")) == 0) {
4645         AskQuestionPopDown();
4646     } else {
4647         AskQuestionReplyAction(w, NULL, NULL, NULL);
4648     }
4649 }
4650
4651 void
4652 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4653 {
4654     Arg args[16];
4655     Widget popup, layout, dialog, edit;
4656     Window root, child;
4657     int x, y, i;
4658     int win_x, win_y;
4659     unsigned int mask;
4660
4661     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4662     pendingReplyPR = pr;
4663
4664     i = 0;
4665     XtSetArg(args[i], XtNresizable, True); i++;
4666     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4667     askQuestionShell = popup =
4668       XtCreatePopupShell(title, transientShellWidgetClass,
4669                          shellWidget, args, i);
4670
4671     layout =
4672       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4673                             layoutArgs, XtNumber(layoutArgs));
4674
4675     i = 0;
4676     XtSetArg(args[i], XtNlabel, question); i++;
4677     XtSetArg(args[i], XtNvalue, ""); i++;
4678     XtSetArg(args[i], XtNborderWidth, 0); i++;
4679     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4680                                    layout, args, i);
4681
4682     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4683                        (XtPointer) dialog);
4684     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4685                        (XtPointer) dialog);
4686
4687     XtRealizeWidget(popup);
4688     CatchDeleteWindow(popup, "AskQuestionPopDown");
4689
4690     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4691                   &x, &y, &win_x, &win_y, &mask);
4692
4693     XtSetArg(args[0], XtNx, x - 10);
4694     XtSetArg(args[1], XtNy, y - 30);
4695     XtSetValues(popup, args, 2);
4696
4697     XtPopup(popup, XtGrabExclusive);
4698     askQuestionUp = True;
4699
4700     edit = XtNameToWidget(dialog, "*value");
4701     XtSetKeyboardFocus(popup, edit);
4702 }
4703
4704
4705 void
4706 PlaySound (char *name)
4707 {
4708   if (*name == NULLCHAR) {
4709     return;
4710   } else if (strcmp(name, "$") == 0) {
4711     putc(BELLCHAR, stderr);
4712   } else {
4713     char buf[2048];
4714     char *prefix = "", *sep = "";
4715     if(appData.soundProgram[0] == NULLCHAR) return;
4716     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4717     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4718     system(buf);
4719   }
4720 }
4721
4722 void
4723 RingBell ()
4724 {
4725   PlaySound(appData.soundMove);
4726 }
4727
4728 void
4729 PlayIcsWinSound ()
4730 {
4731   PlaySound(appData.soundIcsWin);
4732 }
4733
4734 void
4735 PlayIcsLossSound ()
4736 {
4737   PlaySound(appData.soundIcsLoss);
4738 }
4739
4740 void
4741 PlayIcsDrawSound ()
4742 {
4743   PlaySound(appData.soundIcsDraw);
4744 }
4745
4746 void
4747 PlayIcsUnfinishedSound ()
4748 {
4749   PlaySound(appData.soundIcsUnfinished);
4750 }
4751
4752 void
4753 PlayAlarmSound ()
4754 {
4755   PlaySound(appData.soundIcsAlarm);
4756 }
4757
4758 void
4759 PlayTellSound ()
4760 {
4761   PlaySound(appData.soundTell);
4762 }
4763
4764 void
4765 EchoOn ()
4766 {
4767     system("stty echo");
4768     noEcho = False;
4769 }
4770
4771 void
4772 EchoOff ()
4773 {
4774     system("stty -echo");
4775     noEcho = True;
4776 }
4777
4778 void
4779 RunCommand (char *buf)
4780 {
4781     system(buf);
4782 }
4783
4784 void
4785 Colorize (ColorClass cc, int continuation)
4786 {
4787     char buf[MSG_SIZ];
4788     int count, outCount, error;
4789
4790     if (textColors[(int)cc].bg > 0) {
4791         if (textColors[(int)cc].fg > 0) {
4792           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4793                    textColors[(int)cc].fg, textColors[(int)cc].bg);
4794         } else {
4795           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4796                    textColors[(int)cc].bg);
4797         }
4798     } else {
4799         if (textColors[(int)cc].fg > 0) {
4800           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4801                     textColors[(int)cc].fg);
4802         } else {
4803           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4804         }
4805     }
4806     count = strlen(buf);
4807     outCount = OutputToProcess(NoProc, buf, count, &error);
4808     if (outCount < count) {
4809         DisplayFatalError(_("Error writing to display"), error, 1);
4810     }
4811
4812     if (continuation) return;
4813     switch (cc) {
4814     case ColorShout:
4815       PlaySound(appData.soundShout);
4816       break;
4817     case ColorSShout:
4818       PlaySound(appData.soundSShout);
4819       break;
4820     case ColorChannel1:
4821       PlaySound(appData.soundChannel1);
4822       break;
4823     case ColorChannel:
4824       PlaySound(appData.soundChannel);
4825       break;
4826     case ColorKibitz:
4827       PlaySound(appData.soundKibitz);
4828       break;
4829     case ColorTell:
4830       PlaySound(appData.soundTell);
4831       break;
4832     case ColorChallenge:
4833       PlaySound(appData.soundChallenge);
4834       break;
4835     case ColorRequest:
4836       PlaySound(appData.soundRequest);
4837       break;
4838     case ColorSeek:
4839       PlaySound(appData.soundSeek);
4840       break;
4841     case ColorNormal:
4842     case ColorNone:
4843     default:
4844       break;
4845     }
4846 }
4847
4848 char *
4849 UserName ()
4850 {
4851     return getpwuid(getuid())->pw_name;
4852 }
4853
4854 static char *
4855 ExpandPathName (char *path)
4856 {
4857     static char static_buf[4*MSG_SIZ];
4858     char *d, *s, buf[4*MSG_SIZ];
4859     struct passwd *pwd;
4860
4861     s = path;
4862     d = static_buf;
4863
4864     while (*s && isspace(*s))
4865       ++s;
4866
4867     if (!*s) {
4868         *d = 0;
4869         return static_buf;
4870     }
4871
4872     if (*s == '~') {
4873         if (*(s+1) == '/') {
4874           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
4875           strcat(d, s+1);
4876         }
4877         else {
4878           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
4879           { char *p; if(p = strchr(buf, '/')) *p = 0; }
4880           pwd = getpwnam(buf);
4881           if (!pwd)
4882             {
4883               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4884                       buf, path);
4885               return NULL;
4886             }
4887           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
4888           strcat(d, strchr(s+1, '/'));
4889         }
4890     }
4891     else
4892       safeStrCpy(d, s, 4*MSG_SIZ );
4893
4894     return static_buf;
4895 }
4896
4897 char *
4898 HostName ()
4899 {
4900     static char host_name[MSG_SIZ];
4901
4902 #if HAVE_GETHOSTNAME
4903     gethostname(host_name, MSG_SIZ);
4904     return host_name;
4905 #else  /* not HAVE_GETHOSTNAME */
4906 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4907     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4908     return host_name;
4909 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4910     return "localhost";
4911 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4912 #endif /* not HAVE_GETHOSTNAME */
4913 }
4914
4915 XtIntervalId delayedEventTimerXID = 0;
4916 DelayedEventCallback delayedEventCallback = 0;
4917
4918 void
4919 FireDelayedEvent ()
4920 {
4921     delayedEventTimerXID = 0;
4922     delayedEventCallback();
4923 }
4924
4925 void
4926 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4927 {
4928     if(delayedEventTimerXID && delayedEventCallback == cb)
4929         // [HGM] alive: replace, rather than add or flush identical event
4930         XtRemoveTimeOut(delayedEventTimerXID);
4931     delayedEventCallback = cb;
4932     delayedEventTimerXID =
4933       XtAppAddTimeOut(appContext, millisec,
4934                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4935 }
4936
4937 DelayedEventCallback
4938 GetDelayedEvent ()
4939 {
4940   if (delayedEventTimerXID) {
4941     return delayedEventCallback;
4942   } else {
4943     return NULL;
4944   }
4945 }
4946
4947 void
4948 CancelDelayedEvent ()
4949 {
4950   if (delayedEventTimerXID) {
4951     XtRemoveTimeOut(delayedEventTimerXID);
4952     delayedEventTimerXID = 0;
4953   }
4954 }
4955
4956 XtIntervalId loadGameTimerXID = 0;
4957
4958 int
4959 LoadGameTimerRunning ()
4960 {
4961     return loadGameTimerXID != 0;
4962 }
4963
4964 int
4965 StopLoadGameTimer ()
4966 {
4967     if (loadGameTimerXID != 0) {
4968         XtRemoveTimeOut(loadGameTimerXID);
4969         loadGameTimerXID = 0;
4970         return TRUE;
4971     } else {
4972         return FALSE;
4973     }
4974 }
4975
4976 void
4977 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4978 {
4979     loadGameTimerXID = 0;
4980     AutoPlayGameLoop();
4981 }
4982
4983 void
4984 StartLoadGameTimer (long millisec)
4985 {
4986     loadGameTimerXID =
4987       XtAppAddTimeOut(appContext, millisec,
4988                       (XtTimerCallbackProc) LoadGameTimerCallback,
4989                       (XtPointer) 0);
4990 }
4991
4992 XtIntervalId analysisClockXID = 0;
4993
4994 void
4995 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4996 {
4997     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4998          || appData.icsEngineAnalyze) { // [DM]
4999         AnalysisPeriodicEvent(0);
5000         StartAnalysisClock();
5001     }
5002 }
5003
5004 void
5005 StartAnalysisClock ()
5006 {
5007     analysisClockXID =
5008       XtAppAddTimeOut(appContext, 2000,
5009                       (XtTimerCallbackProc) AnalysisClockCallback,
5010                       (XtPointer) 0);
5011 }
5012
5013 XtIntervalId clockTimerXID = 0;
5014
5015 int
5016 ClockTimerRunning ()
5017 {
5018     return clockTimerXID != 0;
5019 }
5020
5021 int
5022 StopClockTimer ()
5023 {
5024     if (clockTimerXID != 0) {
5025         XtRemoveTimeOut(clockTimerXID);
5026         clockTimerXID = 0;
5027         return TRUE;
5028     } else {
5029         return FALSE;
5030     }
5031 }
5032
5033 void
5034 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
5035 {
5036     clockTimerXID = 0;
5037     DecrementClocks();
5038 }
5039
5040 void
5041 StartClockTimer (long millisec)
5042 {
5043     clockTimerXID =
5044       XtAppAddTimeOut(appContext, millisec,
5045                       (XtTimerCallbackProc) ClockTimerCallback,
5046                       (XtPointer) 0);
5047 }
5048
5049 void
5050 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
5051 {
5052     char buf[MSG_SIZ];
5053     Arg args[16];
5054
5055     /* check for low time warning */
5056     Pixel foregroundOrWarningColor = timerForegroundPixel;
5057
5058     if (timer > 0 &&
5059         appData.lowTimeWarning &&
5060         (timer / 1000) < appData.icsAlarmTime)
5061       foregroundOrWarningColor = lowTimeWarningColor;
5062
5063     if (appData.clockMode) {
5064       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
5065       XtSetArg(args[0], XtNlabel, buf);
5066     } else {
5067       snprintf(buf, MSG_SIZ, "%s  ", color);
5068       XtSetArg(args[0], XtNlabel, buf);
5069     }
5070
5071     if (highlight) {
5072
5073         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5074         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5075     } else {
5076         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5077         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5078     }
5079
5080     XtSetValues(w, args, 3);
5081 }
5082
5083 void
5084 DisplayWhiteClock (long timeRemaining, int highlight)
5085 {
5086     Arg args[16];
5087
5088     if(appData.noGUI) return;
5089     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
5090     if (highlight && iconPixmap == bIconPixmap) {
5091         iconPixmap = wIconPixmap;
5092         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5093         XtSetValues(shellWidget, args, 1);
5094     }
5095 }
5096
5097 void
5098 DisplayBlackClock (long timeRemaining, int highlight)
5099 {
5100     Arg args[16];
5101
5102     if(appData.noGUI) return;
5103     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
5104     if (highlight && iconPixmap == wIconPixmap) {
5105         iconPixmap = bIconPixmap;
5106         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
5107         XtSetValues(shellWidget, args, 1);
5108     }
5109 }
5110
5111 #define CPNone 0
5112 #define CPReal 1
5113 #define CPComm 2
5114 #define CPSock 3
5115 #define CPLoop 4
5116 typedef int CPKind;
5117
5118 typedef struct {
5119     CPKind kind;
5120     int pid;
5121     int fdTo, fdFrom;
5122 } ChildProc;
5123
5124
5125 int
5126 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
5127 {
5128     char *argv[64], *p;
5129     int i, pid;
5130     int to_prog[2], from_prog[2];
5131     ChildProc *cp;
5132     char buf[MSG_SIZ];
5133
5134     if (appData.debugMode) {
5135         fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5136     }
5137
5138     /* We do NOT feed the cmdLine to the shell; we just
5139        parse it into blank-separated arguments in the
5140        most simple-minded way possible.
5141        */
5142     i = 0;
5143     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
5144     p = buf;
5145     for (;;) {
5146         while(*p == ' ') p++;
5147         argv[i++] = p;
5148         if(*p == '"' || *p == '\'')
5149              p = strchr(++argv[i-1], *p);
5150         else p = strchr(p, ' ');
5151         if (p == NULL) break;
5152         *p++ = NULLCHAR;
5153     }
5154     argv[i] = NULL;
5155
5156     SetUpChildIO(to_prog, from_prog);
5157
5158     if ((pid = fork()) == 0) {
5159         /* Child process */
5160         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5161         close(to_prog[1]);     // first close the unused pipe ends
5162         close(from_prog[0]);
5163         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
5164         dup2(from_prog[1], 1);
5165         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5166         close(from_prog[1]);                   // and closing again loses one of the pipes!
5167         if(fileno(stderr) >= 2) // better safe than sorry...
5168                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5169
5170         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5171             perror(dir);
5172             exit(1);
5173         }
5174
5175         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5176
5177         execvp(argv[0], argv);
5178
5179         /* If we get here, exec failed */
5180         perror(argv[0]);
5181         exit(1);
5182     }
5183
5184     /* Parent process */
5185     close(to_prog[0]);
5186     close(from_prog[1]);
5187
5188     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5189     cp->kind = CPReal;
5190     cp->pid = pid;
5191     cp->fdFrom = from_prog[0];
5192     cp->fdTo = to_prog[1];
5193     *pr = (ProcRef) cp;
5194     return 0;
5195 }
5196
5197 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5198 static RETSIGTYPE
5199 AlarmCallBack (int n)
5200 {
5201     return;
5202 }
5203
5204 void
5205 DestroyChildProcess (ProcRef pr, int signalType)
5206 {
5207     ChildProc *cp = (ChildProc *) pr;
5208
5209     if (cp->kind != CPReal) return;
5210     cp->kind = CPNone;
5211     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5212         signal(SIGALRM, AlarmCallBack);
5213         alarm(3);
5214         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5215             kill(cp->pid, SIGKILL); // kill it forcefully
5216             wait((int *) 0);        // and wait again
5217         }
5218     } else {
5219         if (signalType) {
5220             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5221         }
5222         /* Process is exiting either because of the kill or because of
5223            a quit command sent by the backend; either way, wait for it to die.
5224         */
5225         wait((int *) 0);
5226     }
5227     close(cp->fdFrom);
5228     close(cp->fdTo);
5229 }
5230
5231 void
5232 InterruptChildProcess (ProcRef pr)
5233 {
5234     ChildProc *cp = (ChildProc *) pr;
5235
5236     if (cp->kind != CPReal) return;
5237     (void) kill(cp->pid, SIGINT); /* stop it thinking */
5238 }
5239
5240 int
5241 OpenTelnet (char *host, char *port, ProcRef *pr)
5242 {
5243     char cmdLine[MSG_SIZ];
5244
5245     if (port[0] == NULLCHAR) {
5246       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5247     } else {
5248       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5249     }
5250     return StartChildProcess(cmdLine, "", pr);
5251 }
5252
5253 int
5254 OpenTCP (char *host, char *port, ProcRef *pr)
5255 {
5256 #if OMIT_SOCKETS
5257     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5258 #else  /* !OMIT_SOCKETS */
5259     struct addrinfo hints;
5260     struct addrinfo *ais, *ai;
5261     int error;
5262     int s=0;
5263     ChildProc *cp;
5264
5265     memset(&hints, 0, sizeof(hints));
5266     hints.ai_family = AF_UNSPEC;
5267     hints.ai_socktype = SOCK_STREAM;
5268
5269     error = getaddrinfo(host, port, &hints, &ais);
5270     if (error != 0) {
5271       /* a getaddrinfo error is not an errno, so can't return it */
5272       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5273               host, port, gai_strerror(error));
5274       return ENOENT;
5275     }
5276      
5277     for (ai = ais; ai != NULL; ai = ai->ai_next) {
5278       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5279         error = errno;
5280         continue;
5281       }
5282       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5283         error = errno;
5284         continue;
5285       }
5286       error = 0;
5287       break;
5288     }
5289     freeaddrinfo(ais);
5290
5291     if (error != 0) {
5292       return error;
5293     }
5294
5295     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5296     cp->kind = CPSock;
5297     cp->pid = 0;
5298     cp->fdFrom = s;
5299     cp->fdTo = s;
5300     *pr = (ProcRef) cp;
5301 #endif /* !OMIT_SOCKETS */
5302
5303     return 0;
5304 }
5305
5306 int
5307 OpenCommPort (char *name, ProcRef *pr)
5308 {
5309     int fd;
5310     ChildProc *cp;
5311
5312     fd = open(name, 2, 0);
5313     if (fd < 0) return errno;
5314
5315     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5316     cp->kind = CPComm;
5317     cp->pid = 0;
5318     cp->fdFrom = fd;
5319     cp->fdTo = fd;
5320     *pr = (ProcRef) cp;
5321
5322     return 0;
5323 }
5324
5325 int
5326 OpenLoopback (ProcRef *pr)
5327 {
5328     ChildProc *cp;
5329     int to[2], from[2];
5330
5331     SetUpChildIO(to, from);
5332
5333     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5334     cp->kind = CPLoop;
5335     cp->pid = 0;
5336     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
5337     cp->fdTo = to[1];
5338     *pr = (ProcRef) cp;
5339
5340     return 0;
5341 }
5342
5343 int
5344 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5345 {
5346     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5347     return -1;
5348 }
5349
5350 #define INPUT_SOURCE_BUF_SIZE 8192
5351
5352 typedef struct {
5353     CPKind kind;
5354     int fd;
5355     int lineByLine;
5356     char *unused;
5357     InputCallback func;
5358     XtInputId xid;
5359     char buf[INPUT_SOURCE_BUF_SIZE];
5360     VOIDSTAR closure;
5361 } InputSource;
5362
5363 void
5364 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5365 {
5366     InputSource *is = (InputSource *) closure;
5367     int count;
5368     int error;
5369     char *p, *q;
5370
5371     if (is->lineByLine) {
5372         count = read(is->fd, is->unused,
5373                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5374         if (count <= 0) {
5375             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5376             return;
5377         }
5378         is->unused += count;
5379         p = is->buf;
5380         while (p < is->unused) {
5381             q = memchr(p, '\n', is->unused - p);
5382             if (q == NULL) break;
5383             q++;
5384             (is->func)(is, is->closure, p, q - p, 0);
5385             p = q;
5386         }
5387         q = is->buf;
5388         while (p < is->unused) {
5389             *q++ = *p++;
5390         }
5391         is->unused = q;
5392     } else {
5393         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5394         if (count == -1)
5395           error = errno;
5396         else
5397           error = 0;
5398         (is->func)(is, is->closure, is->buf, count, error);
5399     }
5400 }
5401
5402 InputSourceRef
5403 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5404 {
5405     InputSource *is;
5406     ChildProc *cp = (ChildProc *) pr;
5407
5408     is = (InputSource *) calloc(1, sizeof(InputSource));
5409     is->lineByLine = lineByLine;
5410     is->func = func;
5411     if (pr == NoProc) {
5412         is->kind = CPReal;
5413         is->fd = fileno(stdin);
5414     } else {
5415         is->kind = cp->kind;
5416         is->fd = cp->fdFrom;
5417     }
5418     if (lineByLine) {
5419         is->unused = is->buf;
5420     }
5421
5422     is->xid = XtAppAddInput(appContext, is->fd,
5423                             (XtPointer) (XtInputReadMask),
5424                             (XtInputCallbackProc) DoInputCallback,
5425                             (XtPointer) is);
5426     is->closure = closure;
5427     return (InputSourceRef) is;
5428 }
5429
5430 void
5431 RemoveInputSource (InputSourceRef isr)
5432 {
5433     InputSource *is = (InputSource *) isr;
5434
5435     if (is->xid == 0) return;
5436     XtRemoveInput(is->xid);
5437     is->xid = 0;
5438 }
5439
5440 int
5441 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5442 {
5443     static int line = 0;
5444     ChildProc *cp = (ChildProc *) pr;
5445     int outCount;
5446
5447     if (pr == NoProc)
5448     {
5449         if (appData.noJoin || !appData.useInternalWrap)
5450             outCount = fwrite(message, 1, count, stdout);
5451         else
5452         {
5453             int width = get_term_width();
5454             int len = wrap(NULL, message, count, width, &line);
5455             char *msg = malloc(len);
5456             int dbgchk;
5457
5458             if (!msg)
5459                 outCount = fwrite(message, 1, count, stdout);
5460             else
5461             {
5462                 dbgchk = wrap(msg, message, count, width, &line);
5463                 if (dbgchk != len && appData.debugMode)
5464                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5465                 outCount = fwrite(msg, 1, dbgchk, stdout);
5466                 free(msg);
5467             }
5468         }
5469     }
5470     else
5471       outCount = write(cp->fdTo, message, count);
5472
5473     if (outCount == -1)
5474       *outError = errno;
5475     else
5476       *outError = 0;
5477
5478     return outCount;
5479 }
5480
5481 /* Output message to process, with "ms" milliseconds of delay
5482    between each character. This is needed when sending the logon
5483    script to ICC, which for some reason doesn't like the
5484    instantaneous send. */
5485 int
5486 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5487 {
5488     ChildProc *cp = (ChildProc *) pr;
5489     int outCount = 0;
5490     int r;
5491
5492     while (count--) {
5493         r = write(cp->fdTo, message++, 1);
5494         if (r == -1) {
5495             *outError = errno;
5496             return outCount;
5497         }
5498         ++outCount;
5499         if (msdelay >= 0)
5500           TimeDelay(msdelay);
5501     }
5502
5503     return outCount;
5504 }
5505
5506 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
5507
5508 /*      Masks for XPM pieces. Black and white pieces can have
5509         different shapes, but in the interest of retaining my
5510         sanity pieces must have the same outline on both light
5511         and dark squares, and all pieces must use the same
5512         background square colors/images.                */
5513
5514 static int xpmDone = 0;
5515 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5516 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5517
5518 static void
5519 CreateAnimMasks (int pieceDepth)
5520 {
5521   ChessSquare   piece;
5522   Pixmap        buf;
5523   GC            bufGC, maskGC;
5524   int           kind, n;
5525   unsigned long plane;
5526   XGCValues     values;
5527
5528   /* Need a bitmap just to get a GC with right depth */
5529   buf = XCreatePixmap(xDisplay, xBoardWindow,
5530                         8, 8, 1);
5531   values.foreground = 1;
5532   values.background = 0;
5533   /* Don't use XtGetGC, not read only */
5534   maskGC = XCreateGC(xDisplay, buf,
5535                     GCForeground | GCBackground, &values);
5536   XFreePixmap(xDisplay, buf);
5537
5538   buf = XCreatePixmap(xDisplay, xBoardWindow,
5539                       squareSize, squareSize, pieceDepth);
5540   values.foreground = XBlackPixel(xDisplay, xScreen);
5541   values.background = XWhitePixel(xDisplay, xScreen);
5542   bufGC = XCreateGC(xDisplay, buf,
5543                     GCForeground | GCBackground, &values);
5544
5545   for (piece = WhitePawn; piece <= BlackKing; piece++) {
5546     /* Begin with empty mask */
5547     if(!xpmDone) // [HGM] pieces: keep using existing
5548     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5549                                  squareSize, squareSize, 1);
5550     XSetFunction(xDisplay, maskGC, GXclear);
5551     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5552                    0, 0, squareSize, squareSize);
5553
5554     /* Take a copy of the piece */
5555     if (White(piece))
5556       kind = 0;
5557     else
5558       kind = 2;
5559     XSetFunction(xDisplay, bufGC, GXcopy);
5560     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5561               buf, bufGC,
5562               0, 0, squareSize, squareSize, 0, 0);
5563
5564     /* XOR the background (light) over the piece */
5565     XSetFunction(xDisplay, bufGC, GXxor);
5566     if (useImageSqs)
5567       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5568                 0, 0, squareSize, squareSize, 0, 0);
5569     else {
5570       XSetForeground(xDisplay, bufGC, lightSquareColor);
5571       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5572     }
5573
5574     /* We now have an inverted piece image with the background
5575        erased. Construct mask by just selecting all the non-zero
5576        pixels - no need to reconstruct the original image.      */
5577     XSetFunction(xDisplay, maskGC, GXor);
5578     plane = 1;
5579     /* Might be quicker to download an XImage and create bitmap
5580        data from it rather than this N copies per piece, but it
5581        only takes a fraction of a second and there is a much
5582        longer delay for loading the pieces.             */
5583     for (n = 0; n < pieceDepth; n ++) {
5584       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5585                  0, 0, squareSize, squareSize,
5586                  0, 0, plane);
5587       plane = plane << 1;
5588     }
5589   }
5590   /* Clean up */
5591   XFreePixmap(xDisplay, buf);
5592   XFreeGC(xDisplay, bufGC);
5593   XFreeGC(xDisplay, maskGC);
5594 }
5595
5596 static void
5597 InitAnimState (AnimNr anr, XWindowAttributes *info)
5598 {
5599   XtGCMask  mask;
5600   XGCValues values;
5601
5602   /* Each buffer is square size, same depth as window */
5603   animBufs[anr+4] = xBoardWindow;
5604   animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5605                         squareSize, squareSize, info->depth);
5606   animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5607                         squareSize, squareSize, info->depth);
5608
5609   /* Create a plain GC for blitting */
5610   mask = GCForeground | GCBackground | GCFunction |
5611          GCPlaneMask | GCGraphicsExposures;
5612   values.foreground = XBlackPixel(xDisplay, xScreen);
5613   values.background = XWhitePixel(xDisplay, xScreen);
5614   values.function   = GXcopy;
5615   values.plane_mask = AllPlanes;
5616   values.graphics_exposures = False;
5617   animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5618
5619   /* Piece will be copied from an existing context at
5620      the start of each new animation/drag. */
5621   animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5622
5623   /* Outline will be a read-only copy of an existing */
5624   animGCs[anr+4] = None;
5625 }
5626
5627 void
5628 CreateAnimVars ()
5629 {
5630   XWindowAttributes info;
5631
5632   if (xpmDone && gameInfo.variant == oldVariant) return;
5633   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5634   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5635
5636   InitAnimState(Game, &info);
5637   InitAnimState(Player, &info);
5638
5639   /* For XPM pieces, we need bitmaps to use as masks. */
5640   if (useImages)
5641     CreateAnimMasks(info.depth), xpmDone = 1;
5642 }
5643
5644 #ifndef HAVE_USLEEP
5645
5646 static Boolean frameWaiting;
5647
5648 static RETSIGTYPE
5649 FrameAlarm (int sig)
5650 {
5651   frameWaiting = False;
5652   /* In case System-V style signals.  Needed?? */
5653   signal(SIGALRM, FrameAlarm);
5654 }
5655
5656 void
5657 FrameDelay (int time)
5658 {
5659   struct itimerval delay;
5660
5661   XSync(xDisplay, False);
5662
5663   if (time > 0) {
5664     frameWaiting = True;
5665     signal(SIGALRM, FrameAlarm);
5666     delay.it_interval.tv_sec =
5667       delay.it_value.tv_sec = time / 1000;
5668     delay.it_interval.tv_usec =
5669       delay.it_value.tv_usec = (time % 1000) * 1000;
5670     setitimer(ITIMER_REAL, &delay, NULL);
5671     while (frameWaiting) pause();
5672     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5673     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5674     setitimer(ITIMER_REAL, &delay, NULL);
5675   }
5676 }
5677
5678 #else
5679
5680 void
5681 FrameDelay (int time)
5682 {
5683   XSync(xDisplay, False);
5684   if (time > 0)
5685     usleep(time * 1000);
5686 }
5687
5688 #endif
5689
5690 static void
5691 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5692 {
5693   GC source;
5694
5695   /* Bitmap for piece being moved. */
5696   if (appData.monoMode) {
5697       *mask = *pieceToSolid(piece);
5698   } else if (useImages) {
5699 #if HAVE_LIBXPM
5700       *mask = xpmMask[piece];
5701 #else
5702       *mask = ximMaskPm[piece];
5703 #endif
5704   } else {
5705       *mask = *pieceToSolid(piece);
5706   }
5707
5708   /* GC for piece being moved. Square color doesn't matter, but
5709      since it gets modified we make a copy of the original. */
5710   if (White(piece)) {
5711     if (appData.monoMode)
5712       source = bwPieceGC;
5713     else
5714       source = wlPieceGC;
5715   } else {
5716     if (appData.monoMode)
5717       source = wbPieceGC;
5718     else
5719       source = blPieceGC;
5720   }
5721   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5722
5723   /* Outline only used in mono mode and is not modified */
5724   if (White(piece))
5725     *outline = bwPieceGC;
5726   else
5727     *outline = wbPieceGC;
5728 }
5729
5730 static void
5731 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
5732 {
5733   int   kind;
5734
5735   if (!useImages) {
5736     /* Draw solid rectangle which will be clipped to shape of piece */
5737     XFillRectangle(xDisplay, dest, clip,
5738                    0, 0, squareSize, squareSize);
5739     if (appData.monoMode)
5740       /* Also draw outline in contrasting color for black
5741          on black / white on white cases                */
5742       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5743                  0, 0, squareSize, squareSize, 0, 0, 1);
5744   } else {
5745     /* Copy the piece */
5746     if (White(piece))
5747       kind = 0;
5748     else
5749       kind = 2;
5750     if(appData.upsideDown && flipView) kind ^= 2;
5751     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5752               dest, clip,
5753               0, 0, squareSize, squareSize,
5754               0, 0);
5755   }
5756 }
5757
5758 void
5759 InsertPiece (AnimNr anr, ChessSquare piece)
5760 {
5761   OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5762 }
5763
5764 void
5765 DrawBlank (AnimNr anr, int x, int y, int startColor)
5766 {
5767     BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5768 }
5769
5770 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5771                  int srcX, int srcY, int width, int height, int destX, int destY)
5772 {
5773     XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5774                 srcX, srcY, width, height, destX, destY);
5775 }
5776
5777 void
5778 SetDragPiece (AnimNr anr, ChessSquare piece)
5779 {
5780   Pixmap mask;
5781   /* The piece will be drawn using its own bitmap as a matte    */
5782   SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5783   XSetClipMask(xDisplay, animGCs[anr+2], mask);
5784 }
5785
5786 #include <sys/ioctl.h>
5787 int
5788 get_term_width ()
5789 {
5790     int fd, default_width;
5791
5792     fd = STDIN_FILENO;
5793     default_width = 79; // this is FICS default anyway...
5794
5795 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5796     struct ttysize win;
5797     if (!ioctl(fd, TIOCGSIZE, &win))
5798         default_width = win.ts_cols;
5799 #elif defined(TIOCGWINSZ)
5800     struct winsize win;
5801     if (!ioctl(fd, TIOCGWINSZ, &win))
5802         default_width = win.ws_col;
5803 #endif
5804     return default_width;
5805 }
5806
5807 void
5808 update_ics_width ()
5809 {
5810   static int old_width = 0;
5811   int new_width = get_term_width();
5812
5813   if (old_width != new_width)
5814     ics_printf("set width %d\n", new_width);
5815   old_width = new_width;
5816 }
5817
5818 void
5819 NotifyFrontendLogin ()
5820 {
5821     update_ics_width();
5822 }
5823
5824 /* [AS] Arrow highlighting support */
5825
5826 void
5827 DrawPolygon (Pnt arrow[], int nr)
5828 {
5829     XPoint pts[10];
5830     int i;
5831     for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
5832     XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
5833     if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
5834 }
5835
5836 void
5837 UpdateLogos (int displ)
5838 {
5839     return; // no logos in XBoard yet
5840 }
5841