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