Cleanup of xboard.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 RETSIGTYPE CmailSigHandler P((int sig));
229 RETSIGTYPE IntSigHandler P((int sig));
230 RETSIGTYPE TermSizeSigHandler P((int sig));
231 static void CreateGCs P((int redo));
232 static void CreateAnyPieces P((void));
233 void CreateXIMPieces P((void));
234 void CreateXPMPieces P((void));
235 void CreateXPMBoard P((char *s, int n));
236 void CreatePieces P((void));
237 void CreatePieceMenus P((void));
238 Widget CreateMenuBar P((Menu *mb, int boardWidth));
239 Widget CreateButtonBar P ((MenuItem *mi));
240 #if ENABLE_NLS
241 char *InsertPxlSize P((char *pattern, int targetPxlSize));
242 XFontSet CreateFontSet P((char *base_fnt_lst));
243 #else
244 char *FindFont P((char *pattern, int targetPxlSize));
245 #endif
246 void PieceMenuPopup P((Widget w, XEvent *event,
247                        String *params, Cardinal *num_params));
248 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
249 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
250 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
251                    u_int wreq, u_int hreq));
252 void CreateGrid P((void));
253 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
254 void DelayedDrag P((void));
255 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
256 void HandleUserMove P((Widget w, XEvent *event,
257                      String *prms, Cardinal *nprms));
258 void AnimateUserMove P((Widget w, XEvent * event,
259                      String * params, Cardinal * nParams));
260 void HandlePV P((Widget w, XEvent * event,
261                      String * params, Cardinal * nParams));
262 void SelectPV P((Widget w, XEvent * event,
263                      String * params, Cardinal * nParams));
264 void StopPV P((Widget w, XEvent * event,
265                      String * params, Cardinal * nParams));
266 void WhiteClock P((Widget w, XEvent *event,
267                    String *prms, Cardinal *nprms));
268 void BlackClock P((Widget w, XEvent *event,
269                    String *prms, Cardinal *nprms));
270 void DrawPositionProc P((Widget w, XEvent *event,
271                      String *prms, Cardinal *nprms));
272 void CommentClick P((Widget w, XEvent * event,
273                    String * params, Cardinal * nParams));
274 void ICSInputBoxPopUp P((void));
275 void ICSInputBoxPopDown P((void));
276 void FileNamePopUp P((char *label, char *def, char *filter,
277                       FileProc proc, char *openMode));
278 void AskQuestionReplyAction P((Widget w, XEvent *event,
279                           String *prms, Cardinal *nprms));
280 void AskQuestionPopDown P((void));
281 void PromotionPopDown P((void));
282 void PromotionCallback P((Widget w, XtPointer client_data,
283                           XtPointer call_data));
284 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
285 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
286 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
287 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
288 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
289 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
290 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
291 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 Boolean TempBackwardActive = False;
293 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void DisplayMove P((int moveNumber));
295 void ICSInitScript P((void));
296 static char *ExpandPathName P((char *path));
297 void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
298 void update_ics_width P(());
299 int get_term_width P(());
300 int CopyMemoProc P(());
301
302 /*
303 * XBoard depends on Xt R4 or higher
304 */
305 int xtVersion = XtSpecificationRelease;
306
307 int xScreen;
308 Display *xDisplay;
309 Window xBoardWindow;
310 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
311   highlightSquareColor, premoveHighlightColor;
312 Pixel lowTimeWarningColor;
313 GC lightSquareGC, darkSquareGC, lineGC, wdPieceGC, wlPieceGC,
314   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
315   prelineGC, countGC;
316 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
317 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
318   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
319   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
320   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
321   ICSInputShell, fileNameShell, askQuestionShell;
322 Widget historyShell, evalGraphShell, gameListShell;
323 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
324 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
325 #if ENABLE_NLS
326 XFontSet fontSet, clockFontSet;
327 #else
328 Font clockFontID;
329 XFontStruct *clockFontStruct;
330 #endif
331 Font coordFontID, countFontID;
332 XFontStruct *coordFontStruct, *countFontStruct;
333 XtAppContext appContext;
334 char *layoutName;
335 char *oldICSInteractionTitle;
336
337 FileProc fileProc;
338 char *fileOpenMode;
339 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
340
341 Position commentX = -1, commentY = -1;
342 Dimension commentW, commentH;
343 typedef unsigned int BoardSize;
344 BoardSize boardSize;
345 Boolean chessProgram;
346
347 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
348 int smallLayout = 0, tinyLayout = 0,
349   marginW, marginH, // [HGM] for run-time resizing
350   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
351   ICSInputBoxUp = False, askQuestionUp = False,
352   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
353   errorUp = False, errorExitStatus = -1, defaultLineGap;
354 Dimension textHeight;
355 Pixel timerForegroundPixel, timerBackgroundPixel;
356 Pixel buttonForegroundPixel, buttonBackgroundPixel;
357 char *chessDir, *programName, *programVersion;
358 Boolean alwaysOnTop = False;
359 char *icsTextMenuString;
360 char *icsNames;
361 char *firstChessProgramNames;
362 char *secondChessProgramNames;
363
364 WindowPlacement wpMain;
365 WindowPlacement wpConsole;
366 WindowPlacement wpComment;
367 WindowPlacement wpMoveHistory;
368 WindowPlacement wpEvalGraph;
369 WindowPlacement wpEngineOutput;
370 WindowPlacement wpGameList;
371 WindowPlacement wpTags;
372
373
374 #define SOLID 0
375 #define OUTLINE 1
376 Pixmap pieceBitmap[2][(int)BlackPawn];
377 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
378 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
379 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
380 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
381 Pixmap xpmBoardBitmap[2];
382 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
383 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
384 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
385 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
386 XImage *ximLightSquare, *ximDarkSquare;
387 XImage *xim_Cross;
388
389 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
390 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
391
392 #define White(piece) ((int)(piece) < (int)BlackPawn)
393
394 /* Bitmaps for use as masks when drawing XPM pieces.
395    Need one for each black and white piece.             */
396 static Pixmap xpmMask[BlackKing + 1];
397
398 /* This magic number is the number of intermediate frames used
399    in each half of the animation. For short moves it's reduced
400    by 1. The total number of frames will be factor * 2 + 1.  */
401 #define kFactor    4
402
403 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
404
405 #define PAUSE_BUTTON "P"
406 MenuItem buttonBar[] = {
407     {"<<", "<<", ToStartEvent},
408     {"<", "<", BackwardEvent},
409     {N_(PAUSE_BUTTON), PAUSE_BUTTON, PauseEvent},
410     {">", ">", ForwardEvent},
411     {">>", ">>", ToEndEvent},
412     {NULL, NULL, NULL}
413 };
414
415 #define PIECE_MENU_SIZE 18
416 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
417     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
418       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
419       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
420       N_("Empty square"), N_("Clear board") },
421     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
422       N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
423       N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
424       N_("Empty square"), N_("Clear board") }
425 };
426 /* must be in same order as pieceMenuStrings! */
427 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
428     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
429         WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
430         WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
431         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
432     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
433         BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
434         BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
435         PromotePiece, DemotePiece, EmptySquare, ClearBoard },
436 };
437
438 #define DROP_MENU_SIZE 6
439 String dropMenuStrings[DROP_MENU_SIZE] = {
440     "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
441   };
442 /* must be in same order as dropMenuStrings! */
443 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
444     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
445     WhiteRook, WhiteQueen
446 };
447
448 typedef struct {
449     char piece;
450     char* widget;
451 } DropMenuEnables;
452
453 DropMenuEnables dmEnables[] = {
454     { 'P', "Pawn" },
455     { 'N', "Knight" },
456     { 'B', "Bishop" },
457     { 'R', "Rook" },
458     { 'Q', "Queen" }
459 };
460
461 Arg shellArgs[] = {
462     { XtNwidth, 0 },
463     { XtNheight, 0 },
464     { XtNminWidth, 0 },
465     { XtNminHeight, 0 },
466     { XtNmaxWidth, 0 },
467     { XtNmaxHeight, 0 }
468 };
469
470 Arg layoutArgs[] = {
471     { XtNborderWidth, 0 },
472     { XtNdefaultDistance, 0 },
473 };
474
475 Arg formArgs[] = {
476     { XtNborderWidth, 0 },
477     { XtNresizable, (XtArgVal) True },
478 };
479
480 Arg boardArgs[] = {
481     { XtNborderWidth, 0 },
482     { XtNwidth, 0 },
483     { XtNheight, 0 }
484 };
485
486 Arg titleArgs[] = {
487     { XtNjustify, (XtArgVal) XtJustifyRight },
488     { XtNlabel, (XtArgVal) "..." },
489     { XtNresizable, (XtArgVal) True },
490     { XtNresize, (XtArgVal) False }
491 };
492
493 Arg messageArgs[] = {
494     { XtNjustify, (XtArgVal) XtJustifyLeft },
495     { XtNlabel, (XtArgVal) "..." },
496     { XtNresizable, (XtArgVal) True },
497     { XtNresize, (XtArgVal) False }
498 };
499
500 Arg timerArgs[] = {
501     { XtNborderWidth, 0 },
502     { XtNjustify, (XtArgVal) XtJustifyLeft }
503 };
504
505 XtResource clientResources[] = {
506     { "flashCount", "flashCount", XtRInt, sizeof(int),
507         XtOffset(AppDataPtr, flashCount), XtRImmediate,
508         (XtPointer) FLASH_COUNT  },
509 };
510
511 XrmOptionDescRec shellOptions[] = {
512     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
513     { "-flash", "flashCount", XrmoptionNoArg, "3" },
514     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
515 };
516
517 XtActionsRec boardActions[] = {
518     { "DrawPosition", DrawPositionProc },
519     { "HandleUserMove", HandleUserMove },
520     { "AnimateUserMove", AnimateUserMove },
521     { "HandlePV", HandlePV },
522     { "SelectPV", SelectPV },
523     { "StopPV", StopPV },
524     { "AskQuestionReplyAction", AskQuestionReplyAction },
525     { "PieceMenuPopup", PieceMenuPopup },
526     { "WhiteClock", WhiteClock },
527     { "BlackClock", BlackClock },
528     { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
529     { "QuitProc", QuitWrapper },
530     { "ManProc", ManInner },
531     { "TempBackwardProc", TempBackwardProc },
532     { "TempForwardProc", TempForwardProc },
533     { "CommentClick", (XtActionProc) CommentClick },
534     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
535     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
536     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
537     { "GameListPopDown", (XtActionProc) GameListPopDown },
538     { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
539     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
540     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
541     { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
542     { "GenericPopDown", (XtActionProc) GenericPopDown },
543     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
544     { "SelectMove", (XtActionProc) SelectMove },
545     { "LoadSelectedProc", LoadSelectedProc },
546     { "SetFilterProc", SetFilterProc },
547     { "TypeInProc", TypeInProc },
548     { "EnterKeyProc", EnterKeyProc },
549     { "UpKeyProc", UpKeyProc },
550     { "DownKeyProc", DownKeyProc },
551 };
552
553 char globalTranslations[] =
554   ":<Key>F9: MenuItem(ResignProc) \n \
555    :Ctrl<Key>n: MenuItem(NewGame) \n \
556    :Meta<Key>V: MenuItem(NewVariant) \n \
557    :Ctrl<Key>o: MenuItem(LoadGame) \n \
558    :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
559    :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
560    :Ctrl<Key>Down: LoadSelectedProc(3) \n \
561    :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
562    :Ctrl<Key>s: MenuItem(SaveGame) \n \
563    :Ctrl<Key>c: MenuItem(CopyGame) \n \
564    :Ctrl<Key>v: MenuItem(PasteGame) \n \
565    :Ctrl<Key>O: MenuItem(LoadPosition) \n \
566    :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
567    :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
568    :Ctrl<Key>S: MenuItem(SavePosition) \n \
569    :Ctrl<Key>C: MenuItem(CopyPosition) \n \
570    :Ctrl<Key>V: MenuItem(PastePosition) \n \
571    :Ctrl<Key>q: MenuItem(Exit) \n \
572    :Ctrl<Key>w: MenuItem(MachineWhite) \n \
573    :Ctrl<Key>b: MenuItem(MachineBlack) \n \
574    :Ctrl<Key>t: MenuItem(TwoMachines) \n \
575    :Ctrl<Key>a: MenuItem(AnalysisMode) \n \
576    :Ctrl<Key>g: MenuItem(AnalyzeFile) \n \
577    :Ctrl<Key>e: MenuItem(EditGame) \n \
578    :Ctrl<Key>E: MenuItem(EditPosition) \n \
579    :Meta<Key>O: MenuItem(ShowEngineOutput) \n \
580    :Meta<Key>E: MenuItem(ShowEvaluationGraph) \n \
581    :Meta<Key>G: MenuItem(ShowGameList) \n \
582    :Meta<Key>H: MenuItem(ShowMoveHistory) \n \
583    :<Key>Pause: MenuItem(Pause) \n \
584    :<Key>F3: MenuItem(Accept) \n \
585    :<Key>F4: MenuItem(Decline) \n \
586    :<Key>F12: MenuItem(Rematch) \n \
587    :<Key>F5: MenuItem(CallFlag) \n \
588    :<Key>F6: MenuItem(Draw) \n \
589    :<Key>F7: MenuItem(Adjourn) \n \
590    :<Key>F8: MenuItem(Abort) \n \
591    :<Key>F10: MenuItem(StopObserving) \n \
592    :<Key>F11: MenuItem(StopExamining) \n \
593    :Ctrl<Key>d: MenuItem(DebugProc) \n \
594    :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
595    :Meta<Key>End: MenuItem(ToEnd) \n \
596    :Meta<Key>Right: MenuItem(Forward) \n \
597    :Meta<Key>Home: MenuItem(ToStart) \n \
598    :Meta<Key>Left: MenuItem(Backward) \n \
599    :<Key>Left: MenuItem(Backward) \n \
600    :<Key>Right: MenuItem(Forward) \n \
601    :<Key>Home: MenuItem(Revert) \n \
602    :<Key>End: MenuItem(TruncateGame) \n \
603    :Ctrl<Key>m: MenuItem(MoveNow) \n \
604    :Ctrl<Key>x: MenuItem(RetractMove) \n \
605    :Meta<Key>J: MenuItem(Adjudications) \n \
606    :Meta<Key>U: MenuItem(CommonEngine) \n \
607    :Meta<Key>T: MenuItem(TimeControl) \n \
608    :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
609 #ifndef OPTIONSDIALOG
610     "\
611    :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
612    :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
613    :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
614    :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
615    :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
616 #endif
617    "\
618    :<Key>F1: MenuItem(Manual) \n \
619    :<Key>F2: MenuItem(FlipView) \n \
620    :<KeyDown>Return: TempBackwardProc() \n \
621    :<KeyUp>Return: TempForwardProc() \n";
622
623 char boardTranslations[] =
624    "<Btn1Down>: HandleUserMove(0) \n \
625    Shift<Btn1Up>: HandleUserMove(1) \n \
626    <Btn1Up>: HandleUserMove(0) \n \
627    <Btn1Motion>: AnimateUserMove() \n \
628    <Btn3Motion>: HandlePV() \n \
629    <Btn2Motion>: HandlePV() \n \
630    <Btn3Up>: PieceMenuPopup(menuB) \n \
631    <Btn2Up>: PieceMenuPopup(menuB) \n \
632    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
633                  PieceMenuPopup(menuB) \n \
634    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
635                  PieceMenuPopup(menuW) \n \
636    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
637                  PieceMenuPopup(menuW) \n \
638    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
639                  PieceMenuPopup(menuB) \n";
640
641 char whiteTranslations[] =
642    "Shift<BtnDown>: WhiteClock(1)\n \
643    <BtnDown>: WhiteClock(0)\n";
644 char blackTranslations[] =
645    "Shift<BtnDown>: BlackClock(1)\n \
646    <BtnDown>: BlackClock(0)\n";
647
648 char ICSInputTranslations[] =
649     "<Key>Up: UpKeyProc() \n "
650     "<Key>Down: DownKeyProc() \n "
651     "<Key>Return: EnterKeyProc() \n";
652
653 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
654 //             as the widget is destroyed before the up-click can call extend-end
655 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
656
657 String xboardResources[] = {
658     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
659     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
660     NULL
661   };
662
663
664 /* Max possible square size */
665 #define MAXSQSIZE 256
666
667 static int xpm_avail[MAXSQSIZE];
668
669 #ifdef HAVE_DIR_STRUCT
670
671 /* Extract piece size from filename */
672 static int
673 xpm_getsize (char *name, int len, char *ext)
674 {
675     char *p, *d;
676     char buf[10];
677
678     if (len < 4)
679       return 0;
680
681     if ((p=strchr(name, '.')) == NULL ||
682         StrCaseCmp(p+1, ext) != 0)
683       return 0;
684
685     p = name + 3;
686     d = buf;
687
688     while (*p && isdigit(*p))
689       *(d++) = *(p++);
690
691     *d = 0;
692     return atoi(buf);
693 }
694
695 /* Setup xpm_avail */
696 static int
697 xpm_getavail (char *dirname, char *ext)
698 {
699     DIR *dir;
700     struct dirent *ent;
701     int  i;
702
703     for (i=0; i<MAXSQSIZE; ++i)
704       xpm_avail[i] = 0;
705
706     if (appData.debugMode)
707       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
708
709     dir = opendir(dirname);
710     if (!dir)
711       {
712           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
713                   programName, dirname);
714           exit(1);
715       }
716
717     while ((ent=readdir(dir)) != NULL) {
718         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
719         if (i > 0 && i < MAXSQSIZE)
720           xpm_avail[i] = 1;
721     }
722
723     closedir(dir);
724
725     return 0;
726 }
727
728 void
729 xpm_print_avail (FILE *fp, char *ext)
730 {
731     int i;
732
733     fprintf(fp, _("Available `%s' sizes:\n"), ext);
734     for (i=1; i<MAXSQSIZE; ++i) {
735         if (xpm_avail[i])
736           printf("%d\n", i);
737     }
738 }
739
740 /* Return XPM piecesize closest to size */
741 int
742 xpm_closest_to (char *dirname, int size, char *ext)
743 {
744     int i;
745     int sm_diff = MAXSQSIZE;
746     int sm_index = 0;
747     int diff;
748
749     xpm_getavail(dirname, ext);
750
751     if (appData.debugMode)
752       xpm_print_avail(stderr, ext);
753
754     for (i=1; i<MAXSQSIZE; ++i) {
755         if (xpm_avail[i]) {
756             diff = size - i;
757             diff = (diff<0) ? -diff : diff;
758             if (diff < sm_diff) {
759                 sm_diff = diff;
760                 sm_index = i;
761             }
762         }
763     }
764
765     if (!sm_index) {
766         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
767         exit(1);
768     }
769
770     return sm_index;
771 }
772 #else   /* !HAVE_DIR_STRUCT */
773 /* If we are on a system without a DIR struct, we can't
774    read the directory, so we can't collect a list of
775    filenames, etc., so we can't do any size-fitting. */
776 int
777 xpm_closest_to (char *dirname, int size, char *ext)
778 {
779     fprintf(stderr, _("\
780 Warning: No DIR structure found on this system --\n\
781          Unable to autosize for XPM/XIM pieces.\n\
782    Please report this error to %s.\n\
783    Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
784     return size;
785 }
786 #endif /* HAVE_DIR_STRUCT */
787
788 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
789                              "magenta", "cyan", "white" };
790 typedef struct {
791     int attr, bg, fg;
792 } TextColors;
793 TextColors textColors[(int)NColorClasses];
794
795 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
796 static int
797 parse_color (char *str, int which)
798 {
799     char *p, buf[100], *d;
800     int i;
801
802     if (strlen(str) > 99)       /* watch bounds on buf */
803       return -1;
804
805     p = str;
806     d = buf;
807     for (i=0; i<which; ++i) {
808         p = strchr(p, ',');
809         if (!p)
810           return -1;
811         ++p;
812     }
813
814     /* Could be looking at something like:
815        black, , 1
816        .. in which case we want to stop on a comma also */
817     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
818       ++p;
819
820     if (*p == ',') {
821         return -1;              /* Use default for empty field */
822     }
823
824     if (which == 2 || isdigit(*p))
825       return atoi(p);
826
827     while (*p && isalpha(*p))
828       *(d++) = *(p++);
829
830     *d = 0;
831
832     for (i=0; i<8; ++i) {
833         if (!StrCaseCmp(buf, cnames[i]))
834           return which? (i+40) : (i+30);
835     }
836     if (!StrCaseCmp(buf, "default")) return -1;
837
838     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
839     return -2;
840 }
841
842 static int
843 parse_cpair (ColorClass cc, char *str)
844 {
845     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
846         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
847                 programName, str);
848         return -1;
849     }
850
851     /* bg and attr are optional */
852     textColors[(int)cc].bg = parse_color(str, 1);
853     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
854         textColors[(int)cc].attr = 0;
855     }
856     return 0;
857 }
858
859
860 /* Arrange to catch delete-window events */
861 Atom wm_delete_window;
862 void
863 CatchDeleteWindow (Widget w, String procname)
864 {
865   char buf[MSG_SIZ];
866   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
867   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
868   XtAugmentTranslations(w, XtParseTranslationTable(buf));
869 }
870
871 void
872 BoardToTop ()
873 {
874   Arg args[16];
875   XtSetArg(args[0], XtNiconic, False);
876   XtSetValues(shellWidget, args, 1);
877
878   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
879 }
880
881 //---------------------------------------------------------------------------------------------------------
882 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
883 #define XBOARD True
884 #define JAWS_ARGS
885 #define CW_USEDEFAULT (1<<31)
886 #define ICS_TEXT_MENU_SIZE 90
887 #define DEBUG_FILE "xboard.debug"
888 #define SetCurrentDirectory chdir
889 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
890 #define OPTCHAR "-"
891 #define SEPCHAR " "
892
893 // these two must some day move to frontend.h, when they are implemented
894 Boolean GameListIsUp();
895
896 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
897 #include "args.h"
898
899 // front-end part of option handling
900
901 // [HGM] This platform-dependent table provides the location for storing the color info
902 extern char *crWhite, * crBlack;
903
904 void *
905 colorVariable[] = {
906   &appData.whitePieceColor,
907   &appData.blackPieceColor,
908   &appData.lightSquareColor,
909   &appData.darkSquareColor,
910   &appData.highlightSquareColor,
911   &appData.premoveHighlightColor,
912   &appData.lowTimeWarningColor,
913   NULL,
914   NULL,
915   NULL,
916   NULL,
917   NULL,
918   &crWhite,
919   &crBlack,
920   NULL
921 };
922
923 // [HGM] font: keep a font for each square size, even non-stndard ones
924 #define NUM_SIZES 18
925 #define MAX_SIZE 130
926 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
927 char *fontTable[NUM_FONTS][MAX_SIZE];
928
929 void
930 ParseFont (char *name, int number)
931 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
932   int size;
933   if(sscanf(name, "size%d:", &size)) {
934     // [HGM] font: font is meant for specific boardSize (likely from settings file);
935     //       defer processing it until we know if it matches our board size
936     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
937         fontTable[number][size] = strdup(strchr(name, ':')+1);
938         fontValid[number][size] = True;
939     }
940     return;
941   }
942   switch(number) {
943     case 0: // CLOCK_FONT
944         appData.clockFont = strdup(name);
945       break;
946     case 1: // MESSAGE_FONT
947         appData.font = strdup(name);
948       break;
949     case 2: // COORD_FONT
950         appData.coordFont = strdup(name);
951       break;
952     default:
953       return;
954   }
955   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
956 }
957
958 void
959 SetFontDefaults ()
960 { // only 2 fonts currently
961   appData.clockFont = CLOCK_FONT_NAME;
962   appData.coordFont = COORD_FONT_NAME;
963   appData.font  =   DEFAULT_FONT_NAME;
964 }
965
966 void
967 CreateFonts ()
968 { // no-op, until we identify the code for this already in XBoard and move it here
969 }
970
971 void
972 ParseColor (int n, char *name)
973 { // in XBoard, just copy the color-name string
974   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
975 }
976
977 void
978 ParseTextAttribs (ColorClass cc, char *s)
979 {
980     (&appData.colorShout)[cc] = strdup(s);
981 }
982
983 void
984 ParseBoardSize (void *addr, char *name)
985 {
986     appData.boardSize = strdup(name);
987 }
988
989 void
990 LoadAllSounds ()
991 { // In XBoard the sound-playing program takes care of obtaining the actual sound
992 }
993
994 void
995 SetCommPortDefaults ()
996 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
997 }
998
999 // [HGM] args: these three cases taken out to stay in front-end
1000 void
1001 SaveFontArg (FILE *f, ArgDescriptor *ad)
1002 {
1003   char *name;
1004   int i, n = (int)(intptr_t)ad->argLoc;
1005   switch(n) {
1006     case 0: // CLOCK_FONT
1007         name = appData.clockFont;
1008       break;
1009     case 1: // MESSAGE_FONT
1010         name = appData.font;
1011       break;
1012     case 2: // COORD_FONT
1013         name = appData.coordFont;
1014       break;
1015     default:
1016       return;
1017   }
1018   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1019     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1020         fontTable[n][squareSize] = strdup(name);
1021         fontValid[n][squareSize] = True;
1022         break;
1023   }
1024   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1025     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
1026 }
1027
1028 void
1029 ExportSounds ()
1030 { // nothing to do, as the sounds are at all times represented by their text-string names already
1031 }
1032
1033 void
1034 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
1035 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1036         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
1037 }
1038
1039 void
1040 SaveColor (FILE *f, ArgDescriptor *ad)
1041 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1042         if(colorVariable[(int)(intptr_t)ad->argLoc])
1043         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
1044 }
1045
1046 void
1047 SaveBoardSize (FILE *f, char *name, void *addr)
1048 { // wrapper to shield back-end from BoardSize & sizeInfo
1049   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1050 }
1051
1052 void
1053 ParseCommPortSettings (char *s)
1054 { // no such option in XBoard (yet)
1055 }
1056
1057 extern Widget engineOutputShell;
1058 int frameX, frameY;
1059
1060 void
1061 GetActualPlacement (Widget wg, WindowPlacement *wp)
1062 {
1063   Arg args[16];
1064   Dimension w, h;
1065   Position x, y;
1066   XWindowAttributes winAt;
1067   Window win, dummy;
1068   int i, rx, ry;
1069
1070   if(!wg) return;
1071
1072     win = XtWindow(wg);
1073     XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
1074     XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
1075     wp->x = rx - winAt.x;
1076     wp->y = ry - winAt.y;
1077     wp->height = winAt.height;
1078     wp->width = winAt.width;
1079     frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
1080 }
1081
1082 void
1083 GetWindowCoords ()
1084 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1085   // In XBoard this will have to wait until awareness of window parameters is implemented
1086   GetActualPlacement(shellWidget, &wpMain);
1087   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput);
1088   if(MoveHistoryIsUp()) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
1089   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1090   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1091   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
1092   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
1093 }
1094
1095 void
1096 PrintCommPortSettings (FILE *f, char *name)
1097 { // This option does not exist in XBoard
1098 }
1099
1100 int
1101 MySearchPath (char *installDir, char *name, char *fullname)
1102 { // just append installDir and name. Perhaps ExpandPath should be used here?
1103   name = ExpandPathName(name);
1104   if(name && name[0] == '/')
1105     safeStrCpy(fullname, name, MSG_SIZ );
1106   else {
1107     sprintf(fullname, "%s%c%s", installDir, '/', name);
1108   }
1109   return 1;
1110 }
1111
1112 int
1113 MyGetFullPathName (char *name, char *fullname)
1114 { // should use ExpandPath?
1115   name = ExpandPathName(name);
1116   safeStrCpy(fullname, name, MSG_SIZ );
1117   return 1;
1118 }
1119
1120 void
1121 EnsureOnScreen (int *x, int *y, int minX, int minY)
1122 {
1123   return;
1124 }
1125
1126 int
1127 MainWindowUp ()
1128 { // [HGM] args: allows testing if main window is realized from back-end
1129   return xBoardWindow != 0;
1130 }
1131
1132 void
1133 PopUpStartupDialog ()
1134 {  // start menu not implemented in XBoard
1135 }
1136
1137 char *
1138 ConvertToLine (int argc, char **argv)
1139 {
1140   static char line[128*1024], buf[1024];
1141   int i;
1142
1143   line[0] = NULLCHAR;
1144   for(i=1; i<argc; i++)
1145     {
1146       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
1147           && argv[i][0] != '{' )
1148         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1149       else
1150         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1151       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1152     }
1153
1154   line[strlen(line)-1] = NULLCHAR;
1155   return line;
1156 }
1157
1158 //--------------------------------------------------------------------------------------------
1159
1160 #ifdef IDSIZES
1161   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1162 #else
1163 #define BoardSize int
1164 void
1165 InitDrawingSizes (BoardSize boardSize, int flags)
1166 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1167     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1168     Arg args[16];
1169     XtGeometryResult gres;
1170     int i;
1171     static Dimension oldWidth, oldHeight;
1172     static VariantClass oldVariant;
1173     static int oldDual = -1, oldMono = -1;
1174
1175     if(!formWidget) return;
1176
1177     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1178     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1179     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1180
1181   if(boardWidth != oldWidth || boardHeight != oldHeight || oldDual != twoBoards) { // do resizing stuff only if size actually changed
1182     /*
1183      * Enable shell resizing.
1184      */
1185     shellArgs[0].value = (XtArgVal) &w;
1186     shellArgs[1].value = (XtArgVal) &h;
1187     XtGetValues(shellWidget, shellArgs, 2);
1188
1189     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1190     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1191     XtSetValues(shellWidget, &shellArgs[2], 4);
1192
1193     XtSetArg(args[0], XtNdefaultDistance, &sep);
1194     XtGetValues(formWidget, args, 1);
1195
1196     oldWidth = boardWidth; oldHeight = boardHeight; oldDual = twoBoards;
1197     CreateGrid();
1198     hOffset = boardWidth + 10;
1199     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1200         secondSegments[i] = gridSegments[i];
1201         secondSegments[i].x1 += hOffset;
1202         secondSegments[i].x2 += hOffset;
1203     }
1204
1205     XtSetArg(args[0], XtNwidth, boardWidth);
1206     XtSetArg(args[1], XtNheight, boardHeight);
1207     XtSetValues(boardWidget, args, 2);
1208
1209     timerWidth = (boardWidth - sep) / 2;
1210     XtSetArg(args[0], XtNwidth, timerWidth);
1211     XtSetValues(whiteTimerWidget, args, 1);
1212     XtSetValues(blackTimerWidget, args, 1);
1213
1214     XawFormDoLayout(formWidget, False);
1215
1216     if (appData.titleInWindow) {
1217         i = 0;
1218         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1219         XtSetArg(args[i], XtNheight, &h);  i++;
1220         XtGetValues(titleWidget, args, i);
1221         if (smallLayout) {
1222             w = boardWidth - 2*bor;
1223         } else {
1224             XtSetArg(args[0], XtNwidth, &w);
1225             XtGetValues(menuBarWidget, args, 1);
1226             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1227         }
1228
1229         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1230         if (gres != XtGeometryYes && appData.debugMode) {
1231             fprintf(stderr,
1232                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1233                     programName, gres, w, h, wr, hr);
1234         }
1235     }
1236
1237     XawFormDoLayout(formWidget, True);
1238
1239     /*
1240      * Inhibit shell resizing.
1241      */
1242     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1243     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1244     shellArgs[4].value = shellArgs[2].value = w;
1245     shellArgs[5].value = shellArgs[3].value = h;
1246     XtSetValues(shellWidget, &shellArgs[0], 6);
1247
1248     XSync(xDisplay, False);
1249     DelayedDrag();
1250   }
1251
1252     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1253     // (only for xpm)
1254
1255   if(gameInfo.variant != oldVariant) { // and only if variant changed
1256
1257     if(useImages) {
1258       for(i=0; i<4; i++) {
1259         int p;
1260         for(p=0; p<=(int)WhiteKing; p++)
1261            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1262         if(gameInfo.variant == VariantShogi) {
1263            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1264            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1265            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1266            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1267            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1268         }
1269 #ifdef GOTHIC
1270         if(gameInfo.variant == VariantGothic) {
1271            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1272         }
1273 #endif
1274         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1275            xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
1276            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
1277         }
1278 #if !HAVE_LIBXPM
1279         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1280         for(p=0; p<=(int)WhiteKing; p++)
1281            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1282         if(gameInfo.variant == VariantShogi) {
1283            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1284            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1285            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1286            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1287            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1288         }
1289 #ifdef GOTHIC
1290         if(gameInfo.variant == VariantGothic) {
1291            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1292         }
1293 #endif
1294         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1295            ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
1296            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
1297         }
1298 #endif
1299       }
1300     } else {
1301       for(i=0; i<2; i++) {
1302         int p;
1303         for(p=0; p<=(int)WhiteKing; p++)
1304            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1305         if(gameInfo.variant == VariantShogi) {
1306            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1307            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1308            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1309            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1310            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1311         }
1312 #ifdef GOTHIC
1313         if(gameInfo.variant == VariantGothic) {
1314            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1315         }
1316 #endif
1317         if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
1318            pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
1319            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
1320         }
1321       }
1322     }
1323     oldMono = -10; // kludge to force recreation of animation masks
1324     oldVariant = gameInfo.variant;
1325   }
1326 #if HAVE_LIBXPM
1327   if(appData.monoMode != oldMono)
1328     CreateAnimVars();
1329 #endif
1330   oldMono = appData.monoMode;
1331 }
1332 #endif
1333
1334 void
1335 ParseIcsTextColors ()
1336 {   // [HGM] tken out of main(), so it can be called from ICS-Options dialog
1337     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1338         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1339         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
1340         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
1341         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1342         parse_cpair(ColorTell, appData.colorTell) < 0 ||
1343         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
1344         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
1345         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
1346         parse_cpair(ColorNormal, appData.colorNormal) < 0)
1347       {
1348           if (appData.colorize) {
1349               fprintf(stderr,
1350                       _("%s: can't parse color names; disabling colorization\n"),
1351                       programName);
1352           }
1353           appData.colorize = FALSE;
1354       }
1355 }
1356
1357 static int
1358 MakeOneColor (char *name, Pixel *color)
1359 {
1360     XrmValue vFrom, vTo;
1361     if (!appData.monoMode) {
1362         vFrom.addr = (caddr_t) name;
1363         vFrom.size = strlen(name);
1364         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1365         if (vTo.addr == NULL) {
1366           appData.monoMode = True;
1367           return True;
1368         } else {
1369           *color = *(Pixel *) vTo.addr;
1370         }
1371     }
1372     return False;
1373 }
1374
1375 static int
1376 MakeColors ()
1377 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
1378     int forceMono = False;
1379
1380     forceMono |= MakeOneColor(appData.lightSquareColor, &lightSquareColor);
1381     forceMono |= MakeOneColor(appData.darkSquareColor, &darkSquareColor);
1382     forceMono |= MakeOneColor(appData.whitePieceColor, &whitePieceColor);
1383     forceMono |= MakeOneColor(appData.blackPieceColor, &blackPieceColor);
1384     forceMono |= MakeOneColor(appData.highlightSquareColor, &highlightSquareColor);
1385     forceMono |= MakeOneColor(appData.premoveHighlightColor, &premoveHighlightColor);
1386
1387     return forceMono;
1388 }
1389
1390 static void
1391 CreateAnyPieces ()
1392 {   // [HGM] taken out of main
1393 #if HAVE_LIBXPM
1394     if (appData.monoMode && // [HGM] no sense to go on to certain doom
1395        (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
1396             appData.bitmapDirectory = strdup(DEF_BITMAP_DIR);
1397
1398     if (appData.bitmapDirectory[0] != NULLCHAR) {
1399       CreatePieces();
1400     } else {
1401       CreateXPMPieces();
1402       CreateXPMBoard(appData.liteBackTextureFile, 1);
1403       CreateXPMBoard(appData.darkBackTextureFile, 0);
1404     }
1405 #else
1406     CreateXIMPieces();
1407     /* Create regular pieces */
1408     if (!useImages) CreatePieces();
1409 #endif
1410 }
1411
1412 void
1413 InitDrawingParams ()
1414 {
1415     MakeColors(); CreateGCs(True);
1416     CreateAnyPieces();
1417 }
1418
1419 int
1420 main (int argc, char **argv)
1421 {
1422     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1423     XSetWindowAttributes window_attributes;
1424     Arg args[16];
1425     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1426     XrmValue vFrom, vTo;
1427     XtGeometryResult gres;
1428     char *p;
1429     XrmDatabase xdb;
1430     int forceMono = False;
1431
1432     srandom(time(0)); // [HGM] book: make random truly random
1433
1434     setbuf(stdout, NULL);
1435     setbuf(stderr, NULL);
1436     debugFP = stderr;
1437
1438     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1439         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1440         exit(0);
1441     }
1442
1443     programName = strrchr(argv[0], '/');
1444     if (programName == NULL)
1445       programName = argv[0];
1446     else
1447       programName++;
1448
1449 #ifdef ENABLE_NLS
1450     XtSetLanguageProc(NULL, NULL, NULL);
1451     bindtextdomain(PACKAGE, LOCALEDIR);
1452     textdomain(PACKAGE);
1453 #endif
1454
1455     shellWidget =
1456       XtAppInitialize(&appContext, "XBoard", shellOptions,
1457                       XtNumber(shellOptions),
1458                       &argc, argv, xboardResources, NULL, 0);
1459     appData.boardSize = "";
1460     InitAppData(ConvertToLine(argc, argv));
1461     p = getenv("HOME");
1462     if (p == NULL) p = "/tmp";
1463     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1464     gameCopyFilename = (char*) malloc(i);
1465     gamePasteFilename = (char*) malloc(i);
1466     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1467     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1468
1469     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1470                               clientResources, XtNumber(clientResources),
1471                               NULL, 0);
1472
1473     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1474         static char buf[MSG_SIZ];
1475         EscapeExpand(buf, appData.firstInitString);
1476         appData.firstInitString = strdup(buf);
1477         EscapeExpand(buf, appData.secondInitString);
1478         appData.secondInitString = strdup(buf);
1479         EscapeExpand(buf, appData.firstComputerString);
1480         appData.firstComputerString = strdup(buf);
1481         EscapeExpand(buf, appData.secondComputerString);
1482         appData.secondComputerString = strdup(buf);
1483     }
1484
1485     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1486         chessDir = ".";
1487     } else {
1488         if (chdir(chessDir) != 0) {
1489             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1490             perror(chessDir);
1491             exit(1);
1492         }
1493     }
1494
1495     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1496         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1497         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1498            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1499            exit(errno);
1500         }
1501         setbuf(debugFP, NULL);
1502     }
1503
1504 #if ENABLE_NLS
1505     if (appData.debugMode) {
1506       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1507     }
1508 #endif
1509
1510     /* [HGM,HR] make sure board size is acceptable */
1511     if(appData.NrFiles > BOARD_FILES ||
1512        appData.NrRanks > BOARD_RANKS   )
1513          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1514
1515 #if !HIGHDRAG
1516     /* This feature does not work; animation needs a rewrite */
1517     appData.highlightDragging = FALSE;
1518 #endif
1519     InitBackEnd1();
1520
1521     xDisplay = XtDisplay(shellWidget);
1522     xScreen = DefaultScreen(xDisplay);
1523     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1524
1525         gameInfo.variant = StringToVariant(appData.variant);
1526         InitPosition(FALSE);
1527
1528 #ifdef IDSIZE
1529     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1530 #else
1531     if (isdigit(appData.boardSize[0])) {
1532         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1533                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1534                    &fontPxlSize, &smallLayout, &tinyLayout);
1535         if (i == 0) {
1536             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1537                     programName, appData.boardSize);
1538             exit(2);
1539         }
1540         if (i < 7) {
1541             /* Find some defaults; use the nearest known size */
1542             SizeDefaults *szd, *nearest;
1543             int distance = 99999;
1544             nearest = szd = sizeDefaults;
1545             while (szd->name != NULL) {
1546                 if (abs(szd->squareSize - squareSize) < distance) {
1547                     nearest = szd;
1548                     distance = abs(szd->squareSize - squareSize);
1549                     if (distance == 0) break;
1550                 }
1551                 szd++;
1552             }
1553             if (i < 2) lineGap = nearest->lineGap;
1554             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1555             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1556             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1557             if (i < 6) smallLayout = nearest->smallLayout;
1558             if (i < 7) tinyLayout = nearest->tinyLayout;
1559         }
1560     } else {
1561         SizeDefaults *szd = sizeDefaults;
1562         if (*appData.boardSize == NULLCHAR) {
1563             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1564                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1565               szd++;
1566             }
1567             if (szd->name == NULL) szd--;
1568             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1569         } else {
1570             while (szd->name != NULL &&
1571                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1572             if (szd->name == NULL) {
1573                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1574                         programName, appData.boardSize);
1575                 exit(2);
1576             }
1577         }
1578         squareSize = szd->squareSize;
1579         lineGap = szd->lineGap;
1580         clockFontPxlSize = szd->clockFontPxlSize;
1581         coordFontPxlSize = szd->coordFontPxlSize;
1582         fontPxlSize = szd->fontPxlSize;
1583         smallLayout = szd->smallLayout;
1584         tinyLayout = szd->tinyLayout;
1585         // [HGM] font: use defaults from settings file if available and not overruled
1586     }
1587     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1588         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1589     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1590         appData.font = fontTable[MESSAGE_FONT][squareSize];
1591     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1592         appData.coordFont = fontTable[COORD_FONT][squareSize];
1593
1594     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1595     if (strlen(appData.pixmapDirectory) > 0) {
1596         p = ExpandPathName(appData.pixmapDirectory);
1597         if (!p) {
1598             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1599                    appData.pixmapDirectory);
1600             exit(1);
1601         }
1602         if (appData.debugMode) {
1603           fprintf(stderr, _("\
1604 XBoard square size (hint): %d\n\
1605 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1606         }
1607         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1608         if (appData.debugMode) {
1609             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1610         }
1611     }
1612     defaultLineGap = lineGap;
1613     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1614
1615     /* [HR] height treated separately (hacked) */
1616     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1617     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1618         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1619         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1620
1621     /*
1622      * Determine what fonts to use.
1623      */
1624 #if ENABLE_NLS
1625     appData.font = InsertPxlSize(appData.font, fontPxlSize);
1626     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
1627     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
1628     fontSet = CreateFontSet(appData.font);
1629     clockFontSet = CreateFontSet(appData.clockFont);
1630     {
1631       /* For the coordFont, use the 0th font of the fontset. */
1632       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
1633       XFontStruct **font_struct_list;
1634       XFontSetExtents *fontSize;
1635       char **font_name_list;
1636       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
1637       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
1638       coordFontStruct = XQueryFont(xDisplay, coordFontID);
1639       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
1640       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
1641     }
1642 #else
1643     appData.font = FindFont(appData.font, fontPxlSize);
1644     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1645     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1646     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1647     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1648     coordFontID = XLoadFont(xDisplay, appData.coordFont);
1649     coordFontStruct = XQueryFont(xDisplay, coordFontID);
1650 #endif
1651     countFontID = coordFontID;  // [HGM] holdings
1652     countFontStruct = coordFontStruct;
1653
1654     xdb = XtDatabase(xDisplay);
1655 #if ENABLE_NLS
1656     XrmPutLineResource(&xdb, "*international: True");
1657     vTo.size = sizeof(XFontSet);
1658     vTo.addr = (XtPointer) &fontSet;
1659     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
1660 #else
1661     XrmPutStringResource(&xdb, "*font", appData.font);
1662 #endif
1663
1664     /*
1665      * Detect if there are not enough colors available and adapt.
1666      */
1667     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1668       appData.monoMode = True;
1669     }
1670
1671     forceMono = MakeColors();
1672
1673     if (forceMono) {
1674       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1675               programName);
1676         appData.monoMode = True;
1677     }
1678
1679     if (appData.lowTimeWarning && !appData.monoMode) {
1680       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1681       vFrom.size = strlen(appData.lowTimeWarningColor);
1682       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1683       if (vTo.addr == NULL)
1684                 appData.monoMode = True;
1685       else
1686                 lowTimeWarningColor = *(Pixel *) vTo.addr;
1687     }
1688
1689     if (appData.monoMode && appData.debugMode) {
1690         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1691                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1692                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1693     }
1694
1695     ParseIcsTextColors();
1696     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1697     textColors[ColorNone].attr = 0;
1698
1699     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1700
1701     /*
1702      * widget hierarchy
1703      */
1704     if (tinyLayout) {
1705         layoutName = "tinyLayout";
1706     } else if (smallLayout) {
1707         layoutName = "smallLayout";
1708     } else {
1709         layoutName = "normalLayout";
1710     }
1711     /* Outer layoutWidget is there only to provide a name for use in
1712        resources that depend on the layout style */
1713     layoutWidget =
1714       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
1715                             layoutArgs, XtNumber(layoutArgs));
1716     formWidget =
1717       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
1718                             formArgs, XtNumber(formArgs));
1719     XtSetArg(args[0], XtNdefaultDistance, &sep);
1720     XtGetValues(formWidget, args, 1);
1721
1722     j = 0;
1723     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar, boardWidth);
1724     XtSetArg(args[0], XtNtop,    XtChainTop);
1725     XtSetArg(args[1], XtNbottom, XtChainTop);
1726     XtSetArg(args[2], XtNright,  XtChainLeft);
1727     XtSetValues(menuBarWidget, args, 3);
1728
1729     widgetList[j++] = whiteTimerWidget =
1730       XtCreateWidget("whiteTime", labelWidgetClass,
1731                      formWidget, timerArgs, XtNumber(timerArgs));
1732 #if ENABLE_NLS
1733     XtSetArg(args[0], XtNfontSet, clockFontSet);
1734 #else
1735     XtSetArg(args[0], XtNfont, clockFontStruct);
1736 #endif
1737     XtSetArg(args[1], XtNtop,    XtChainTop);
1738     XtSetArg(args[2], XtNbottom, XtChainTop);
1739     XtSetValues(whiteTimerWidget, args, 3);
1740
1741     widgetList[j++] = blackTimerWidget =
1742       XtCreateWidget("blackTime", labelWidgetClass,
1743                      formWidget, timerArgs, XtNumber(timerArgs));
1744 #if ENABLE_NLS
1745     XtSetArg(args[0], XtNfontSet, clockFontSet);
1746 #else
1747     XtSetArg(args[0], XtNfont, clockFontStruct);
1748 #endif
1749     XtSetArg(args[1], XtNtop,    XtChainTop);
1750     XtSetArg(args[2], XtNbottom, XtChainTop);
1751     XtSetValues(blackTimerWidget, args, 3);
1752
1753     if (appData.titleInWindow) {
1754         widgetList[j++] = titleWidget =
1755           XtCreateWidget("title", labelWidgetClass, formWidget,
1756                          titleArgs, XtNumber(titleArgs));
1757         XtSetArg(args[0], XtNtop,    XtChainTop);
1758         XtSetArg(args[1], XtNbottom, XtChainTop);
1759         XtSetValues(titleWidget, args, 2);
1760     }
1761
1762     if (appData.showButtonBar) {
1763       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
1764       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
1765       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
1766       XtSetArg(args[2], XtNtop,    XtChainTop);
1767       XtSetArg(args[3], XtNbottom, XtChainTop);
1768       XtSetValues(buttonBarWidget, args, 4);
1769     }
1770
1771     widgetList[j++] = messageWidget =
1772       XtCreateWidget("message", labelWidgetClass, formWidget,
1773                      messageArgs, XtNumber(messageArgs));
1774     XtSetArg(args[0], XtNtop,    XtChainTop);
1775     XtSetArg(args[1], XtNbottom, XtChainTop);
1776     XtSetValues(messageWidget, args, 2);
1777
1778     widgetList[j++] = boardWidget =
1779       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
1780                      XtNumber(boardArgs));
1781
1782     XtManageChildren(widgetList, j);
1783
1784     timerWidth = (boardWidth - sep) / 2;
1785     XtSetArg(args[0], XtNwidth, timerWidth);
1786     XtSetValues(whiteTimerWidget, args, 1);
1787     XtSetValues(blackTimerWidget, args, 1);
1788
1789     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1790     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1791     XtGetValues(whiteTimerWidget, args, 2);
1792
1793     if (appData.showButtonBar) {
1794       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1795       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1796       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
1797     }
1798
1799     /*
1800      * formWidget uses these constraints but they are stored
1801      * in the children.
1802      */
1803     i = 0;
1804     XtSetArg(args[i], XtNfromHoriz, 0); i++;
1805     XtSetValues(menuBarWidget, args, i);
1806     if (appData.titleInWindow) {
1807         if (smallLayout) {
1808             i = 0;
1809             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1810             XtSetValues(whiteTimerWidget, args, i);
1811             i = 0;
1812             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1813             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1814             XtSetValues(blackTimerWidget, args, i);
1815             i = 0;
1816             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1817             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
1818             XtSetValues(titleWidget, args, i);
1819             i = 0;
1820             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1821             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1822             XtSetValues(messageWidget, args, i);
1823             if (appData.showButtonBar) {
1824               i = 0;
1825               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1826               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1827               XtSetValues(buttonBarWidget, args, i);
1828             }
1829         } else {
1830             i = 0;
1831             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1832             XtSetValues(whiteTimerWidget, args, i);
1833             i = 0;
1834             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
1835             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1836             XtSetValues(blackTimerWidget, args, i);
1837             i = 0;
1838             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
1839             XtSetValues(titleWidget, args, i);
1840             i = 0;
1841             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1842             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1843             XtSetValues(messageWidget, args, i);
1844             if (appData.showButtonBar) {
1845               i = 0;
1846               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1847               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1848               XtSetValues(buttonBarWidget, args, i);
1849             }
1850         }
1851     } else {
1852         i = 0;
1853         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1854         XtSetValues(whiteTimerWidget, args, i);
1855         i = 0;
1856         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
1857         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
1858         XtSetValues(blackTimerWidget, args, i);
1859         i = 0;
1860         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1861         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
1862         XtSetValues(messageWidget, args, i);
1863         if (appData.showButtonBar) {
1864           i = 0;
1865           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
1866           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
1867           XtSetValues(buttonBarWidget, args, i);
1868         }
1869     }
1870     i = 0;
1871     XtSetArg(args[0], XtNfromVert, messageWidget);
1872     XtSetArg(args[1], XtNtop,    XtChainTop);
1873     XtSetArg(args[2], XtNbottom, XtChainBottom);
1874     XtSetArg(args[3], XtNleft,   XtChainLeft);
1875     XtSetArg(args[4], XtNright,  XtChainRight);
1876     XtSetValues(boardWidget, args, 5);
1877
1878     XtRealizeWidget(shellWidget);
1879
1880     if(wpMain.x > 0) {
1881       XtSetArg(args[0], XtNx, wpMain.x);
1882       XtSetArg(args[1], XtNy, wpMain.y);
1883       XtSetValues(shellWidget, args, 2);
1884     }
1885
1886     /*
1887      * Correct the width of the message and title widgets.
1888      * It is not known why some systems need the extra fudge term.
1889      * The value "2" is probably larger than needed.
1890      */
1891     XawFormDoLayout(formWidget, False);
1892
1893 #define WIDTH_FUDGE 2
1894     i = 0;
1895     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
1896     XtSetArg(args[i], XtNheight, &h);  i++;
1897     XtGetValues(messageWidget, args, i);
1898     if (appData.showButtonBar) {
1899       i = 0;
1900       XtSetArg(args[i], XtNwidth, &w);  i++;
1901       XtGetValues(buttonBarWidget, args, i);
1902       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1903     } else {
1904       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
1905     }
1906
1907     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1908     if (gres != XtGeometryYes && appData.debugMode) {
1909       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1910               programName, gres, w, h, wr, hr);
1911     }
1912
1913     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
1914     /* The size used for the child widget in layout lags one resize behind
1915        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
1916     w--;
1917     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
1918     if (gres != XtGeometryYes && appData.debugMode) {
1919       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
1920               programName, gres, w, h, wr, hr);
1921     }
1922     /* !! end hack */
1923     if(!textHeight) textHeight = hr; // [HGM] if !NLS textHeight is still undefined, and we grab it from here
1924     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
1925     XtSetArg(args[1], XtNright, XtChainRight);
1926     XtSetValues(messageWidget, args, 2);
1927
1928     if (appData.titleInWindow) {
1929         i = 0;
1930         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1931         XtSetArg(args[i], XtNheight, &h);  i++;
1932         XtGetValues(titleWidget, args, i);
1933         if (smallLayout) {
1934             w = boardWidth - 2*bor;
1935         } else {
1936             XtSetArg(args[0], XtNwidth, &w);
1937             XtGetValues(menuBarWidget, args, 1);
1938             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
1939         }
1940
1941         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1942         if (gres != XtGeometryYes && appData.debugMode) {
1943             fprintf(stderr,
1944                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1945                     programName, gres, w, h, wr, hr);
1946         }
1947     }
1948     XawFormDoLayout(formWidget, True);
1949
1950     xBoardWindow = XtWindow(boardWidget);
1951
1952     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1953     //       not need to go into InitDrawingSizes().
1954 #endif
1955
1956     /*
1957      * Create X checkmark bitmap and initialize option menu checks.
1958      */
1959     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1960                checkmark_bits, checkmark_width, checkmark_height);
1961     InitMenuMarkers();
1962
1963     /*
1964      * Create an icon.
1965      */
1966     ReadBitmap(&wIconPixmap, "icon_white.bm",
1967                icon_white_bits, icon_white_width, icon_white_height);
1968     ReadBitmap(&bIconPixmap, "icon_black.bm",
1969                icon_black_bits, icon_black_width, icon_black_height);
1970     iconPixmap = wIconPixmap;
1971     i = 0;
1972     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1973     XtSetValues(shellWidget, args, i);
1974
1975     /*
1976      * Create a cursor for the board widget.
1977      */
1978     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1979     XChangeWindowAttributes(xDisplay, xBoardWindow,
1980                             CWCursor, &window_attributes);
1981
1982     /*
1983      * Inhibit shell resizing.
1984      */
1985     shellArgs[0].value = (XtArgVal) &w;
1986     shellArgs[1].value = (XtArgVal) &h;
1987     XtGetValues(shellWidget, shellArgs, 2);
1988     shellArgs[4].value = shellArgs[2].value = w;
1989     shellArgs[5].value = shellArgs[3].value = h;
1990     XtSetValues(shellWidget, &shellArgs[2], 4);
1991     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1992     marginH =  h - boardHeight;
1993
1994     CatchDeleteWindow(shellWidget, "QuitProc");
1995
1996     CreateGCs(False);
1997     CreateGrid();
1998     CreateAnyPieces();
1999
2000     CreatePieceMenus();
2001
2002     if (appData.animate || appData.animateDragging)
2003       CreateAnimVars();
2004
2005     XtAugmentTranslations(formWidget,
2006                           XtParseTranslationTable(globalTranslations));
2007     XtAugmentTranslations(boardWidget,
2008                           XtParseTranslationTable(boardTranslations));
2009     XtAugmentTranslations(whiteTimerWidget,
2010                           XtParseTranslationTable(whiteTranslations));
2011     XtAugmentTranslations(blackTimerWidget,
2012                           XtParseTranslationTable(blackTranslations));
2013
2014     /* Why is the following needed on some versions of X instead
2015      * of a translation? */
2016     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2017                       (XtEventHandler) EventProc, NULL);
2018     /* end why */
2019     XtAddEventHandler(formWidget, KeyPressMask, False,
2020                       (XtEventHandler) MoveTypeInProc, NULL);
2021     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
2022                       (XtEventHandler) EventProc, NULL);
2023
2024     /* [AS] Restore layout */
2025     if( wpMoveHistory.visible ) {
2026       HistoryPopUp();
2027     }
2028
2029     if( wpEvalGraph.visible )
2030       {
2031         EvalGraphPopUp();
2032       };
2033
2034     if( wpEngineOutput.visible ) {
2035       EngineOutputPopUp();
2036     }
2037
2038     InitBackEnd2();
2039
2040     if (errorExitStatus == -1) {
2041         if (appData.icsActive) {
2042             /* We now wait until we see "login:" from the ICS before
2043                sending the logon script (problems with timestamp otherwise) */
2044             /*ICSInitScript();*/
2045             if (appData.icsInputBox) ICSInputBoxPopUp();
2046         }
2047
2048     #ifdef SIGWINCH
2049     signal(SIGWINCH, TermSizeSigHandler);
2050     #endif
2051         signal(SIGINT, IntSigHandler);
2052         signal(SIGTERM, IntSigHandler);
2053         if (*appData.cmailGameName != NULLCHAR) {
2054             signal(SIGUSR1, CmailSigHandler);
2055         }
2056     }
2057
2058     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2059     InitPosition(TRUE);
2060 //    XtSetKeyboardFocus(shellWidget, formWidget);
2061     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
2062
2063     XtAppMainLoop(appContext);
2064     if (appData.debugMode) fclose(debugFP); // [DM] debug
2065     return 0;
2066 }
2067
2068 static Boolean noEcho;
2069
2070 void
2071 ShutDownFrontEnd ()
2072 {
2073     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2074         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2075     }
2076     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2077     unlink(gameCopyFilename);
2078     unlink(gamePasteFilename);
2079     if(noEcho) EchoOn();
2080 }
2081
2082 RETSIGTYPE
2083 TermSizeSigHandler (int sig)
2084 {
2085     update_ics_width();
2086 }
2087
2088 RETSIGTYPE
2089 IntSigHandler (int sig)
2090 {
2091     ExitEvent(sig);
2092 }
2093
2094 RETSIGTYPE
2095 CmailSigHandler (int sig)
2096 {
2097     int dummy = 0;
2098     int error;
2099
2100     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2101
2102     /* Activate call-back function CmailSigHandlerCallBack()             */
2103     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2104
2105     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2106 }
2107
2108 void
2109 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
2110 {
2111     BoardToTop();
2112     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2113 }
2114 /**** end signal code ****/
2115
2116
2117 void
2118 ICSInitScript ()
2119 {
2120   /* try to open the icsLogon script, either in the location given
2121    * or in the users HOME directory
2122    */
2123
2124   FILE *f;
2125   char buf[MSG_SIZ];
2126   char *homedir;
2127
2128   f = fopen(appData.icsLogon, "r");
2129   if (f == NULL)
2130     {
2131       homedir = getenv("HOME");
2132       if (homedir != NULL)
2133         {
2134           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2135           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2136           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
2137           f = fopen(buf, "r");
2138         }
2139     }
2140
2141   if (f != NULL)
2142     ProcessICSInitScript(f);
2143   else
2144     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2145
2146   return;
2147 }
2148
2149 void
2150 ResetFrontEnd ()
2151 {
2152     CommentPopDown();
2153     TagsPopDown();
2154     return;
2155 }
2156
2157 #define Abs(n) ((n)<0 ? -(n) : (n))
2158
2159 #ifdef ENABLE_NLS
2160 char *
2161 InsertPxlSize (char *pattern, int targetPxlSize)
2162 {
2163     char *base_fnt_lst, strInt[12], *p, *q;
2164     int alternatives, i, len, strIntLen;
2165
2166     /*
2167      * Replace the "*" (if present) in the pixel-size slot of each
2168      * alternative with the targetPxlSize.
2169      */
2170     p = pattern;
2171     alternatives = 1;
2172     while ((p = strchr(p, ',')) != NULL) {
2173       alternatives++;
2174       p++;
2175     }
2176     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
2177     strIntLen = strlen(strInt);
2178     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
2179
2180     p = pattern;
2181     q = base_fnt_lst;
2182     while (alternatives--) {
2183       char *comma = strchr(p, ',');
2184       for (i=0; i<14; i++) {
2185         char *hyphen = strchr(p, '-');
2186         if (!hyphen) break;
2187         if (comma && hyphen > comma) break;
2188         len = hyphen + 1 - p;
2189         if (i == 7 && *p == '*' && len == 2) {
2190           p += len;
2191           memcpy(q, strInt, strIntLen);
2192           q += strIntLen;
2193           *q++ = '-';
2194         } else {
2195           memcpy(q, p, len);
2196           p += len;
2197           q += len;
2198         }
2199       }
2200       if (!comma) break;
2201       len = comma + 1 - p;
2202       memcpy(q, p, len);
2203       p += len;
2204       q += len;
2205     }
2206     strcpy(q, p);
2207
2208     return base_fnt_lst;
2209 }
2210
2211 XFontSet
2212 CreateFontSet (char *base_fnt_lst)
2213 {
2214     XFontSet fntSet;
2215     char **missing_list;
2216     int missing_count;
2217     char *def_string;
2218
2219     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
2220                             &missing_list, &missing_count, &def_string);
2221     if (appData.debugMode) {
2222       int i, count;
2223       XFontStruct **font_struct_list;
2224       char **font_name_list;
2225       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
2226       if (fntSet) {
2227         fprintf(debugFP, " got list %s, locale %s\n",
2228                 XBaseFontNameListOfFontSet(fntSet),
2229                 XLocaleOfFontSet(fntSet));
2230         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
2231         for (i = 0; i < count; i++) {
2232           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
2233         }
2234       }
2235       for (i = 0; i < missing_count; i++) {
2236         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
2237       }
2238     }
2239     if (fntSet == NULL) {
2240       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
2241       exit(2);
2242     }
2243     return fntSet;
2244 }
2245 #else // not ENABLE_NLS
2246 /*
2247  * Find a font that matches "pattern" that is as close as
2248  * possible to the targetPxlSize.  Prefer fonts that are k
2249  * pixels smaller to fonts that are k pixels larger.  The
2250  * pattern must be in the X Consortium standard format,
2251  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2252  * The return value should be freed with XtFree when no
2253  * longer needed.
2254  */
2255 char *
2256 FindFont (char *pattern, int targetPxlSize)
2257 {
2258     char **fonts, *p, *best, *scalable, *scalableTail;
2259     int i, j, nfonts, minerr, err, pxlSize;
2260
2261     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2262     if (nfonts < 1) {
2263         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2264                 programName, pattern);
2265         exit(2);
2266     }
2267
2268     best = fonts[0];
2269     scalable = NULL;
2270     minerr = 999999;
2271     for (i=0; i<nfonts; i++) {
2272         j = 0;
2273         p = fonts[i];
2274         if (*p != '-') continue;
2275         while (j < 7) {
2276             if (*p == NULLCHAR) break;
2277             if (*p++ == '-') j++;
2278         }
2279         if (j < 7) continue;
2280         pxlSize = atoi(p);
2281         if (pxlSize == 0) {
2282             scalable = fonts[i];
2283             scalableTail = p;
2284         } else {
2285             err = pxlSize - targetPxlSize;
2286             if (Abs(err) < Abs(minerr) ||
2287                 (minerr > 0 && err < 0 && -err == minerr)) {
2288                 best = fonts[i];
2289                 minerr = err;
2290             }
2291         }
2292     }
2293     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2294         /* If the error is too big and there is a scalable font,
2295            use the scalable font. */
2296         int headlen = scalableTail - scalable;
2297         p = (char *) XtMalloc(strlen(scalable) + 10);
2298         while (isdigit(*scalableTail)) scalableTail++;
2299         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2300     } else {
2301         p = (char *) XtMalloc(strlen(best) + 2);
2302         safeStrCpy(p, best, strlen(best)+1 );
2303     }
2304     if (appData.debugMode) {
2305         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2306                 pattern, targetPxlSize, p);
2307     }
2308     XFreeFontNames(fonts);
2309     return p;
2310 }
2311 #endif
2312
2313 void
2314 DeleteGCs ()
2315 {   // [HGM] deletes GCs that are to be remade, to prevent resource leak;
2316     // must be called before all non-first callse to CreateGCs()
2317     XtReleaseGC(shellWidget, highlineGC);
2318     XtReleaseGC(shellWidget, lightSquareGC);
2319     XtReleaseGC(shellWidget, darkSquareGC);
2320     XtReleaseGC(shellWidget, lineGC);
2321     if (appData.monoMode) {
2322         if (DefaultDepth(xDisplay, xScreen) == 1) {
2323             XtReleaseGC(shellWidget, wbPieceGC);
2324         } else {
2325             XtReleaseGC(shellWidget, bwPieceGC);
2326         }
2327     } else {
2328         XtReleaseGC(shellWidget, prelineGC);
2329         XtReleaseGC(shellWidget, wdPieceGC);
2330         XtReleaseGC(shellWidget, wlPieceGC);
2331         XtReleaseGC(shellWidget, bdPieceGC);
2332         XtReleaseGC(shellWidget, blPieceGC);
2333     }
2334 }
2335
2336 static GC
2337 CreateOneGC (XGCValues *gc_values, Pixel foreground, Pixel background)
2338 {
2339     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2340       | GCBackground | GCFunction | GCPlaneMask;
2341     gc_values->foreground = foreground;
2342     gc_values->background = background;
2343     return XtGetGC(shellWidget, value_mask, gc_values);
2344 }
2345
2346 static void
2347 CreateGCs (int redo)
2348 {
2349     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2350       | GCBackground | GCFunction | GCPlaneMask;
2351     XGCValues gc_values;
2352     GC copyInvertedGC;
2353     Pixel white = XWhitePixel(xDisplay, xScreen);
2354     Pixel black = XBlackPixel(xDisplay, xScreen);
2355
2356     gc_values.plane_mask = AllPlanes;
2357     gc_values.line_width = lineGap;
2358     gc_values.line_style = LineSolid;
2359     gc_values.function = GXcopy;
2360
2361   if(redo) {
2362     DeleteGCs(); // called a second time; clean up old GCs first
2363   } else { // [HGM] grid and font GCs created on first call only
2364     coordGC = CreateOneGC(&gc_values, black, white);
2365     XSetFont(xDisplay, coordGC, coordFontID);
2366
2367     // [HGM] make font for holdings counts (white on black)
2368     countGC = CreateOneGC(&gc_values, white, black);
2369     XSetFont(xDisplay, countGC, countFontID);
2370   }
2371     lineGC = CreateOneGC(&gc_values, black, black);
2372
2373     if (appData.monoMode) {
2374
2375         highlineGC = CreateOneGC(&gc_values, white, white);
2376         lightSquareGC = wbPieceGC = CreateOneGC(&gc_values, white, black);
2377         darkSquareGC = bwPieceGC = CreateOneGC(&gc_values, black, white);
2378
2379         if (DefaultDepth(xDisplay, xScreen) == 1) {
2380             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2381             gc_values.function = GXcopyInverted;
2382             copyInvertedGC = CreateOneGC(&gc_values, black, white);
2383             gc_values.function = GXcopy;
2384             if (XBlackPixel(xDisplay, xScreen) == 1) {
2385                 bwPieceGC = darkSquareGC;
2386                 wbPieceGC = copyInvertedGC;
2387             } else {
2388                 bwPieceGC = copyInvertedGC;
2389                 wbPieceGC = lightSquareGC;
2390             }
2391         }
2392     } else {
2393
2394         highlineGC = CreateOneGC(&gc_values, highlightSquareColor, highlightSquareColor);
2395         prelineGC = CreateOneGC(&gc_values, premoveHighlightColor, premoveHighlightColor);
2396         lightSquareGC = CreateOneGC(&gc_values, lightSquareColor, darkSquareColor);
2397         darkSquareGC = CreateOneGC(&gc_values, darkSquareColor, lightSquareColor);
2398         wdPieceGC = CreateOneGC(&gc_values, whitePieceColor, darkSquareColor);
2399         wlPieceGC = CreateOneGC(&gc_values, whitePieceColor, lightSquareColor);
2400         bdPieceGC = CreateOneGC(&gc_values, blackPieceColor, darkSquareColor);
2401         blPieceGC = CreateOneGC(&gc_values, blackPieceColor, lightSquareColor);
2402     }
2403 }
2404
2405 void
2406 loadXIM (XImage *xim, XImage *xmask, char *filename, Pixmap *dest, Pixmap *mask)
2407 {
2408     int x, y, w, h, p;
2409     FILE *fp;
2410     Pixmap temp;
2411     XGCValues   values;
2412     GC maskGC;
2413
2414     fp = fopen(filename, "rb");
2415     if (!fp) {
2416         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
2417         exit(1);
2418     }
2419
2420     w = fgetc(fp);
2421     h = fgetc(fp);
2422
2423     for (y=0; y<h; ++y) {
2424         for (x=0; x<h; ++x) {
2425             p = fgetc(fp);
2426
2427             switch (p) {
2428               case 0:
2429                 XPutPixel(xim, x, y, blackPieceColor);
2430                 if (xmask)
2431                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2432                 break;
2433               case 1:
2434                 XPutPixel(xim, x, y, darkSquareColor);
2435                 if (xmask)
2436                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2437                 break;
2438               case 2:
2439                 XPutPixel(xim, x, y, whitePieceColor);
2440                 if (xmask)
2441                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
2442                 break;
2443               case 3:
2444                 XPutPixel(xim, x, y, lightSquareColor);
2445                 if (xmask)
2446                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
2447                 break;
2448             }
2449         }
2450     }
2451
2452     fclose(fp);
2453
2454     /* create Pixmap of piece */
2455     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2456                           w, h, xim->depth);
2457     XPutImage(xDisplay, *dest, lightSquareGC, xim,
2458               0, 0, 0, 0, w, h);
2459
2460     /* create Pixmap of clipmask
2461        Note: We assume the white/black pieces have the same
2462              outline, so we make only 6 masks. This is okay
2463              since the XPM clipmask routines do the same. */
2464     if (xmask) {
2465       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2466                             w, h, xim->depth);
2467       XPutImage(xDisplay, temp, lightSquareGC, xmask,
2468               0, 0, 0, 0, w, h);
2469
2470       /* now create the 1-bit version */
2471       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
2472                           w, h, 1);
2473
2474       values.foreground = 1;
2475       values.background = 0;
2476
2477       /* Don't use XtGetGC, not read only */
2478       maskGC = XCreateGC(xDisplay, *mask,
2479                     GCForeground | GCBackground, &values);
2480       XCopyPlane(xDisplay, temp, *mask, maskGC,
2481                   0, 0, squareSize, squareSize, 0, 0, 1);
2482       XFreePixmap(xDisplay, temp);
2483     }
2484 }
2485
2486
2487 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
2488
2489 void
2490 CreateXIMPieces ()
2491 {
2492     int piece, kind;
2493     char buf[MSG_SIZ];
2494     u_int ss;
2495     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
2496     XImage *ximtemp;
2497
2498     ss = squareSize;
2499
2500     /* The XSynchronize calls were copied from CreatePieces.
2501        Not sure if needed, but can't hurt */
2502     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2503                                      buffering bug */
2504
2505     /* temp needed by loadXIM() */
2506     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2507                  0, 0, ss, ss, AllPlanes, XYPixmap);
2508
2509     if (strlen(appData.pixmapDirectory) == 0) {
2510       useImages = 0;
2511     } else {
2512         useImages = 1;
2513         if (appData.monoMode) {
2514           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
2515                             0, 2);
2516           ExitEvent(2);
2517         }
2518         fprintf(stderr, _("\nLoading XIMs...\n"));
2519         /* Load pieces */
2520         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2521             fprintf(stderr, "%d", piece+1);
2522             for (kind=0; kind<4; kind++) {
2523                 fprintf(stderr, ".");
2524                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
2525                         ExpandPathName(appData.pixmapDirectory),
2526                         piece <= (int) WhiteKing ? "" : "w",
2527                         pieceBitmapNames[piece],
2528                         ximkind[kind], ss);
2529                 ximPieceBitmap[kind][piece] =
2530                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2531                             0, 0, ss, ss, AllPlanes, XYPixmap);
2532                 if (appData.debugMode)
2533                   fprintf(stderr, _("(File:%s:) "), buf);
2534                 loadXIM(ximPieceBitmap[kind][piece],
2535                         ximtemp, buf,
2536                         &(xpmPieceBitmap2[kind][piece]),
2537                         &(ximMaskPm2[piece]));
2538                 if(piece <= (int)WhiteKing)
2539                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2540             }
2541             fprintf(stderr," ");
2542         }
2543         /* Load light and dark squares */
2544         /* If the LSQ and DSQ pieces don't exist, we will
2545            draw them with solid squares. */
2546         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
2547         if (access(buf, 0) != 0) {
2548             useImageSqs = 0;
2549         } else {
2550             useImageSqs = 1;
2551             fprintf(stderr, _("light square "));
2552             ximLightSquare=
2553               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2554                         0, 0, ss, ss, AllPlanes, XYPixmap);
2555             if (appData.debugMode)
2556               fprintf(stderr, _("(File:%s:) "), buf);
2557
2558             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
2559             fprintf(stderr, _("dark square "));
2560             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
2561                     ExpandPathName(appData.pixmapDirectory), ss);
2562             if (appData.debugMode)
2563               fprintf(stderr, _("(File:%s:) "), buf);
2564             ximDarkSquare=
2565               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
2566                         0, 0, ss, ss, AllPlanes, XYPixmap);
2567             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
2568             xpmJailSquare = xpmLightSquare;
2569         }
2570         fprintf(stderr, _("Done.\n"));
2571     }
2572     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
2573 }
2574
2575 static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
2576
2577 #if HAVE_LIBXPM
2578 void
2579 CreateXPMBoard (char *s, int kind)
2580 {
2581     XpmAttributes attr;
2582     attr.valuemask = 0;
2583     if(!appData.useBitmaps || s == NULL || *s == 0 || *s == '*') { useTexture &= ~(kind+1); return; }
2584     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
2585         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
2586     }
2587 }
2588
2589 void
2590 FreeXPMPieces ()
2591 {   // [HGM] to prevent resoucre leak on calling CreaeXPMPieces() a second time,
2592     // thisroutine has to be called t free the old piece pixmaps
2593     int piece, kind;
2594     for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++)
2595         for (kind=0; kind<4; kind++) XFreePixmap(xDisplay, xpmPieceBitmap2[kind][piece]);
2596     if(useImageSqs) {
2597         XFreePixmap(xDisplay, xpmLightSquare);
2598         XFreePixmap(xDisplay, xpmDarkSquare);
2599     }
2600 }
2601
2602 void
2603 CreateXPMPieces ()
2604 {
2605     int piece, kind, r;
2606     char buf[MSG_SIZ];
2607     u_int ss = squareSize;
2608     XpmAttributes attr;
2609     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
2610     XpmColorSymbol symbols[4];
2611     static int redo = False;
2612
2613     if(redo) FreeXPMPieces(); else redo = 1;
2614
2615     /* The XSynchronize calls were copied from CreatePieces.
2616        Not sure if needed, but can't hurt */
2617     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
2618
2619     /* Setup translations so piece colors match square colors */
2620     symbols[0].name = "light_piece";
2621     symbols[0].value = appData.whitePieceColor;
2622     symbols[1].name = "dark_piece";
2623     symbols[1].value = appData.blackPieceColor;
2624     symbols[2].name = "light_square";
2625     symbols[2].value = appData.lightSquareColor;
2626     symbols[3].name = "dark_square";
2627     symbols[3].value = appData.darkSquareColor;
2628
2629     attr.valuemask = XpmColorSymbols;
2630     attr.colorsymbols = symbols;
2631     attr.numsymbols = 4;
2632
2633     if (appData.monoMode) {
2634       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
2635                         0, 2);
2636       ExitEvent(2);
2637     }
2638     if (strlen(appData.pixmapDirectory) == 0) {
2639         XpmPieces* pieces = builtInXpms;
2640         useImages = 1;
2641         /* Load pieces */
2642         while (pieces->size != squareSize && pieces->size) pieces++;
2643         if (!pieces->size) {
2644           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
2645           exit(1);
2646         }
2647         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2648             for (kind=0; kind<4; kind++) {
2649
2650                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
2651                                                pieces->xpm[piece][kind],
2652                                                &(xpmPieceBitmap2[kind][piece]),
2653                                                NULL, &attr)) != 0) {
2654                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
2655                           r, buf);
2656                   exit(1);
2657                 }
2658                 if(piece <= (int) WhiteKing)
2659                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2660             }
2661         }
2662         useImageSqs = 0;
2663         xpmJailSquare = xpmLightSquare;
2664     } else {
2665         useImages = 1;
2666
2667         fprintf(stderr, _("\nLoading XPMs...\n"));
2668
2669         /* Load pieces */
2670         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2671             fprintf(stderr, "%d ", piece+1);
2672             for (kind=0; kind<4; kind++) {
2673               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
2674                         ExpandPathName(appData.pixmapDirectory),
2675                         piece > (int) WhiteKing ? "w" : "",
2676                         pieceBitmapNames[piece],
2677                         xpmkind[kind], ss);
2678                 if (appData.debugMode) {
2679                     fprintf(stderr, _("(File:%s:) "), buf);
2680                 }
2681                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2682                                            &(xpmPieceBitmap2[kind][piece]),
2683                                            NULL, &attr)) != 0) {
2684                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
2685                       // [HGM] missing: read of unorthodox piece failed; substitute King.
2686                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
2687                                 ExpandPathName(appData.pixmapDirectory),
2688                                 xpmkind[kind], ss);
2689                         if (appData.debugMode) {
2690                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
2691                         }
2692                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2693                                                 &(xpmPieceBitmap2[kind][piece]),
2694                                                 NULL, &attr);
2695                     }
2696                     if (r != 0) {
2697                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
2698                                 r, buf);
2699                         exit(1);
2700                     }
2701                 }
2702                 if(piece <= (int) WhiteKing)
2703                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
2704             }
2705         }
2706         /* Load light and dark squares */
2707         /* If the LSQ and DSQ pieces don't exist, we will
2708            draw them with solid squares. */
2709         fprintf(stderr, _("light square "));
2710         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
2711         if (access(buf, 0) != 0) {
2712             useImageSqs = 0;
2713         } else {
2714             useImageSqs = 1;
2715             if (appData.debugMode)
2716               fprintf(stderr, _("(File:%s:) "), buf);
2717
2718             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2719                                        &xpmLightSquare, NULL, &attr)) != 0) {
2720                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2721                 exit(1);
2722             }
2723             fprintf(stderr, _("dark square "));
2724             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
2725                     ExpandPathName(appData.pixmapDirectory), ss);
2726             if (appData.debugMode) {
2727                 fprintf(stderr, _("(File:%s:) "), buf);
2728             }
2729             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
2730                                        &xpmDarkSquare, NULL, &attr)) != 0) {
2731                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
2732                 exit(1);
2733             }
2734         }
2735         xpmJailSquare = xpmLightSquare;
2736         fprintf(stderr, _("Done.\n"));
2737     }
2738     oldVariant = -1; // kludge to force re-makig of animation masks
2739     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2740                                       buffering bug */
2741 }
2742 #endif /* HAVE_LIBXPM */
2743
2744 #if HAVE_LIBXPM
2745 /* No built-in bitmaps */
2746 void CreatePieces()
2747 {
2748     int piece, kind;
2749     char buf[MSG_SIZ];
2750     u_int ss = squareSize;
2751
2752     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2753                                      buffering bug */
2754
2755     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2756         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2757           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2758                    pieceBitmapNames[piece],
2759                    ss, kind == SOLID ? 's' : 'o');
2760           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
2761           if(piece <= (int)WhiteKing)
2762             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2763         }
2764     }
2765
2766     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2767                                       buffering bug */
2768 }
2769 #else
2770 /* With built-in bitmaps */
2771 void
2772 CreatePieces ()
2773 {
2774     BuiltInBits* bib = builtInBits;
2775     int piece, kind;
2776     char buf[MSG_SIZ];
2777     u_int ss = squareSize;
2778
2779     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
2780                                      buffering bug */
2781
2782     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
2783
2784     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
2785         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
2786           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
2787                    pieceBitmapNames[piece],
2788                    ss, kind == SOLID ? 's' : 'o');
2789           ReadBitmap(&pieceBitmap2[kind][piece], buf,
2790                      bib->bits[kind][piece], ss, ss);
2791           if(piece <= (int)WhiteKing)
2792             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
2793         }
2794     }
2795
2796     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
2797                                       buffering bug */
2798 }
2799 #endif
2800
2801 void
2802 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
2803 {
2804     int x_hot, y_hot;
2805     u_int w, h;
2806     int errcode;
2807     char msg[MSG_SIZ], fullname[MSG_SIZ];
2808
2809     if (*appData.bitmapDirectory != NULLCHAR) {
2810       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
2811       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
2812       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
2813       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
2814                                 &w, &h, pm, &x_hot, &y_hot);
2815       fprintf(stderr, "load %s\n", name);
2816         if (errcode != BitmapSuccess) {
2817             switch (errcode) {
2818               case BitmapOpenFailed:
2819                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
2820                 break;
2821               case BitmapFileInvalid:
2822                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
2823                 break;
2824               case BitmapNoMemory:
2825                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
2826                         fullname);
2827                 break;
2828               default:
2829                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
2830                         errcode, fullname);
2831                 break;
2832             }
2833             fprintf(stderr, _("%s: %s...using built-in\n"),
2834                     programName, msg);
2835         } else if (w != wreq || h != hreq) {
2836             fprintf(stderr,
2837                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
2838                     programName, fullname, w, h, wreq, hreq);
2839         } else {
2840             return;
2841         }
2842     }
2843     if (bits != NULL) {
2844         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
2845                                     wreq, hreq);
2846     }
2847 }
2848
2849 void
2850 CreateGrid ()
2851 {
2852     int i, j;
2853
2854     if (lineGap == 0) return;
2855
2856     /* [HR] Split this into 2 loops for non-square boards. */
2857
2858     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
2859         gridSegments[i].x1 = 0;
2860         gridSegments[i].x2 =
2861           lineGap + BOARD_WIDTH * (squareSize + lineGap);
2862         gridSegments[i].y1 = gridSegments[i].y2
2863           = lineGap / 2 + (i * (squareSize + lineGap));
2864     }
2865
2866     for (j = 0; j < BOARD_WIDTH + 1; j++) {
2867         gridSegments[j + i].y1 = 0;
2868         gridSegments[j + i].y2 =
2869           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
2870         gridSegments[j + i].x1 = gridSegments[j + i].x2
2871           = lineGap / 2 + (j * (squareSize + lineGap));
2872     }
2873 }
2874
2875 int nrOfMenuItems = 7;
2876 Widget menuWidget[150];
2877 MenuListItem menuItemList[150] = {
2878     { "LoadNextGameProc", LoadNextGameProc },
2879     { "LoadPrevGameProc", LoadPrevGameProc },
2880     { "ReloadGameProc", ReloadGameProc },
2881     { "ReloadPositionProc", ReloadPositionProc },
2882 #ifndef OPTIONSDIALOG
2883     { "AlwaysQueenProc", AlwaysQueenProc },
2884     { "AnimateDraggingProc", AnimateDraggingProc },
2885     { "AnimateMovingProc", AnimateMovingProc },
2886     { "AutoflagProc", AutoflagProc },
2887     { "AutoflipProc", AutoflipProc },
2888     { "BlindfoldProc", BlindfoldProc },
2889     { "FlashMovesProc", FlashMovesProc },
2890 #if HIGHDRAG
2891     { "HighlightDraggingProc", HighlightDraggingProc },
2892 #endif
2893     { "HighlightLastMoveProc", HighlightLastMoveProc },
2894 //    { "IcsAlarmProc", IcsAlarmProc },
2895     { "MoveSoundProc", MoveSoundProc },
2896     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
2897     { "PopupExitMessageProc", PopupExitMessageProc },
2898     { "PopupMoveErrorsProc", PopupMoveErrorsProc },
2899 //    { "PremoveProc", PremoveProc },
2900     { "ShowCoordsProc", ShowCoordsProc },
2901     { "ShowThinkingProc", ShowThinkingProc },
2902     { "HideThinkingProc", HideThinkingProc },
2903     { "TestLegalityProc", TestLegalityProc },
2904 #endif
2905     { "AboutGameProc", AboutGameEvent },
2906     { "DebugProc", DebugProc },
2907     { "NothingProc", NothingProc },
2908   {NULL, NothingProc}
2909 };
2910
2911 void
2912 MarkMenuItem (char *menuRef, int state)
2913 {
2914     int nr = MenuToNumber(menuRef);
2915     if(nr >= 0) {
2916         Arg args[2];
2917         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
2918         XtSetValues(menuWidget[nr], args, 1);
2919     }
2920 }
2921
2922 void
2923 EnableMenuItem (char *menuRef, int state)
2924 {
2925     int nr = MenuToNumber(menuRef);
2926     if(nr >= 0) XtSetSensitive(menuWidget[nr], state);
2927 }
2928
2929 void
2930 EnableButtonBar (int state)
2931 {
2932     XtSetSensitive(buttonBarWidget, state);
2933 }
2934
2935
2936 void
2937 SetMenuEnables (Enables *enab)
2938 {
2939   while (enab->name != NULL) {
2940     EnableMenuItem(enab->name, enab->value);
2941     enab++;
2942   }
2943 }
2944
2945 int
2946 Equal(char *p, char *s)
2947 {   // compare strings skipping spaces in second
2948     while(*s) {
2949         if(*s == ' ') { s++; continue; }
2950         if(*s++ != *p++) return 0;
2951     }
2952     return !*p;
2953 }
2954
2955 void
2956 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2957 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
2958     int i;
2959     if(*nprms == 0) return;
2960     for(i=0; menuItemList[i].name; i++) {
2961         if(Equal(prms[0], menuItemList[i].name)) {
2962             (menuItemList[i].proc) ();
2963             return;
2964         }
2965     }
2966 }
2967
2968 static void
2969 MenuBarSelect (Widget w, caddr_t addr, caddr_t index)
2970 {
2971     MenuProc *proc = (MenuProc *) addr;
2972
2973     (proc)();
2974 }
2975
2976 static void
2977 MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
2978 {
2979     RecentEngineEvent((int) (intptr_t) addr);
2980 }
2981
2982 // some stuff that must remain in front-end
2983 static Widget mainBar, currentMenu;
2984 static int wtot, nr = 0, widths[10];
2985
2986 void
2987 AppendMenuItem (char *text, char *name, MenuProc *action)
2988 {
2989     int j;
2990     Widget entry;
2991     Arg args[16];
2992
2993     j = 0;
2994     XtSetArg(args[j], XtNleftMargin, 20);   j++;
2995     XtSetArg(args[j], XtNrightMargin, 20);  j++;
2996
2997         if (strcmp(text, "----") == 0) {
2998           entry = XtCreateManagedWidget(text, smeLineObjectClass,
2999                                           currentMenu, args, j);
3000         } else {
3001           XtSetArg(args[j], XtNlabel, XtNewString(_(text)));
3002             entry = XtCreateManagedWidget(name, smeBSBObjectClass,
3003                                           currentMenu, args, j+1);
3004             XtAddCallback(entry, XtNcallback,
3005                           (XtCallbackProc) (strcmp(name, "recent") ? MenuBarSelect : MenuEngineSelect),
3006                           (caddr_t) action);
3007             menuWidget[nrOfMenuItems] = entry;
3008         }
3009 }
3010
3011 void
3012 CreateMenuButton (char *name, Menu *mb)
3013 {   // create menu button on main bar, and shell for pull-down list
3014     int i, j;
3015     Arg args[16];
3016     Dimension w;
3017
3018         j = 0;
3019         XtSetArg(args[j], XtNmenuName, XtNewString(name));  j++;
3020         XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name)));  j++;
3021         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3022         mb->subMenu = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3023                                        mainBar, args, j);
3024     currentMenu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3025                               mainBar, NULL, 0);
3026         j = 0;
3027         XtSetArg(args[j], XtNwidth, &w);                   j++;
3028         XtGetValues(mb->subMenu, args, j);
3029         wtot += mb->textWidth = widths[nr++] = w;
3030 }
3031
3032 Widget
3033 CreateMenuBar (Menu *mb, int boardWidth)
3034 {
3035     int i, j;
3036     Arg args[16];
3037     char menuName[MSG_SIZ];
3038     Dimension w;
3039     Menu *ma = mb;
3040
3041     // create bar itself
3042     j = 0;
3043     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3044     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3045     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3046     mainBar = XtCreateWidget("menuBar", boxWidgetClass,
3047                              formWidget, args, j);
3048
3049     CreateMainMenus(mb); // put menus in bar according to description in back-end
3050
3051     // size buttons to make menu bar fit, clipping menu names where necessary
3052     while(wtot > boardWidth - 40) {
3053         int wmax=0, imax=0;
3054         for(i=0; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
3055         widths[imax]--;
3056         wtot--;
3057     }
3058     for(i=0; i<nr; i++) if(widths[i] != ma[i].textWidth) {
3059         j = 0;
3060         XtSetArg(args[j], XtNwidth, widths[i]);                   j++;
3061         XtSetValues(ma[i].subMenu, args, j);
3062     }
3063
3064     return mainBar;
3065 }
3066
3067 Widget
3068 CreateButtonBar (MenuItem *mi)
3069 {
3070     int j;
3071     Widget button, buttonBar;
3072     Arg args[16];
3073
3074     j = 0;
3075     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3076     if (tinyLayout) {
3077         XtSetArg(args[j], XtNhSpace, 0); j++;
3078     }
3079     XtSetArg(args[j], XtNborderWidth, 0); j++;
3080     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3081     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3082                                formWidget, args, j);
3083
3084     while (mi->string != NULL) {
3085         j = 0;
3086         if (tinyLayout) {
3087             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3088             XtSetArg(args[j], XtNborderWidth, 0); j++;
3089         }
3090       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3091         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3092                                        buttonBar, args, j);
3093         XtAddCallback(button, XtNcallback,
3094                       (XtCallbackProc) MenuBarSelect,
3095                       (caddr_t) mi->proc);
3096         mi++;
3097     }
3098     return buttonBar;
3099 }
3100
3101 Widget
3102 CreatePieceMenu (char *name, int color)
3103 {
3104     int i;
3105     Widget entry, menu;
3106     Arg args[16];
3107     ChessSquare selection;
3108
3109     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3110                               boardWidget, args, 0);
3111
3112     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3113         String item = pieceMenuStrings[color][i];
3114
3115         if (strcmp(item, "----") == 0) {
3116             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3117                                           menu, NULL, 0);
3118         } else {
3119           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3120             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3121                                 menu, args, 1);
3122             selection = pieceMenuTranslation[color][i];
3123             XtAddCallback(entry, XtNcallback,
3124                           (XtCallbackProc) PieceMenuSelect,
3125                           (caddr_t) selection);
3126             if (selection == WhitePawn || selection == BlackPawn) {
3127                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3128                 XtSetValues(menu, args, 1);
3129             }
3130         }
3131     }
3132     return menu;
3133 }
3134
3135 void
3136 CreatePieceMenus ()
3137 {
3138     int i;
3139     Widget entry;
3140     Arg args[16];
3141     ChessSquare selection;
3142
3143     whitePieceMenu = CreatePieceMenu("menuW", 0);
3144     blackPieceMenu = CreatePieceMenu("menuB", 1);
3145
3146     if(appData.pieceMenu) // [HGM] sweep: no idea what this was good for, but it stopped reporting button events outside the window
3147     XtRegisterGrabAction(PieceMenuPopup, True,
3148                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3149                          GrabModeAsync, GrabModeAsync);
3150
3151     XtSetArg(args[0], XtNlabel, _("Drop"));
3152     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3153                                   boardWidget, args, 1);
3154     for (i = 0; i < DROP_MENU_SIZE; i++) {
3155         String item = dropMenuStrings[i];
3156
3157         if (strcmp(item, "----") == 0) {
3158             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3159                                           dropMenu, NULL, 0);
3160         } else {
3161           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3162             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3163                                 dropMenu, args, 1);
3164             selection = dropMenuTranslation[i];
3165             XtAddCallback(entry, XtNcallback,
3166                           (XtCallbackProc) DropMenuSelect,
3167                           (caddr_t) selection);
3168         }
3169     }
3170 }
3171
3172 void
3173 SetupDropMenu ()
3174 {
3175     int i, j, count;
3176     char label[32];
3177     Arg args[16];
3178     Widget entry;
3179     char* p;
3180
3181     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3182         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3183         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3184                    dmEnables[i].piece);
3185         XtSetSensitive(entry, p != NULL || !appData.testLegality
3186                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3187                                        && !appData.icsActive));
3188         count = 0;
3189         while (p && *p++ == dmEnables[i].piece) count++;
3190         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3191         j = 0;
3192         XtSetArg(args[j], XtNlabel, label); j++;
3193         XtSetValues(entry, args, j);
3194     }
3195 }
3196
3197 void
3198 PieceMenuPopup (Widget w, XEvent *event, String *params, Cardinal *num_params)
3199 {
3200     String whichMenu; int menuNr = -2;
3201     shiftKey = strcmp(params[0], "menuW"); // used to indicate black
3202     if (event->type == ButtonRelease)
3203         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3204     else if (event->type == ButtonPress)
3205         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3206     switch(menuNr) {
3207       case 0: whichMenu = params[0]; break;
3208       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3209       case 2:
3210       case -1: if (errorUp) ErrorPopDown();
3211       default: return;
3212     }
3213     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3214 }
3215
3216 static void
3217 PieceMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3218 {
3219     if (pmFromX < 0 || pmFromY < 0) return;
3220     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3221 }
3222
3223 static void
3224 DropMenuSelect (Widget w, ChessSquare piece, caddr_t junk)
3225 {
3226     if (pmFromX < 0 || pmFromY < 0) return;
3227     DropMenuEvent(piece, pmFromX, pmFromY);
3228 }
3229
3230 void
3231 WhiteClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3232 {
3233     shiftKey = prms[0][0] & 1;
3234     ClockClick(0);
3235 }
3236
3237 void
3238 BlackClock (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3239 {
3240     shiftKey = prms[0][0] & 1;
3241     ClockClick(1);
3242 }
3243
3244
3245 static void
3246 do_flash_delay (unsigned long msec)
3247 {
3248     TimeDelay(msec);
3249 }
3250
3251 void
3252 DrawBorder (int x, int y, int type)
3253 {
3254     GC gc = lineGC;
3255
3256     if(type == 1) gc = highlineGC; else if(type == 2) gc = prelineGC;
3257
3258     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3259                    squareSize+lineGap, squareSize+lineGap);
3260 }
3261
3262 static int
3263 CutOutSquare (int x, int y, int *x0, int *y0, int  kind)
3264 {
3265     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
3266     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
3267     *x0 = 0; *y0 = 0;
3268     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
3269     if(textureW[kind] < W*squareSize)
3270         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
3271     else
3272         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
3273     if(textureH[kind] < H*squareSize)
3274         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
3275     else
3276         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
3277     return 1;
3278 }
3279
3280 static void
3281 BlankSquare (int x, int y, int color, ChessSquare piece, Drawable dest, int fac)
3282 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
3283     int x0, y0;
3284     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
3285         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
3286                   squareSize, squareSize, x*fac, y*fac);
3287     } else
3288     if (useImages && useImageSqs) {
3289         Pixmap pm;
3290         switch (color) {
3291           case 1: /* light */
3292             pm = xpmLightSquare;
3293             break;
3294           case 0: /* dark */
3295             pm = xpmDarkSquare;
3296             break;
3297           case 2: /* neutral */
3298           default:
3299             pm = xpmJailSquare; // [HGM] this is wrong, but apparently never used?
3300             break;
3301         }
3302         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3303                   squareSize, squareSize, x*fac, y*fac);
3304     } else {
3305         GC gc;
3306         switch (color) {
3307           case 1: /* light */
3308             gc = lightSquareGC;
3309             break;
3310           case 0: /* dark */
3311             gc = darkSquareGC;
3312             break;
3313           case 2: /* neutral */
3314           default:
3315             gc = lineGC;
3316             break;
3317         }
3318         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
3319     }
3320 }
3321
3322 /*
3323    I split out the routines to draw a piece so that I could
3324    make a generic flash routine.
3325 */
3326 static void
3327 monoDrawPiece_1bit (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3328 {
3329     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3330     switch (square_color) {
3331       case 1: /* light */
3332       case 2: /* neutral */
3333       default:
3334         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3335                   ? *pieceToOutline(piece)
3336                   : *pieceToSolid(piece),
3337                   dest, bwPieceGC, 0, 0,
3338                   squareSize, squareSize, x, y);
3339         break;
3340       case 0: /* dark */
3341         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3342                   ? *pieceToSolid(piece)
3343                   : *pieceToOutline(piece),
3344                   dest, wbPieceGC, 0, 0,
3345                   squareSize, squareSize, x, y);
3346         break;
3347     }
3348 }
3349
3350 static void
3351 monoDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3352 {
3353     switch (square_color) {
3354       case 1: /* light */
3355       case 2: /* neutral */
3356       default:
3357         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3358                    ? *pieceToOutline(piece)
3359                    : *pieceToSolid(piece),
3360                    dest, bwPieceGC, 0, 0,
3361                    squareSize, squareSize, x, y, 1);
3362         break;
3363       case 0: /* dark */
3364         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3365                    ? *pieceToSolid(piece)
3366                    : *pieceToOutline(piece),
3367                    dest, wbPieceGC, 0, 0,
3368                    squareSize, squareSize, x, y, 1);
3369         break;
3370     }
3371 }
3372
3373 static void
3374 colorDrawPiece (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3375 {
3376     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
3377     switch (square_color) {
3378       case 1: /* light */
3379         XCopyPlane(xDisplay, *pieceToSolid(piece),
3380                    dest, (int) piece < (int) BlackPawn
3381                    ? wlPieceGC : blPieceGC, 0, 0,
3382                    squareSize, squareSize, x, y, 1);
3383         break;
3384       case 0: /* dark */
3385         XCopyPlane(xDisplay, *pieceToSolid(piece),
3386                    dest, (int) piece < (int) BlackPawn
3387                    ? wdPieceGC : bdPieceGC, 0, 0,
3388                    squareSize, squareSize, x, y, 1);
3389         break;
3390       case 2: /* neutral */
3391       default:
3392         break; // should never contain pieces
3393     }
3394 }
3395
3396 static void
3397 colorDrawPieceImage (ChessSquare piece, int square_color, int x, int y, Drawable dest)
3398 {
3399     int kind, p = piece;
3400
3401     switch (square_color) {
3402       case 1: /* light */
3403       case 2: /* neutral */
3404       default:
3405         if ((int)piece < (int) BlackPawn) {
3406             kind = 0;
3407         } else {
3408             kind = 2;
3409             piece -= BlackPawn;
3410         }
3411         break;
3412       case 0: /* dark */
3413         if ((int)piece < (int) BlackPawn) {
3414             kind = 1;
3415         } else {
3416             kind = 3;
3417             piece -= BlackPawn;
3418         }
3419         break;
3420     }
3421     if(appData.upsideDown && flipView) { kind ^= 2; p += p < BlackPawn ? BlackPawn : -BlackPawn; }// swap white and black pieces
3422     if(useTexture & square_color+1) {
3423         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
3424         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
3425         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
3426         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
3427         XSetClipMask(xDisplay, wlPieceGC, None);
3428         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
3429     } else
3430     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3431               dest, wlPieceGC, 0, 0,
3432               squareSize, squareSize, x, y);
3433 }
3434
3435 typedef void (*DrawFunc)();
3436
3437 DrawFunc
3438 ChooseDrawFunc ()
3439 {
3440     if (appData.monoMode) {
3441         if (DefaultDepth(xDisplay, xScreen) == 1) {
3442             return monoDrawPiece_1bit;
3443         } else {
3444             return monoDrawPiece;
3445         }
3446     } else {
3447         if (useImages)
3448           return colorDrawPieceImage;
3449         else
3450           return colorDrawPiece;
3451     }
3452 }
3453
3454 void
3455 DrawDot (int marker, int x, int y, int r)
3456 {
3457         if(appData.monoMode) {
3458             XFillArc(xDisplay, xBoardWindow, marker == 2 ? darkSquareGC : lightSquareGC,
3459                     x, y, r, r, 0, 64*360);
3460             XDrawArc(xDisplay, xBoardWindow, marker == 2 ? lightSquareGC : darkSquareGC,
3461                     x, y, r, r, 0, 64*360);
3462         } else
3463         XFillArc(xDisplay, xBoardWindow, marker == 2 ? prelineGC : highlineGC,
3464                     x, y, r, r, 0, 64*360);
3465 }
3466
3467 void
3468 DrawOneSquare (int x, int y, ChessSquare piece, int square_color, int marker, char *string, int align)
3469 {   // basic front-end board-draw function: takes care of everything that can be in square:
3470     // piece, background, coordinate/count, marker dot
3471     int direction, font_ascent, font_descent;
3472     XCharStruct overall;
3473     DrawFunc drawfunc;
3474
3475     if (piece == EmptySquare) {
3476         BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
3477     } else {
3478         drawfunc = ChooseDrawFunc();
3479         drawfunc(piece, square_color, x, y, xBoardWindow);
3480     }
3481
3482     if(align) { // square carries inscription (coord or piece count)
3483         int xx = x, yy = y;
3484         GC hGC = align < 3 ? coordGC : countGC;
3485         // first calculate where it goes
3486         XTextExtents(countFontStruct, string, 1, &direction,
3487                          &font_ascent, &font_descent, &overall);
3488         if (align == 1) {
3489             xx += squareSize - overall.width - 2;
3490             yy += squareSize - font_descent - 1;
3491         } else if (align == 2) {
3492             xx += 2, yy += font_ascent + 1;
3493         } else if (align == 3) {
3494             xx += squareSize - overall.width - 2;
3495             yy += font_ascent + 1;
3496         } else if (align == 4) {
3497             xx += 2, yy += font_ascent + 1;
3498         }
3499         // then draw it
3500         if (appData.monoMode) {
3501             XDrawImageString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3502         } else {
3503             XDrawString(xDisplay, xBoardWindow, hGC, xx, yy, string, 1);
3504         }
3505     }
3506
3507     if(marker) { // print fat marker dot, if requested
3508         DrawDot(marker, x + squareSize/4, y+squareSize/4, squareSize/2);
3509     }
3510 }
3511
3512 void
3513 FlashDelay (int flash_delay)
3514 {
3515         XSync(xDisplay, False);
3516         if(flash_delay) do_flash_delay(flash_delay);
3517 }
3518
3519 double
3520 Fraction (int x, int start, int stop)
3521 {
3522    double f = ((double) x - start)/(stop - start);
3523    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
3524    return f;
3525 }
3526
3527 static WindowPlacement wpNew;
3528
3529 void
3530 CoDrag (Widget sh, WindowPlacement *wp)
3531 {
3532     Arg args[16];
3533     int j=0, touch=0, fudge = 2;
3534     GetActualPlacement(sh, wp);
3535     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
3536     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
3537     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
3538     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
3539     if(!touch ) return; // only windows that touch co-move
3540     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
3541         int heightInc = wpNew.height - wpMain.height;
3542         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3543         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
3544         wp->y += fracTop * heightInc;
3545         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
3546         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
3547     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
3548         int widthInc = wpNew.width - wpMain.width;
3549         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3550         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
3551         wp->y += fracLeft * widthInc;
3552         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
3553         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
3554     }
3555     wp->x += wpNew.x - wpMain.x;
3556     wp->y += wpNew.y - wpMain.y;
3557     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
3558     if(touch == 3) wp->y += wpNew.height - wpMain.height;
3559     XtSetArg(args[j], XtNx, wp->x); j++;
3560     XtSetArg(args[j], XtNy, wp->y); j++;
3561     XtSetValues(sh, args, j);
3562 }
3563
3564 static XtIntervalId delayedDragID = 0;
3565
3566 void
3567 DragProc ()
3568 {
3569         GetActualPlacement(shellWidget, &wpNew);
3570         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
3571            wpNew.width == wpMain.width && wpNew.height == wpMain.height) // not sized
3572             return; // false alarm
3573         if(EngineOutputIsUp()) CoDrag(engineOutputShell, &wpEngineOutput);
3574         if(MoveHistoryIsUp()) CoDrag(shells[HistoryDlg], &wpMoveHistory);
3575         if(EvalGraphIsUp()) CoDrag(evalGraphShell, &wpEvalGraph);
3576         if(GameListIsUp()) CoDrag(gameListShell, &wpGameList);
3577         wpMain = wpNew;
3578         DrawPosition(True, NULL);
3579         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
3580 }
3581
3582
3583 void
3584 DelayedDrag ()
3585 {
3586     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
3587     delayedDragID =
3588       XtAppAddTimeOut(appContext, 50, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
3589 }
3590
3591 /* Why is this needed on some versions of X? */
3592 void
3593 EventProc (Widget widget, caddr_t unused, XEvent *event)
3594 {
3595     if (!XtIsRealized(widget))
3596       return;
3597     switch (event->type) {
3598       case ConfigureNotify: // main window is being dragged: drag attached windows with it
3599         if(appData.useStickyWindows)
3600             DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
3601         break;
3602       case Expose:
3603         if (event->xexpose.count > 0) return;  /* no clipping is done */
3604         DrawPosition(True, NULL);
3605         if(twoBoards) { // [HGM] dual: draw other board in other orientation
3606             flipView = !flipView; partnerUp = !partnerUp;
3607             DrawPosition(True, NULL);
3608             flipView = !flipView; partnerUp = !partnerUp;
3609         }
3610         break;
3611       case MotionNotify:
3612         if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
3613       default:
3614         return;
3615     }
3616 }
3617 /* end why */
3618
3619 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
3620 void
3621 DrawSeekAxis (int x, int y, int xTo, int yTo)
3622 {
3623       XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3624 }
3625
3626 void
3627 DrawSeekBackground (int left, int top, int right, int bottom)
3628 {
3629     XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3630 }
3631
3632 void
3633 DrawSeekText (char *buf, int x, int y)
3634 {
3635     XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3636 }
3637
3638 void
3639 DrawSeekDot (int x, int y, int colorNr)
3640 {
3641     int square = colorNr & 0x80;
3642     GC color;
3643     colorNr &= 0x7F;
3644     color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3645     if(square)
3646         XFillRectangle(xDisplay, xBoardWindow, color,
3647                 x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3648     else
3649         XFillArc(xDisplay, xBoardWindow, color,
3650                 x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3651 }
3652
3653 void
3654 DrawGrid (int second)
3655 {
3656           XDrawSegments(xDisplay, xBoardWindow, lineGC,
3657                         second ? secondSegments : // [HGM] dual
3658                         gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
3659 }
3660
3661
3662 /*
3663  * event handler for redrawing the board
3664  */
3665 void
3666 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3667 {
3668     DrawPosition(True, NULL);
3669 }
3670
3671
3672 /*
3673  * event handler for parsing user moves
3674  */
3675 // [HGM] This routine will need quite some reworking. Although the backend still supports the old
3676 //       way of doing things, by calling UserMoveEvent() to test the legality of the move and then perform
3677 //       it at the end, and doing all kind of preliminary tests here (e.g. to weed out self-captures), it
3678 //       should be made to use the new way, of calling UserMoveTest early  to determine the legality of the
3679 //       move, (which will weed out the illegal selfcaptures and moves into the holdings, and flag promotions),
3680 //       and at the end FinishMove() to perform the move after optional promotion popups.
3681 //       For now I patched it to allow self-capture with King, and suppress clicks between board and holdings.
3682 void
3683 HandleUserMove (Widget w, XEvent *event, String *prms, Cardinal *nprms)
3684 {
3685     if (w != boardWidget || errorExitStatus != -1) return;
3686     if(nprms) shiftKey = !strcmp(prms[0], "1");
3687
3688     if (promotionUp) {
3689         if (event->type == ButtonPress) {
3690             XtPopdown(promotionShell);
3691             XtDestroyWidget(promotionShell);
3692             promotionUp = False;
3693             ClearHighlights();
3694             fromX = fromY = -1;
3695         } else {
3696             return;
3697         }
3698     }
3699
3700     // [HGM] mouse: the rest of the mouse handler is moved to the backend, and called here
3701     if(event->type == ButtonPress)   LeftClick(Press,   event->xbutton.x, event->xbutton.y);
3702     if(event->type == ButtonRelease) LeftClick(Release, event->xbutton.x, event->xbutton.y);
3703 }
3704
3705 void
3706 AnimateUserMove (Widget w, XEvent *event, String *params, Cardinal *nParams)
3707 {
3708     if(!PromoScroll(event->xmotion.x, event->xmotion.y))
3709     DragPieceMove(event->xmotion.x, event->xmotion.y);
3710 }
3711
3712 void
3713 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
3714 {   // [HGM] pv: walk PV
3715     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3716 }
3717
3718 static int savedIndex;  /* gross that this is global */
3719
3720 void
3721 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
3722 {
3723         String val;
3724         XawTextPosition index, dummy;
3725         Arg arg;
3726
3727         XawTextGetSelectionPos(w, &index, &dummy);
3728         XtSetArg(arg, XtNstring, &val);
3729         XtGetValues(w, &arg, 1);
3730         ReplaceComment(savedIndex, val);
3731         if(savedIndex != currentMove) ToNrEvent(savedIndex);
3732         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
3733 }
3734
3735 void
3736 EditCommentPopUp (int index, char *title, char *text)
3737 {
3738     savedIndex = index;
3739     if (text == NULL) text = "";
3740     NewCommentPopup(title, text, index);
3741 }
3742
3743 void
3744 ICSInputBoxPopUp ()
3745 {
3746     InputBoxPopup();
3747 }
3748
3749
3750 void
3751 ICSInputBoxPopDown ()
3752 {
3753     PopDown(InputBoxDlg);
3754 }
3755
3756 void
3757 CommentPopUp (char *title, char *text)
3758 {
3759     savedIndex = currentMove; // [HGM] vari
3760     NewCommentPopup(title, text, currentMove);
3761 }
3762
3763 void
3764 CommentPopDown ()
3765 {
3766     PopDown(CommentDlg);
3767 }
3768
3769 static char *openName;
3770 FILE *openFP;
3771
3772 void
3773 DelayedLoad ()
3774 {
3775   (void) (*fileProc)(openFP, 0, openName);
3776 }
3777
3778 void
3779 FileNamePopUp (char *label, char *def, char *filter, FileProc proc, char *openMode)
3780 {
3781     fileProc = proc;            /* I can't see a way not */
3782     fileOpenMode = openMode;    /*   to use globals here */
3783     {   // [HGM] use file-selector dialog stolen from Ghostview
3784         int index; // this is not supported yet
3785         if(openFP = XsraSelFile(shellWidget, label, NULL, NULL, _("could not open: "),
3786                            (def[0] ? def : NULL), filter, openMode, NULL, &openName))
3787           // [HGM] delay to give expose event opportunity to redraw board after browser-dialog popdown before lengthy load starts
3788           ScheduleDelayedEvent(&DelayedLoad, 50);
3789     }
3790 }
3791
3792 void
3793 PromotionPopUp ()
3794 {
3795     Arg args[16];
3796     Widget dialog, layout;
3797     Position x, y;
3798     Dimension bw_width, pw_width;
3799     int j;
3800     char *PromoChars = "wglcqrbnkac+=\0";
3801
3802     j = 0;
3803     XtSetArg(args[j], XtNwidth, &bw_width); j++;
3804     XtGetValues(boardWidget, args, j);
3805
3806     j = 0;
3807     XtSetArg(args[j], XtNresizable, True); j++;
3808     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3809     promotionShell =
3810       XtCreatePopupShell("Promotion", transientShellWidgetClass,
3811                          shellWidget, args, j);
3812     layout =
3813       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3814                             layoutArgs, XtNumber(layoutArgs));
3815
3816     j = 0;
3817     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3818     XtSetArg(args[j], XtNborderWidth, 0); j++;
3819     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3820                                    layout, args, j);
3821
3822   if(gameInfo.variant != VariantShogi) {
3823    if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
3824       XawDialogAddButton(dialog, _("Warlord"), PromotionCallback, PromoChars + 0);
3825       XawDialogAddButton(dialog, _("General"), PromotionCallback, PromoChars + 1);
3826       XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback, PromoChars + 2);
3827       XawDialogAddButton(dialog, _("Captain"), PromotionCallback, PromoChars + 3);
3828     } else {
3829     XawDialogAddButton(dialog, _("Queen"), PromotionCallback, PromoChars + 4);
3830     XawDialogAddButton(dialog, _("Rook"), PromotionCallback, PromoChars + 5);
3831     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback, PromoChars + 6);
3832     XawDialogAddButton(dialog, _("Knight"), PromotionCallback, PromoChars + 7);
3833     }
3834     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3835         gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||
3836         gameInfo.variant == VariantGiveaway) {
3837       XawDialogAddButton(dialog, _("King"), PromotionCallback, PromoChars + 8);
3838     }
3839     if(gameInfo.variant == VariantCapablanca ||
3840        gameInfo.variant == VariantGothic ||
3841        gameInfo.variant == VariantCapaRandom) {
3842       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback, PromoChars + 9);
3843       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback, PromoChars + 10);
3844     }
3845   } else // [HGM] shogi
3846   {
3847       XawDialogAddButton(dialog, _("Promote"), PromotionCallback, PromoChars + 11);
3848       XawDialogAddButton(dialog, _("Defer"), PromotionCallback, PromoChars + 12);
3849   }
3850     XawDialogAddButton(dialog, _("cancel"), PromotionCallback, PromoChars + 13);
3851
3852     XtRealizeWidget(promotionShell);
3853     CatchDeleteWindow(promotionShell, "PromotionPopDown");
3854
3855     j = 0;
3856     XtSetArg(args[j], XtNwidth, &pw_width); j++;
3857     XtGetValues(promotionShell, args, j);
3858
3859     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3860                       lineGap + squareSize/3 +
3861                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3862                        0 : 6*(squareSize + lineGap)), &x, &y);
3863
3864     j = 0;
3865     XtSetArg(args[j], XtNx, x); j++;
3866     XtSetArg(args[j], XtNy, y); j++;
3867     XtSetValues(promotionShell, args, j);
3868
3869     XtPopup(promotionShell, XtGrabNone);
3870
3871     promotionUp = True;
3872 }
3873
3874 void
3875 PromotionPopDown ()
3876 {
3877     if (!promotionUp) return;
3878     XtPopdown(promotionShell);
3879     XtDestroyWidget(promotionShell);
3880     promotionUp = False;
3881 }
3882
3883 void
3884 PromotionCallback (Widget w, XtPointer client_data, XtPointer call_data)
3885 {
3886     int promoChar = * (const char *) client_data;
3887
3888     PromotionPopDown();
3889
3890     if (fromX == -1) return;
3891
3892     if (! promoChar) {
3893         fromX = fromY = -1;
3894         ClearHighlights();
3895         return;
3896     }
3897     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3898
3899     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3900     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3901     fromX = fromY = -1;
3902 }
3903
3904
3905 void
3906 ErrorCallback (Widget w, XtPointer client_data, XtPointer call_data)
3907 {
3908     dialogError = errorUp = False;
3909     XtPopdown(w = XtParent(XtParent(XtParent(w))));
3910     XtDestroyWidget(w);
3911     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3912 }
3913
3914
3915 void
3916 ErrorPopDown ()
3917 {
3918     if (!errorUp) return;
3919     dialogError = errorUp = False;
3920     XtPopdown(errorShell);
3921     XtDestroyWidget(errorShell);
3922     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3923 }
3924
3925 void
3926 ErrorPopUp (char *title, char *label, int modal)
3927 {
3928     Arg args[16];
3929     Widget dialog, layout;
3930     Position x, y;
3931     int xx, yy;
3932     Window junk;
3933     Dimension bw_width, pw_width;
3934     Dimension pw_height;
3935     int i;
3936
3937     i = 0;
3938     XtSetArg(args[i], XtNresizable, True);  i++;
3939     XtSetArg(args[i], XtNtitle, title); i++;
3940     errorShell =
3941       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
3942                          shellUp[TransientDlg] ? (dialogError = modal = TRUE, shells[TransientDlg]) : shellWidget, args, i);
3943     layout =
3944       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
3945                             layoutArgs, XtNumber(layoutArgs));
3946
3947     i = 0;
3948     XtSetArg(args[i], XtNlabel, label); i++;
3949     XtSetArg(args[i], XtNborderWidth, 0); i++;
3950     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
3951                                    layout, args, i);
3952
3953     XawDialogAddButton(dialog, _("ok"), ErrorCallback, (XtPointer) dialog);
3954
3955     XtRealizeWidget(errorShell);
3956     CatchDeleteWindow(errorShell, "ErrorPopDown");
3957
3958     i = 0;
3959     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
3960     XtGetValues(boardWidget, args, i);
3961     i = 0;
3962     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
3963     XtSetArg(args[i], XtNheight, &pw_height);  i++;
3964     XtGetValues(errorShell, args, i);
3965
3966 #ifdef NOTDEF
3967     /* This code seems to tickle an X bug if it is executed too soon
3968        after xboard starts up.  The coordinates get transformed as if
3969        the main window was positioned at (0, 0).
3970        */
3971     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3972                       0 - pw_height + squareSize / 3, &x, &y);
3973 #else
3974     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
3975                           RootWindowOfScreen(XtScreen(boardWidget)),
3976                           (bw_width - pw_width) / 2,
3977                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
3978     x = xx;
3979     y = yy;
3980 #endif
3981     if (y < 0) y = 0; /*avoid positioning top offscreen*/
3982
3983     i = 0;
3984     XtSetArg(args[i], XtNx, x);  i++;
3985     XtSetArg(args[i], XtNy, y);  i++;
3986     XtSetValues(errorShell, args, i);
3987
3988     errorUp = True;
3989     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
3990 }
3991
3992 /* Disable all user input other than deleting the window */
3993 static int frozen = 0;
3994
3995 void
3996 FreezeUI ()
3997 {
3998   if (frozen) return;
3999   /* Grab by a widget that doesn't accept input */
4000   XtAddGrab(messageWidget, TRUE, FALSE);
4001   frozen = 1;
4002 }
4003
4004 /* Undo a FreezeUI */
4005 void
4006 ThawUI ()
4007 {
4008   if (!frozen) return;
4009   XtRemoveGrab(messageWidget);
4010   frozen = 0;
4011 }
4012
4013 void
4014 ModeHighlight ()
4015 {
4016     Arg args[16];
4017     static int oldPausing = FALSE;
4018     static GameMode oldmode = (GameMode) -1;
4019     char *wname;
4020
4021     if (!boardWidget || !XtIsRealized(boardWidget)) return;
4022
4023     if (pausing != oldPausing) {
4024         oldPausing = pausing;
4025         MarkMenuItem("Pause", pausing);
4026
4027         if (appData.showButtonBar) {
4028           /* Always toggle, don't set.  Previous code messes up when
4029              invoked while the button is pressed, as releasing it
4030              toggles the state again. */
4031           {
4032             Pixel oldbg, oldfg;
4033             XtSetArg(args[0], XtNbackground, &oldbg);
4034             XtSetArg(args[1], XtNforeground, &oldfg);
4035             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
4036                         args, 2);
4037             XtSetArg(args[0], XtNbackground, oldfg);
4038             XtSetArg(args[1], XtNforeground, oldbg);
4039           }
4040           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
4041         }
4042     }
4043
4044     wname = ModeToWidgetName(oldmode);
4045     if (wname != NULL) {
4046         MarkMenuItem(wname, False);
4047     }
4048     wname = ModeToWidgetName(gameMode);
4049     if (wname != NULL) {
4050         MarkMenuItem(wname, True);
4051     }
4052     oldmode = gameMode;
4053     MarkMenuItem("Machine Match", matchMode && matchGame < appData.matchGames);
4054
4055     /* Maybe all the enables should be handled here, not just this one */
4056     EnableMenuItem("Training", gameMode == Training || gameMode == PlayFromGameFile);
4057 }
4058
4059
4060 /*
4061  * Button/menu procedures
4062  */
4063 int
4064 LoadGamePopUp (FILE *f, int gameNumber, char *title)
4065 {
4066     cmailMsgLoaded = FALSE;
4067     if (gameNumber == 0) {
4068         int error = GameListBuild(f);
4069         if (error) {
4070             DisplayError(_("Cannot build game list"), error);
4071         } else if (!ListEmpty(&gameList) &&
4072                    ((ListGame *) gameList.tailPred)->number > 1) {
4073             GameListPopUp(f, title);
4074             return TRUE;
4075         }
4076         GameListDestroy();
4077         gameNumber = 1;
4078     }
4079     return LoadGame(f, gameNumber, title, FALSE);
4080 }
4081
4082 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4083 char *selected_fen_position=NULL;
4084
4085 Boolean
4086 SendPositionSelection (Widget w, Atom *selection, Atom *target,
4087                        Atom *type_return, XtPointer *value_return,
4088                        unsigned long *length_return, int *format_return)
4089 {
4090   char *selection_tmp;
4091
4092 //  if (!selected_fen_position) return False; /* should never happen */
4093   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4094    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
4095     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
4096     long len;
4097     size_t count;
4098     if (f == NULL) return False;
4099     fseek(f, 0, 2);
4100     len = ftell(f);
4101     rewind(f);
4102     selection_tmp = XtMalloc(len + 1);
4103     count = fread(selection_tmp, 1, len, f);
4104     fclose(f);
4105     if (len != count) {
4106       XtFree(selection_tmp);
4107       return False;
4108     }
4109     selection_tmp[len] = NULLCHAR;
4110    } else {
4111     /* note: since no XtSelectionDoneProc was registered, Xt will
4112      * automatically call XtFree on the value returned.  So have to
4113      * make a copy of it allocated with XtMalloc */
4114     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4115     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
4116    }
4117
4118     *value_return=selection_tmp;
4119     *length_return=strlen(selection_tmp);
4120     *type_return=*target;
4121     *format_return = 8; /* bits per byte */
4122     return True;
4123   } else if (*target == XA_TARGETS(xDisplay)) {
4124     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4125     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4126     targets_tmp[1] = XA_STRING;
4127     *value_return = targets_tmp;
4128     *type_return = XA_ATOM;
4129     *length_return = 2;
4130 #if 0
4131     // This code leads to a read of value_return out of bounds on 64-bit systems.
4132     // Other code which I have seen always sets *format_return to 32 independent of
4133     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
4134     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
4135     *format_return = 8 * sizeof(Atom);
4136     if (*format_return > 32) {
4137       *length_return *= *format_return / 32;
4138       *format_return = 32;
4139     }
4140 #else
4141     *format_return = 32;
4142 #endif
4143     return True;
4144   } else {
4145     return False;
4146   }
4147 }
4148
4149 /* note: when called from menu all parameters are NULL, so no clue what the
4150  * Widget which was clicked on was, or what the click event was
4151  */
4152 void
4153 CopySomething (char *src)
4154 {
4155     selected_fen_position = src;
4156     /*
4157      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4158      * have a notion of a position that is selected but not copied.
4159      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4160      */
4161     XtOwnSelection(menuBarWidget, XA_PRIMARY,
4162                    CurrentTime,
4163                    SendPositionSelection,
4164                    NULL/* lose_ownership_proc */ ,
4165                    NULL/* transfer_done_proc */);
4166     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4167                    CurrentTime,
4168                    SendPositionSelection,
4169                    NULL/* lose_ownership_proc */ ,
4170                    NULL/* transfer_done_proc */);
4171 }
4172
4173 /* function called when the data to Paste is ready */
4174 static void
4175 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
4176                  Atom *type, XtPointer value, unsigned long *len, int *format)
4177 {
4178   char *fenstr=value;
4179   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4180   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4181   EditPositionPasteFEN(fenstr);
4182   XtFree(value);
4183 }
4184
4185 /* called when Paste Position button is pressed,
4186  * all parameters will be NULL */
4187 void
4188 PastePositionProc ()
4189 {
4190     XtGetSelectionValue(menuBarWidget,
4191       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4192       /* (XtSelectionCallbackProc) */ PastePositionCB,
4193       NULL, /* client_data passed to PastePositionCB */
4194
4195       /* better to use the time field from the event that triggered the
4196        * call to this function, but that isn't trivial to get
4197        */
4198       CurrentTime
4199     );
4200     return;
4201 }
4202
4203 /* note: when called from menu all parameters are NULL, so no clue what the
4204  * Widget which was clicked on was, or what the click event was
4205  */
4206 /* function called when the data to Paste is ready */
4207 static void
4208 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
4209              Atom *type, XtPointer value, unsigned long *len, int *format)
4210 {
4211   FILE* f;
4212   if (value == NULL || *len == 0) {
4213     return; /* nothing had been selected to copy */
4214   }
4215   f = fopen(gamePasteFilename, "w");
4216   if (f == NULL) {
4217     DisplayError(_("Can't open temp file"), errno);
4218     return;
4219   }
4220   fwrite(value, 1, *len, f);
4221   fclose(f);
4222   XtFree(value);
4223   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4224 }
4225
4226 /* called when Paste Game button is pressed,
4227  * all parameters will be NULL */
4228 void
4229 PasteGameProc ()
4230 {
4231     XtGetSelectionValue(menuBarWidget,
4232       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4233       /* (XtSelectionCallbackProc) */ PasteGameCB,
4234       NULL, /* client_data passed to PasteGameCB */
4235
4236       /* better to use the time field from the event that triggered the
4237        * call to this function, but that isn't trivial to get
4238        */
4239       CurrentTime
4240     );
4241     return;
4242 }
4243
4244
4245 void
4246 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4247 {
4248     QuitProc();
4249 }
4250
4251 static void
4252 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
4253 {
4254     char buf[10], keys[32];
4255     KeySym sym;
4256     KeyCode metaL, metaR; //, ctrlL, ctrlR;
4257     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
4258     XQueryKeymap(xDisplay,keys);
4259     metaL = XKeysymToKeycode(xDisplay, XK_Meta_L);
4260     metaR = XKeysymToKeycode(xDisplay, XK_Meta_R);
4261 //    ctrlL = XKeysymToKeycode(xDisplay, XK_Control_L);
4262 //    ctrlR = XKeysymToKeycode(xDisplay, XK_Control_R);
4263     if ( n == 1 && *buf >= 32 // printable
4264          && !(keys[metaL>>3]&1<<(metaL&7)) && !(keys[metaR>>3]&1<<(metaR&7)) // no alt key pressed
4265 //       && !(keys[ctrlL>>3]&1<<(ctrlL&7)) && !(keys[ctrlR>>3]&1<<(ctrlR&7)) // no ctrl key pressed
4266        ) BoxAutoPopUp (buf);
4267 }
4268
4269 static void
4270 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4271 {   // [HGM] input: let up-arrow recall previous line from history
4272     IcsKey(1);
4273 }
4274
4275 static void
4276 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4277 {   // [HGM] input: let down-arrow recall next line from history
4278     IcsKey(-1);
4279 }
4280
4281 static void
4282 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4283 {
4284     IcsKey(0);
4285 }
4286
4287 void
4288 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4289 {
4290         if (!TempBackwardActive) {
4291                 TempBackwardActive = True;
4292                 BackwardEvent();
4293         }
4294 }
4295
4296 void
4297 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4298 {
4299         /* Check to see if triggered by a key release event for a repeating key.
4300          * If so the next queued event will be a key press of the same key at the same time */
4301         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
4302                 XEvent next;
4303                 XPeekEvent(xDisplay, &next);
4304                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
4305                         next.xkey.keycode == event->xkey.keycode)
4306                                 return;
4307         }
4308     ForwardEvent();
4309         TempBackwardActive = False;
4310 }
4311
4312 void
4313 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4314 {   // called as key binding
4315     char buf[MSG_SIZ];
4316     String name;
4317     if (nprms && *nprms > 0)
4318       name = prms[0];
4319     else
4320       name = "xboard";
4321     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
4322     system(buf);
4323 }
4324
4325 void
4326 DisplayMessage (char *message, char *extMessage)
4327 {
4328   /* display a message in the message widget */
4329
4330   char buf[MSG_SIZ];
4331   Arg arg;
4332
4333   if (extMessage)
4334     {
4335       if (*message)
4336         {
4337           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
4338           message = buf;
4339         }
4340       else
4341         {
4342           message = extMessage;
4343         };
4344     };
4345
4346     safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
4347
4348   /* need to test if messageWidget already exists, since this function
4349      can also be called during the startup, if for example a Xresource
4350      is not set up correctly */
4351   if(messageWidget)
4352     {
4353       XtSetArg(arg, XtNlabel, message);
4354       XtSetValues(messageWidget, &arg, 1);
4355     };
4356
4357   return;
4358 }
4359
4360 void
4361 SetWindowTitle (char *text, char *title, char *icon)
4362 {
4363     Arg args[16];
4364     int i;
4365     if (appData.titleInWindow) {
4366         i = 0;
4367         XtSetArg(args[i], XtNlabel, text);   i++;
4368         XtSetValues(titleWidget, args, i);
4369     }
4370     i = 0;
4371     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
4372     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
4373     XtSetValues(shellWidget, args, i);
4374     XSync(xDisplay, False);
4375 }
4376
4377
4378 static int
4379 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
4380 {
4381     return 0;
4382 }
4383
4384 void
4385 DisplayIcsInteractionTitle (String message)
4386 {
4387   if (oldICSInteractionTitle == NULL) {
4388     /* Magic to find the old window title, adapted from vim */
4389     char *wina = getenv("WINDOWID");
4390     if (wina != NULL) {
4391       Window win = (Window) atoi(wina);
4392       Window root, parent, *children;
4393       unsigned int nchildren;
4394       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4395       for (;;) {
4396         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4397         if (!XQueryTree(xDisplay, win, &root, &parent,
4398                         &children, &nchildren)) break;
4399         if (children) XFree((void *)children);
4400         if (parent == root || parent == 0) break;
4401         win = parent;
4402       }
4403       XSetErrorHandler(oldHandler);
4404     }
4405     if (oldICSInteractionTitle == NULL) {
4406       oldICSInteractionTitle = "xterm";
4407     }
4408   }
4409   printf("\033]0;%s\007", message);
4410   fflush(stdout);
4411 }
4412
4413 char pendingReplyPrefix[MSG_SIZ];
4414 ProcRef pendingReplyPR;
4415
4416 void
4417 AskQuestionPopDown ()
4418 {
4419     if (!askQuestionUp) return;
4420     XtPopdown(askQuestionShell);
4421     XtDestroyWidget(askQuestionShell);
4422     askQuestionUp = False;
4423 }
4424
4425 void
4426 AskQuestionReplyAction (Widget w, XEvent *event, String *prms, Cardinal *nprms)
4427 {
4428     char buf[MSG_SIZ];
4429     int err;
4430     String reply;
4431
4432     reply = XawDialogGetValueString(w = XtParent(w));
4433     safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
4434     if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
4435     strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
4436     strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
4437     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4438     AskQuestionPopDown();
4439
4440     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4441 }
4442
4443 void
4444 AskQuestionCallback (Widget w, XtPointer client_data, XtPointer call_data)
4445 {
4446     String name;
4447     Arg args[16];
4448
4449     XtSetArg(args[0], XtNlabel, &name);
4450     XtGetValues(w, args, 1);
4451
4452     if (strcmp(name, _("cancel")) == 0) {
4453         AskQuestionPopDown();
4454     } else {
4455         AskQuestionReplyAction(w, NULL, NULL, NULL);
4456     }
4457 }
4458
4459 void
4460 AskQuestion (char *title, char *question, char *replyPrefix, ProcRef pr)
4461 {
4462     Arg args[16];
4463     Widget popup, layout, dialog, edit;
4464     Window root, child;
4465     int x, y, i;
4466     int win_x, win_y;
4467     unsigned int mask;
4468
4469     safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
4470     pendingReplyPR = pr;
4471
4472     i = 0;
4473     XtSetArg(args[i], XtNresizable, True); i++;
4474     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4475     askQuestionShell = popup =
4476       XtCreatePopupShell(title, transientShellWidgetClass,
4477                          shellWidget, args, i);
4478
4479     layout =
4480       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4481                             layoutArgs, XtNumber(layoutArgs));
4482
4483     i = 0;
4484     XtSetArg(args[i], XtNlabel, question); i++;
4485     XtSetArg(args[i], XtNvalue, ""); i++;
4486     XtSetArg(args[i], XtNborderWidth, 0); i++;
4487     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4488                                    layout, args, i);
4489
4490     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4491                        (XtPointer) dialog);
4492     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4493                        (XtPointer) dialog);
4494
4495     XtRealizeWidget(popup);
4496     CatchDeleteWindow(popup, "AskQuestionPopDown");
4497
4498     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4499                   &x, &y, &win_x, &win_y, &mask);
4500
4501     XtSetArg(args[0], XtNx, x - 10);
4502     XtSetArg(args[1], XtNy, y - 30);
4503     XtSetValues(popup, args, 2);
4504
4505     XtPopup(popup, XtGrabExclusive);
4506     askQuestionUp = True;
4507
4508     edit = XtNameToWidget(dialog, "*value");
4509     XtSetKeyboardFocus(popup, edit);
4510 }
4511
4512
4513 void
4514 PlaySound (char *name)
4515 {
4516   if (*name == NULLCHAR) {
4517     return;
4518   } else if (strcmp(name, "$") == 0) {
4519     putc(BELLCHAR, stderr);
4520   } else {
4521     char buf[2048];
4522     char *prefix = "", *sep = "";
4523     if(appData.soundProgram[0] == NULLCHAR) return;
4524     if(!strchr(name, '/')) { prefix = appData.soundDirectory; sep = "/"; }
4525     snprintf(buf, sizeof(buf), "%s '%s%s%s' &", appData.soundProgram, prefix, sep, name);
4526     system(buf);
4527   }
4528 }
4529
4530 void
4531 RingBell ()
4532 {
4533   PlaySound(appData.soundMove);
4534 }
4535
4536 void
4537 PlayIcsWinSound ()
4538 {
4539   PlaySound(appData.soundIcsWin);
4540 }
4541
4542 void
4543 PlayIcsLossSound ()
4544 {
4545   PlaySound(appData.soundIcsLoss);
4546 }
4547
4548 void
4549 PlayIcsDrawSound ()
4550 {
4551   PlaySound(appData.soundIcsDraw);
4552 }
4553
4554 void
4555 PlayIcsUnfinishedSound ()
4556 {
4557   PlaySound(appData.soundIcsUnfinished);
4558 }
4559
4560 void
4561 PlayAlarmSound ()
4562 {
4563   PlaySound(appData.soundIcsAlarm);
4564 }
4565
4566 void
4567 PlayTellSound ()
4568 {
4569   PlaySound(appData.soundTell);
4570 }
4571
4572 void
4573 EchoOn ()
4574 {
4575     system("stty echo");
4576     noEcho = False;
4577 }
4578
4579 void
4580 EchoOff ()
4581 {
4582     system("stty -echo");
4583     noEcho = True;
4584 }
4585
4586 void
4587 RunCommand (char *buf)
4588 {
4589     system(buf);
4590 }
4591
4592 void
4593 Colorize (ColorClass cc, int continuation)
4594 {
4595     char buf[MSG_SIZ];
4596     int count, outCount, error;
4597
4598     if (textColors[(int)cc].bg > 0) {
4599         if (textColors[(int)cc].fg > 0) {
4600           snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4601                    textColors[(int)cc].fg, textColors[(int)cc].bg);
4602         } else {
4603           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4604                    textColors[(int)cc].bg);
4605         }
4606     } else {
4607         if (textColors[(int)cc].fg > 0) {
4608           snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
4609                     textColors[(int)cc].fg);
4610         } else {
4611           snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
4612         }
4613     }
4614     count = strlen(buf);
4615     outCount = OutputToProcess(NoProc, buf, count, &error);
4616     if (outCount < count) {
4617         DisplayFatalError(_("Error writing to display"), error, 1);
4618     }
4619
4620     if (continuation) return;
4621     switch (cc) {
4622     case ColorShout:
4623       PlaySound(appData.soundShout);
4624       break;
4625     case ColorSShout:
4626       PlaySound(appData.soundSShout);
4627       break;
4628     case ColorChannel1:
4629       PlaySound(appData.soundChannel1);
4630       break;
4631     case ColorChannel:
4632       PlaySound(appData.soundChannel);
4633       break;
4634     case ColorKibitz:
4635       PlaySound(appData.soundKibitz);
4636       break;
4637     case ColorTell:
4638       PlaySound(appData.soundTell);
4639       break;
4640     case ColorChallenge:
4641       PlaySound(appData.soundChallenge);
4642       break;
4643     case ColorRequest:
4644       PlaySound(appData.soundRequest);
4645       break;
4646     case ColorSeek:
4647       PlaySound(appData.soundSeek);
4648       break;
4649     case ColorNormal:
4650     case ColorNone:
4651     default:
4652       break;
4653     }
4654 }
4655
4656 char *
4657 UserName ()
4658 {
4659     return getpwuid(getuid())->pw_name;
4660 }
4661
4662 static char *
4663 ExpandPathName (char *path)
4664 {
4665     static char static_buf[4*MSG_SIZ];
4666     char *d, *s, buf[4*MSG_SIZ];
4667     struct passwd *pwd;
4668
4669     s = path;
4670     d = static_buf;
4671
4672     while (*s && isspace(*s))
4673       ++s;
4674
4675     if (!*s) {
4676         *d = 0;
4677         return static_buf;
4678     }
4679
4680     if (*s == '~') {
4681         if (*(s+1) == '/') {
4682           safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
4683           strcat(d, s+1);
4684         }
4685         else {
4686           safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
4687           { char *p; if(p = strchr(buf, '/')) *p = 0; }
4688           pwd = getpwnam(buf);
4689           if (!pwd)
4690             {
4691               fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4692                       buf, path);
4693               return NULL;
4694             }
4695           safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
4696           strcat(d, strchr(s+1, '/'));
4697         }
4698     }
4699     else
4700       safeStrCpy(d, s, 4*MSG_SIZ );
4701
4702     return static_buf;
4703 }
4704
4705 char *
4706 HostName ()
4707 {
4708     static char host_name[MSG_SIZ];
4709
4710 #if HAVE_GETHOSTNAME
4711     gethostname(host_name, MSG_SIZ);
4712     return host_name;
4713 #else  /* not HAVE_GETHOSTNAME */
4714 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4715     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4716     return host_name;
4717 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4718     return "localhost";
4719 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4720 #endif /* not HAVE_GETHOSTNAME */
4721 }
4722
4723 XtIntervalId delayedEventTimerXID = 0;
4724 DelayedEventCallback delayedEventCallback = 0;
4725
4726 void
4727 FireDelayedEvent ()
4728 {
4729     delayedEventTimerXID = 0;
4730     delayedEventCallback();
4731 }
4732
4733 void
4734 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
4735 {
4736     if(delayedEventTimerXID && delayedEventCallback == cb)
4737         // [HGM] alive: replace, rather than add or flush identical event
4738         XtRemoveTimeOut(delayedEventTimerXID);
4739     delayedEventCallback = cb;
4740     delayedEventTimerXID =
4741       XtAppAddTimeOut(appContext, millisec,
4742                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
4743 }
4744
4745 DelayedEventCallback
4746 GetDelayedEvent ()
4747 {
4748   if (delayedEventTimerXID) {
4749     return delayedEventCallback;
4750   } else {
4751     return NULL;
4752   }
4753 }
4754
4755 void
4756 CancelDelayedEvent ()
4757 {
4758   if (delayedEventTimerXID) {
4759     XtRemoveTimeOut(delayedEventTimerXID);
4760     delayedEventTimerXID = 0;
4761   }
4762 }
4763
4764 XtIntervalId loadGameTimerXID = 0;
4765
4766 int
4767 LoadGameTimerRunning ()
4768 {
4769     return loadGameTimerXID != 0;
4770 }
4771
4772 int
4773 StopLoadGameTimer ()
4774 {
4775     if (loadGameTimerXID != 0) {
4776         XtRemoveTimeOut(loadGameTimerXID);
4777         loadGameTimerXID = 0;
4778         return TRUE;
4779     } else {
4780         return FALSE;
4781     }
4782 }
4783
4784 void
4785 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
4786 {
4787     loadGameTimerXID = 0;
4788     AutoPlayGameLoop();
4789 }
4790
4791 void
4792 StartLoadGameTimer (long millisec)
4793 {
4794     loadGameTimerXID =
4795       XtAppAddTimeOut(appContext, millisec,
4796                       (XtTimerCallbackProc) LoadGameTimerCallback,
4797                       (XtPointer) 0);
4798 }
4799
4800 XtIntervalId analysisClockXID = 0;
4801
4802 void
4803 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
4804 {
4805     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4806          || appData.icsEngineAnalyze) { // [DM]
4807         AnalysisPeriodicEvent(0);
4808         StartAnalysisClock();
4809     }
4810 }
4811
4812 void
4813 StartAnalysisClock ()
4814 {
4815     analysisClockXID =
4816       XtAppAddTimeOut(appContext, 2000,
4817                       (XtTimerCallbackProc) AnalysisClockCallback,
4818                       (XtPointer) 0);
4819 }
4820
4821 XtIntervalId clockTimerXID = 0;
4822
4823 int
4824 ClockTimerRunning ()
4825 {
4826     return clockTimerXID != 0;
4827 }
4828
4829 int
4830 StopClockTimer ()
4831 {
4832     if (clockTimerXID != 0) {
4833         XtRemoveTimeOut(clockTimerXID);
4834         clockTimerXID = 0;
4835         return TRUE;
4836     } else {
4837         return FALSE;
4838     }
4839 }
4840
4841 void
4842 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
4843 {
4844     clockTimerXID = 0;
4845     DecrementClocks();
4846 }
4847
4848 void
4849 StartClockTimer (long millisec)
4850 {
4851     clockTimerXID =
4852       XtAppAddTimeOut(appContext, millisec,
4853                       (XtTimerCallbackProc) ClockTimerCallback,
4854                       (XtPointer) 0);
4855 }
4856
4857 void
4858 DisplayTimerLabel (Widget w, char *color, long timer, int highlight)
4859 {
4860     char buf[MSG_SIZ];
4861     Arg args[16];
4862
4863     /* check for low time warning */
4864     Pixel foregroundOrWarningColor = timerForegroundPixel;
4865
4866     if (timer > 0 &&
4867         appData.lowTimeWarning &&
4868         (timer / 1000) < appData.icsAlarmTime)
4869       foregroundOrWarningColor = lowTimeWarningColor;
4870
4871     if (appData.clockMode) {
4872       snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
4873       XtSetArg(args[0], XtNlabel, buf);
4874     } else {
4875       snprintf(buf, MSG_SIZ, "%s  ", color);
4876       XtSetArg(args[0], XtNlabel, buf);
4877     }
4878
4879     if (highlight) {
4880
4881         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
4882         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
4883     } else {
4884         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
4885         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
4886     }
4887
4888     XtSetValues(w, args, 3);
4889 }
4890
4891 void
4892 DisplayWhiteClock (long timeRemaining, int highlight)
4893 {
4894     Arg args[16];
4895
4896     if(appData.noGUI) return;
4897     DisplayTimerLabel(whiteTimerWidget, _("White"), timeRemaining, highlight);
4898     if (highlight && iconPixmap == bIconPixmap) {
4899         iconPixmap = wIconPixmap;
4900         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4901         XtSetValues(shellWidget, args, 1);
4902     }
4903 }
4904
4905 void
4906 DisplayBlackClock (long timeRemaining, int highlight)
4907 {
4908     Arg args[16];
4909
4910     if(appData.noGUI) return;
4911     DisplayTimerLabel(blackTimerWidget, _("Black"), timeRemaining, highlight);
4912     if (highlight && iconPixmap == wIconPixmap) {
4913         iconPixmap = bIconPixmap;
4914         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
4915         XtSetValues(shellWidget, args, 1);
4916     }
4917 }
4918
4919 #define CPNone 0
4920 #define CPReal 1
4921 #define CPComm 2
4922 #define CPSock 3
4923 #define CPLoop 4
4924 typedef int CPKind;
4925
4926 typedef struct {
4927     CPKind kind;
4928     int pid;
4929     int fdTo, fdFrom;
4930 } ChildProc;
4931
4932
4933 int
4934 StartChildProcess (char *cmdLine, char *dir, ProcRef *pr)
4935 {
4936     char *argv[64], *p;
4937     int i, pid;
4938     int to_prog[2], from_prog[2];
4939     ChildProc *cp;
4940     char buf[MSG_SIZ];
4941
4942     if (appData.debugMode) {
4943         fprintf(debugFP, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
4944     }
4945
4946     /* We do NOT feed the cmdLine to the shell; we just
4947        parse it into blank-separated arguments in the
4948        most simple-minded way possible.
4949        */
4950     i = 0;
4951     safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
4952     p = buf;
4953     for (;;) {
4954         while(*p == ' ') p++;
4955         argv[i++] = p;
4956         if(*p == '"' || *p == '\'')
4957              p = strchr(++argv[i-1], *p);
4958         else p = strchr(p, ' ');
4959         if (p == NULL) break;
4960         *p++ = NULLCHAR;
4961     }
4962     argv[i] = NULL;
4963
4964     SetUpChildIO(to_prog, from_prog);
4965
4966     if ((pid = fork()) == 0) {
4967         /* Child process */
4968         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
4969         close(to_prog[1]);     // first close the unused pipe ends
4970         close(from_prog[0]);
4971         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
4972         dup2(from_prog[1], 1);
4973         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
4974         close(from_prog[1]);                   // and closing again loses one of the pipes!
4975         if(fileno(stderr) >= 2) // better safe than sorry...
4976                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
4977
4978         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
4979             perror(dir);
4980             exit(1);
4981         }
4982
4983         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
4984
4985         execvp(argv[0], argv);
4986
4987         /* If we get here, exec failed */
4988         perror(argv[0]);
4989         exit(1);
4990     }
4991
4992     /* Parent process */
4993     close(to_prog[0]);
4994     close(from_prog[1]);
4995
4996     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
4997     cp->kind = CPReal;
4998     cp->pid = pid;
4999     cp->fdFrom = from_prog[0];
5000     cp->fdTo = to_prog[1];
5001     *pr = (ProcRef) cp;
5002     return 0;
5003 }
5004
5005 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5006 static RETSIGTYPE
5007 AlarmCallBack (int n)
5008 {
5009     return;
5010 }
5011
5012 void
5013 DestroyChildProcess (ProcRef pr, int signalType)
5014 {
5015     ChildProc *cp = (ChildProc *) pr;
5016
5017     if (cp->kind != CPReal) return;
5018     cp->kind = CPNone;
5019     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5020         signal(SIGALRM, AlarmCallBack);
5021         alarm(3);
5022         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5023             kill(cp->pid, SIGKILL); // kill it forcefully
5024             wait((int *) 0);        // and wait again
5025         }
5026     } else {
5027         if (signalType) {
5028             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5029         }
5030         /* Process is exiting either because of the kill or because of
5031            a quit command sent by the backend; either way, wait for it to die.
5032         */
5033         wait((int *) 0);
5034     }
5035     close(cp->fdFrom);
5036     close(cp->fdTo);
5037 }
5038
5039 void
5040 InterruptChildProcess (ProcRef pr)
5041 {
5042     ChildProc *cp = (ChildProc *) pr;
5043
5044     if (cp->kind != CPReal) return;
5045     (void) kill(cp->pid, SIGINT); /* stop it thinking */
5046 }
5047
5048 int
5049 OpenTelnet (char *host, char *port, ProcRef *pr)
5050 {
5051     char cmdLine[MSG_SIZ];
5052
5053     if (port[0] == NULLCHAR) {
5054       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5055     } else {
5056       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5057     }
5058     return StartChildProcess(cmdLine, "", pr);
5059 }
5060
5061 int
5062 OpenTCP (char *host, char *port, ProcRef *pr)
5063 {
5064 #if OMIT_SOCKETS
5065     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5066 #else  /* !OMIT_SOCKETS */
5067     struct addrinfo hints;
5068     struct addrinfo *ais, *ai;
5069     int error;
5070     int s=0;
5071     ChildProc *cp;
5072
5073     memset(&hints, 0, sizeof(hints));
5074     hints.ai_family = AF_UNSPEC;
5075     hints.ai_socktype = SOCK_STREAM;
5076
5077     error = getaddrinfo(host, port, &hints, &ais);
5078     if (error != 0) {
5079       /* a getaddrinfo error is not an errno, so can't return it */
5080       fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
5081               host, port, gai_strerror(error));
5082       return ENOENT;
5083     }
5084      
5085     for (ai = ais; ai != NULL; ai = ai->ai_next) {
5086       if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
5087         error = errno;
5088         continue;
5089       }
5090       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
5091         error = errno;
5092         continue;
5093       }
5094       error = 0;
5095       break;
5096     }
5097     freeaddrinfo(ais);
5098
5099     if (error != 0) {
5100       return error;
5101     }
5102
5103     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5104     cp->kind = CPSock;
5105     cp->pid = 0;
5106     cp->fdFrom = s;
5107     cp->fdTo = s;
5108     *pr = (ProcRef) cp;
5109 #endif /* !OMIT_SOCKETS */
5110
5111     return 0;
5112 }
5113
5114 int
5115 OpenCommPort (char *name, ProcRef *pr)
5116 {
5117     int fd;
5118     ChildProc *cp;
5119
5120     fd = open(name, 2, 0);
5121     if (fd < 0) return errno;
5122
5123     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5124     cp->kind = CPComm;
5125     cp->pid = 0;
5126     cp->fdFrom = fd;
5127     cp->fdTo = fd;
5128     *pr = (ProcRef) cp;
5129
5130     return 0;
5131 }
5132
5133 int
5134 OpenLoopback (ProcRef *pr)
5135 {
5136     ChildProc *cp;
5137     int to[2], from[2];
5138
5139     SetUpChildIO(to, from);
5140
5141     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5142     cp->kind = CPLoop;
5143     cp->pid = 0;
5144     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
5145     cp->fdTo = to[1];
5146     *pr = (ProcRef) cp;
5147
5148     return 0;
5149 }
5150
5151 int
5152 OpenRcmd (char *host, char *user, char *cmd, ProcRef *pr)
5153 {
5154     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5155     return -1;
5156 }
5157
5158 #define INPUT_SOURCE_BUF_SIZE 8192
5159
5160 typedef struct {
5161     CPKind kind;
5162     int fd;
5163     int lineByLine;
5164     char *unused;
5165     InputCallback func;
5166     XtInputId xid;
5167     char buf[INPUT_SOURCE_BUF_SIZE];
5168     VOIDSTAR closure;
5169 } InputSource;
5170
5171 void
5172 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
5173 {
5174     InputSource *is = (InputSource *) closure;
5175     int count;
5176     int error;
5177     char *p, *q;
5178
5179     if (is->lineByLine) {
5180         count = read(is->fd, is->unused,
5181                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5182         if (count <= 0) {
5183             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5184             return;
5185         }
5186         is->unused += count;
5187         p = is->buf;
5188         while (p < is->unused) {
5189             q = memchr(p, '\n', is->unused - p);
5190             if (q == NULL) break;
5191             q++;
5192             (is->func)(is, is->closure, p, q - p, 0);
5193             p = q;
5194         }
5195         q = is->buf;
5196         while (p < is->unused) {
5197             *q++ = *p++;
5198         }
5199         is->unused = q;
5200     } else {
5201         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5202         if (count == -1)
5203           error = errno;
5204         else
5205           error = 0;
5206         (is->func)(is, is->closure, is->buf, count, error);
5207     }
5208 }
5209
5210 InputSourceRef
5211 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
5212 {
5213     InputSource *is;
5214     ChildProc *cp = (ChildProc *) pr;
5215
5216     is = (InputSource *) calloc(1, sizeof(InputSource));
5217     is->lineByLine = lineByLine;
5218     is->func = func;
5219     if (pr == NoProc) {
5220         is->kind = CPReal;
5221         is->fd = fileno(stdin);
5222     } else {
5223         is->kind = cp->kind;
5224         is->fd = cp->fdFrom;
5225     }
5226     if (lineByLine) {
5227         is->unused = is->buf;
5228     }
5229
5230     is->xid = XtAppAddInput(appContext, is->fd,
5231                             (XtPointer) (XtInputReadMask),
5232                             (XtInputCallbackProc) DoInputCallback,
5233                             (XtPointer) is);
5234     is->closure = closure;
5235     return (InputSourceRef) is;
5236 }
5237
5238 void
5239 RemoveInputSource (InputSourceRef isr)
5240 {
5241     InputSource *is = (InputSource *) isr;
5242
5243     if (is->xid == 0) return;
5244     XtRemoveInput(is->xid);
5245     is->xid = 0;
5246 }
5247
5248 int
5249 OutputToProcess (ProcRef pr, char *message, int count, int *outError)
5250 {
5251     static int line = 0;
5252     ChildProc *cp = (ChildProc *) pr;
5253     int outCount;
5254
5255     if (pr == NoProc)
5256     {
5257         if (appData.noJoin || !appData.useInternalWrap)
5258             outCount = fwrite(message, 1, count, stdout);
5259         else
5260         {
5261             int width = get_term_width();
5262             int len = wrap(NULL, message, count, width, &line);
5263             char *msg = malloc(len);
5264             int dbgchk;
5265
5266             if (!msg)
5267                 outCount = fwrite(message, 1, count, stdout);
5268             else
5269             {
5270                 dbgchk = wrap(msg, message, count, width, &line);
5271                 if (dbgchk != len && appData.debugMode)
5272                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5273                 outCount = fwrite(msg, 1, dbgchk, stdout);
5274                 free(msg);
5275             }
5276         }
5277     }
5278     else
5279       outCount = write(cp->fdTo, message, count);
5280
5281     if (outCount == -1)
5282       *outError = errno;
5283     else
5284       *outError = 0;
5285
5286     return outCount;
5287 }
5288
5289 /* Output message to process, with "ms" milliseconds of delay
5290    between each character. This is needed when sending the logon
5291    script to ICC, which for some reason doesn't like the
5292    instantaneous send. */
5293 int
5294 OutputToProcessDelayed (ProcRef pr, char *message, int count, int *outError, long msdelay)
5295 {
5296     ChildProc *cp = (ChildProc *) pr;
5297     int outCount = 0;
5298     int r;
5299
5300     while (count--) {
5301         r = write(cp->fdTo, message++, 1);
5302         if (r == -1) {
5303             *outError = errno;
5304             return outCount;
5305         }
5306         ++outCount;
5307         if (msdelay >= 0)
5308           TimeDelay(msdelay);
5309     }
5310
5311     return outCount;
5312 }
5313
5314 /****   Animation code by Hugh Fisher, DCS, ANU. ****/
5315
5316 /*      Masks for XPM pieces. Black and white pieces can have
5317         different shapes, but in the interest of retaining my
5318         sanity pieces must have the same outline on both light
5319         and dark squares, and all pieces must use the same
5320         background square colors/images.                */
5321
5322 static int xpmDone = 0;
5323 static Pixmap animBufs[3*NrOfAnims]; // newBuf, saveBuf
5324 static GC animGCs[3*NrOfAnims]; // blitGC, pieceGC, outlineGC;
5325
5326 static void
5327 CreateAnimMasks (int pieceDepth)
5328 {
5329   ChessSquare   piece;
5330   Pixmap        buf;
5331   GC            bufGC, maskGC;
5332   int           kind, n;
5333   unsigned long plane;
5334   XGCValues     values;
5335
5336   /* Need a bitmap just to get a GC with right depth */
5337   buf = XCreatePixmap(xDisplay, xBoardWindow,
5338                         8, 8, 1);
5339   values.foreground = 1;
5340   values.background = 0;
5341   /* Don't use XtGetGC, not read only */
5342   maskGC = XCreateGC(xDisplay, buf,
5343                     GCForeground | GCBackground, &values);
5344   XFreePixmap(xDisplay, buf);
5345
5346   buf = XCreatePixmap(xDisplay, xBoardWindow,
5347                       squareSize, squareSize, pieceDepth);
5348   values.foreground = XBlackPixel(xDisplay, xScreen);
5349   values.background = XWhitePixel(xDisplay, xScreen);
5350   bufGC = XCreateGC(xDisplay, buf,
5351                     GCForeground | GCBackground, &values);
5352
5353   for (piece = WhitePawn; piece <= BlackKing; piece++) {
5354     /* Begin with empty mask */
5355     if(!xpmDone) // [HGM] pieces: keep using existing
5356     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5357                                  squareSize, squareSize, 1);
5358     XSetFunction(xDisplay, maskGC, GXclear);
5359     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5360                    0, 0, squareSize, squareSize);
5361
5362     /* Take a copy of the piece */
5363     if (White(piece))
5364       kind = 0;
5365     else
5366       kind = 2;
5367     XSetFunction(xDisplay, bufGC, GXcopy);
5368     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5369               buf, bufGC,
5370               0, 0, squareSize, squareSize, 0, 0);
5371
5372     /* XOR the background (light) over the piece */
5373     XSetFunction(xDisplay, bufGC, GXxor);
5374     if (useImageSqs)
5375       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5376                 0, 0, squareSize, squareSize, 0, 0);
5377     else {
5378       XSetForeground(xDisplay, bufGC, lightSquareColor);
5379       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5380     }
5381
5382     /* We now have an inverted piece image with the background
5383        erased. Construct mask by just selecting all the non-zero
5384        pixels - no need to reconstruct the original image.      */
5385     XSetFunction(xDisplay, maskGC, GXor);
5386     plane = 1;
5387     /* Might be quicker to download an XImage and create bitmap
5388        data from it rather than this N copies per piece, but it
5389        only takes a fraction of a second and there is a much
5390        longer delay for loading the pieces.             */
5391     for (n = 0; n < pieceDepth; n ++) {
5392       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5393                  0, 0, squareSize, squareSize,
5394                  0, 0, plane);
5395       plane = plane << 1;
5396     }
5397   }
5398   /* Clean up */
5399   XFreePixmap(xDisplay, buf);
5400   XFreeGC(xDisplay, bufGC);
5401   XFreeGC(xDisplay, maskGC);
5402 }
5403
5404 static void
5405 InitAnimState (AnimNr anr, XWindowAttributes *info)
5406 {
5407   XtGCMask  mask;
5408   XGCValues values;
5409
5410   /* Each buffer is square size, same depth as window */
5411   animBufs[anr+4] = xBoardWindow;
5412   animBufs[anr+2] = XCreatePixmap(xDisplay, xBoardWindow,
5413                         squareSize, squareSize, info->depth);
5414   animBufs[anr] = XCreatePixmap(xDisplay, xBoardWindow,
5415                         squareSize, squareSize, info->depth);
5416
5417   /* Create a plain GC for blitting */
5418   mask = GCForeground | GCBackground | GCFunction |
5419          GCPlaneMask | GCGraphicsExposures;
5420   values.foreground = XBlackPixel(xDisplay, xScreen);
5421   values.background = XWhitePixel(xDisplay, xScreen);
5422   values.function   = GXcopy;
5423   values.plane_mask = AllPlanes;
5424   values.graphics_exposures = False;
5425   animGCs[anr] = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5426
5427   /* Piece will be copied from an existing context at
5428      the start of each new animation/drag. */
5429   animGCs[anr+2] = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5430
5431   /* Outline will be a read-only copy of an existing */
5432   animGCs[anr+4] = None;
5433 }
5434
5435 void
5436 CreateAnimVars ()
5437 {
5438   XWindowAttributes info;
5439
5440   if (xpmDone && gameInfo.variant == oldVariant) return;
5441   if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
5442   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5443
5444   InitAnimState(Game, &info);
5445   InitAnimState(Player, &info);
5446
5447   /* For XPM pieces, we need bitmaps to use as masks. */
5448   if (useImages)
5449     CreateAnimMasks(info.depth), xpmDone = 1;
5450 }
5451
5452 #ifndef HAVE_USLEEP
5453
5454 static Boolean frameWaiting;
5455
5456 static RETSIGTYPE
5457 FrameAlarm (int sig)
5458 {
5459   frameWaiting = False;
5460   /* In case System-V style signals.  Needed?? */
5461   signal(SIGALRM, FrameAlarm);
5462 }
5463
5464 void
5465 FrameDelay (int time)
5466 {
5467   struct itimerval delay;
5468
5469   XSync(xDisplay, False);
5470
5471   if (time > 0) {
5472     frameWaiting = True;
5473     signal(SIGALRM, FrameAlarm);
5474     delay.it_interval.tv_sec =
5475       delay.it_value.tv_sec = time / 1000;
5476     delay.it_interval.tv_usec =
5477       delay.it_value.tv_usec = (time % 1000) * 1000;
5478     setitimer(ITIMER_REAL, &delay, NULL);
5479     while (frameWaiting) pause();
5480     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5481     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5482     setitimer(ITIMER_REAL, &delay, NULL);
5483   }
5484 }
5485
5486 #else
5487
5488 void
5489 FrameDelay (int time)
5490 {
5491   XSync(xDisplay, False);
5492   if (time > 0)
5493     usleep(time * 1000);
5494 }
5495
5496 #endif
5497
5498 static void
5499 SelectGCMask (ChessSquare piece, GC *clip, GC *outline, Pixmap *mask)
5500 {
5501   GC source;
5502
5503   /* Bitmap for piece being moved. */
5504   if (appData.monoMode) {
5505       *mask = *pieceToSolid(piece);
5506   } else if (useImages) {
5507 #if HAVE_LIBXPM
5508       *mask = xpmMask[piece];
5509 #else
5510       *mask = ximMaskPm[piece];
5511 #endif
5512   } else {
5513       *mask = *pieceToSolid(piece);
5514   }
5515
5516   /* GC for piece being moved. Square color doesn't matter, but
5517      since it gets modified we make a copy of the original. */
5518   if (White(piece)) {
5519     if (appData.monoMode)
5520       source = bwPieceGC;
5521     else
5522       source = wlPieceGC;
5523   } else {
5524     if (appData.monoMode)
5525       source = wbPieceGC;
5526     else
5527       source = blPieceGC;
5528   }
5529   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5530
5531   /* Outline only used in mono mode and is not modified */
5532   if (White(piece))
5533     *outline = bwPieceGC;
5534   else
5535     *outline = wbPieceGC;
5536 }
5537
5538 static void
5539 OverlayPiece (ChessSquare piece, GC clip, GC outline,  Drawable dest)
5540 {
5541   int   kind;
5542
5543   if (!useImages) {
5544     /* Draw solid rectangle which will be clipped to shape of piece */
5545     XFillRectangle(xDisplay, dest, clip,
5546                    0, 0, squareSize, squareSize);
5547     if (appData.monoMode)
5548       /* Also draw outline in contrasting color for black
5549          on black / white on white cases                */
5550       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5551                  0, 0, squareSize, squareSize, 0, 0, 1);
5552   } else {
5553     /* Copy the piece */
5554     if (White(piece))
5555       kind = 0;
5556     else
5557       kind = 2;
5558     if(appData.upsideDown && flipView) kind ^= 2;
5559     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5560               dest, clip,
5561               0, 0, squareSize, squareSize,
5562               0, 0);
5563   }
5564 }
5565
5566 void
5567 InsertPiece (AnimNr anr, ChessSquare piece)
5568 {
5569   OverlayPiece(piece, animGCs[anr+2], animGCs[anr+4], animBufs[anr]);
5570 }
5571
5572 void
5573 DrawBlank (AnimNr anr, int x, int y, int startColor)
5574 {
5575     BlankSquare(x, y, startColor, EmptySquare, animBufs[anr+2], 0);
5576 }
5577
5578 void CopyRectangle (AnimNr anr, int srcBuf, int destBuf,
5579                  int srcX, int srcY, int width, int height, int destX, int destY)
5580 {
5581     XCopyArea(xDisplay, animBufs[anr+srcBuf], animBufs[anr+destBuf], animGCs[anr],
5582                 srcX, srcY, width, height, destX, destY);
5583 }
5584
5585 void
5586 SetDragPiece (AnimNr anr, ChessSquare piece)
5587 {
5588   Pixmap mask;
5589   /* The piece will be drawn using its own bitmap as a matte    */
5590   SelectGCMask(piece, &animGCs[anr+2], &animGCs[anr+4], &mask);
5591   XSetClipMask(xDisplay, animGCs[anr+2], mask);
5592 }
5593
5594 #include <sys/ioctl.h>
5595 int
5596 get_term_width ()
5597 {
5598     int fd, default_width;
5599
5600     fd = STDIN_FILENO;
5601     default_width = 79; // this is FICS default anyway...
5602
5603 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
5604     struct ttysize win;
5605     if (!ioctl(fd, TIOCGSIZE, &win))
5606         default_width = win.ts_cols;
5607 #elif defined(TIOCGWINSZ)
5608     struct winsize win;
5609     if (!ioctl(fd, TIOCGWINSZ, &win))
5610         default_width = win.ws_col;
5611 #endif
5612     return default_width;
5613 }
5614
5615 void
5616 update_ics_width ()
5617 {
5618   static int old_width = 0;
5619   int new_width = get_term_width();
5620
5621   if (old_width != new_width)
5622     ics_printf("set width %d\n", new_width);
5623   old_width = new_width;
5624 }
5625
5626 void
5627 NotifyFrontendLogin ()
5628 {
5629     update_ics_width();
5630 }
5631
5632 /* [AS] Arrow highlighting support */
5633
5634 void
5635 DrawPolygon (Pnt arrow[], int nr)
5636 {
5637     XPoint pts[10];
5638     int i;
5639     for(i=0; i<10; i++) pts[i].x = arrow[i].x, pts[i].y = arrow[i].y;
5640     XFillPolygon(xDisplay, xBoardWindow, highlineGC, pts, nr, Nonconvex, CoordModeOrigin);
5641     if(appData.monoMode) arrow[nr] = arrow[0], XDrawLines(xDisplay, xBoardWindow, darkSquareGC, pts, nr+1, CoordModeOrigin);
5642 }
5643
5644 void
5645 UpdateLogos (int displ)
5646 {
5647     return; // no logos in XBoard yet
5648 }
5649