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