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