Add --show-config special option
[xboard.git] / xaw / 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, 2013, 2014 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 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
66
67 #if !OMIT_SOCKETS
68 # if HAVE_SYS_SOCKET_H
69 #  include <sys/socket.h>
70 #  include <netinet/in.h>
71 #  include <netdb.h>
72 # else /* not HAVE_SYS_SOCKET_H */
73 #  if HAVE_LAN_SOCKET_H
74 #   include <lan/socket.h>
75 #   include <lan/in.h>
76 #   include <lan/netdb.h>
77 #  else /* not HAVE_LAN_SOCKET_H */
78 #   define OMIT_SOCKETS 1
79 #  endif /* not HAVE_LAN_SOCKET_H */
80 # endif /* not HAVE_SYS_SOCKET_H */
81 #endif /* !OMIT_SOCKETS */
82
83 #if STDC_HEADERS
84 # include <stdlib.h>
85 # include <string.h>
86 #else /* not STDC_HEADERS */
87 extern char *getenv();
88 # if HAVE_STRING_H
89 #  include <string.h>
90 # else /* not HAVE_STRING_H */
91 #  include <strings.h>
92 # endif /* not HAVE_STRING_H */
93 #endif /* not STDC_HEADERS */
94
95 #if HAVE_SYS_FCNTL_H
96 # include <sys/fcntl.h>
97 #else /* not HAVE_SYS_FCNTL_H */
98 # if HAVE_FCNTL_H
99 #  include <fcntl.h>
100 # endif /* HAVE_FCNTL_H */
101 #endif /* not HAVE_SYS_FCNTL_H */
102
103 #if HAVE_SYS_SYSTEMINFO_H
104 # include <sys/systeminfo.h>
105 #endif /* HAVE_SYS_SYSTEMINFO_H */
106
107 #if TIME_WITH_SYS_TIME
108 # include <sys/time.h>
109 # include <time.h>
110 #else
111 # if HAVE_SYS_TIME_H
112 #  include <sys/time.h>
113 # else
114 #  include <time.h>
115 # endif
116 #endif
117
118 #if HAVE_UNISTD_H
119 # include <unistd.h>
120 #endif
121
122 #if HAVE_SYS_WAIT_H
123 # include <sys/wait.h>
124 #endif
125
126 #if HAVE_DIRENT_H
127 # include <dirent.h>
128 # define NAMLEN(dirent) strlen((dirent)->d_name)
129 # define HAVE_DIR_STRUCT
130 #else
131 # define dirent direct
132 # define NAMLEN(dirent) (dirent)->d_namlen
133 # if HAVE_SYS_NDIR_H
134 #  include <sys/ndir.h>
135 #  define HAVE_DIR_STRUCT
136 # endif
137 # if HAVE_SYS_DIR_H
138 #  include <sys/dir.h>
139 #  define HAVE_DIR_STRUCT
140 # endif
141 # if HAVE_NDIR_H
142 #  include <ndir.h>
143 #  define HAVE_DIR_STRUCT
144 # endif
145 #endif
146
147 #if ENABLE_NLS
148 #include <locale.h>
149 #endif
150
151 #include <X11/Intrinsic.h>
152 #include <X11/StringDefs.h>
153 #include <X11/Shell.h>
154 #include <X11/cursorfont.h>
155 #include <X11/Xatom.h>
156 #include <X11/Xmu/Atoms.h>
157 #if USE_XAW3D
158 #include <X11/Xaw3d/Dialog.h>
159 #include <X11/Xaw3d/Form.h>
160 #include <X11/Xaw3d/List.h>
161 #include <X11/Xaw3d/Label.h>
162 #include <X11/Xaw3d/SimpleMenu.h>
163 #include <X11/Xaw3d/SmeBSB.h>
164 #include <X11/Xaw3d/SmeLine.h>
165 #include <X11/Xaw3d/Box.h>
166 #include <X11/Xaw3d/MenuButton.h>
167 #include <X11/Xaw3d/Text.h>
168 #include <X11/Xaw3d/AsciiText.h>
169 #else
170 #include <X11/Xaw/Dialog.h>
171 #include <X11/Xaw/Form.h>
172 #include <X11/Xaw/List.h>
173 #include <X11/Xaw/Label.h>
174 #include <X11/Xaw/SimpleMenu.h>
175 #include <X11/Xaw/SmeBSB.h>
176 #include <X11/Xaw/SmeLine.h>
177 #include <X11/Xaw/Box.h>
178 #include <X11/Xaw/MenuButton.h>
179 #include <X11/Xaw/Text.h>
180 #include <X11/Xaw/AsciiText.h>
181 #endif
182
183 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
184 #include "common.h"
185
186 #include "bitmaps/icon_white.bm"
187 #include "bitmaps/icon_black.bm"
188 #include "bitmaps/checkmark.bm"
189
190 #include "frontend.h"
191 #include "backend.h"
192 #include "backendz.h"
193 #include "moves.h"
194 #include "xboard.h"
195 #include "childio.h"
196 #include "xgamelist.h"
197 #include "xhistory.h"
198 #include "menus.h"
199 #include "board.h"
200 #include "dialogs.h"
201 #include "engineoutput.h"
202 #include "usystem.h"
203 #include "gettext.h"
204 #include "draw.h"
205
206 #define SLASH '/'
207
208 #ifdef __EMX__
209 #ifndef HAVE_USLEEP
210 #define HAVE_USLEEP
211 #endif
212 #define usleep(t)   _sleep2(((t)+500)/1000)
213 #endif
214
215 #ifdef ENABLE_NLS
216 # define  _(s) gettext (s)
217 # define N_(s) gettext_noop (s)
218 #else
219 # define  _(s) (s)
220 # define N_(s)  s
221 #endif
222
223 int main P((int argc, char **argv));
224 RETSIGTYPE CmailSigHandler P((int sig));
225 RETSIGTYPE IntSigHandler P((int sig));
226 RETSIGTYPE TermSizeSigHandler P((int sig));
227 Widget CreateMenuBar P((Menu *mb, int boardWidth));
228 #if ENABLE_NLS
229 char *InsertPxlSize P((char *pattern, int targetPxlSize));
230 XFontSet CreateFontSet P((char *base_fnt_lst));
231 #else
232 char *FindFont P((char *pattern, int targetPxlSize));
233 #endif
234 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
235                    u_int wreq, u_int hreq));
236 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
237 void DelayedDrag P((void));
238 static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
239 void HandlePV P((Widget w, XEvent * event,
240                      String * params, Cardinal * nParams));
241 void DrawPositionProc P((Widget w, XEvent *event,
242                      String *prms, Cardinal *nprms));
243 void CommentClick P((Widget w, XEvent * event,
244                    String * params, Cardinal * nParams));
245 void ICSInputBoxPopUp P((void));
246 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
247 void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
248 void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
249 static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
250 static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
251 static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
252 void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
253 void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
254 Boolean TempBackwardActive = False;
255 void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
256 void DisplayMove P((int moveNumber));
257 void update_ics_width P(());
258 int CopyMemoProc P(());
259
260 /*
261 * XBoard depends on Xt R4 or higher
262 */
263 int xtVersion = XtSpecificationRelease;
264
265 int xScreen;
266 Display *xDisplay;
267 Window xBoardWindow;
268 Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
269 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
270 Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
271 Option *optList; // contains all widgets of main window
272 #if ENABLE_NLS
273 XFontSet fontSet, clockFontSet;
274 #else
275 Font clockFontID;
276 XFontStruct *clockFontStruct;
277 #endif
278 Font coordFontID, countFontID;
279 XFontStruct *coordFontStruct, *countFontStruct;
280 XtAppContext appContext;
281 char *layoutName;
282
283 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
284
285 Position commentX = -1, commentY = -1;
286 Dimension commentW, commentH;
287 typedef unsigned int BoardSize;
288 BoardSize boardSize;
289 Boolean chessProgram;
290
291 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
292 int smallLayout = 0, tinyLayout = 0,
293   marginW, marginH, // [HGM] for run-time resizing
294   fromX = -1, fromY = -1, toX, toY, commentUp = False,
295   errorExitStatus = -1, defaultLineGap;
296 Dimension textHeight;
297 Pixel timerForegroundPixel, timerBackgroundPixel;
298 Pixel buttonForegroundPixel, buttonBackgroundPixel;
299 char *chessDir, *programName, *programVersion;
300 Boolean alwaysOnTop = False;
301 char *icsTextMenuString;
302 char *icsNames;
303 char *firstChessProgramNames;
304 char *secondChessProgramNames;
305
306 WindowPlacement wpMain;
307 WindowPlacement wpConsole;
308 WindowPlacement wpComment;
309 WindowPlacement wpMoveHistory;
310 WindowPlacement wpEvalGraph;
311 WindowPlacement wpEngineOutput;
312 WindowPlacement wpGameList;
313 WindowPlacement wpTags;
314 WindowPlacement wpDualBoard;
315
316
317 /* This magic number is the number of intermediate frames used
318    in each half of the animation. For short moves it's reduced
319    by 1. The total number of frames will be factor * 2 + 1.  */
320 #define kFactor    4
321
322 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
323
324 typedef struct {
325     char piece;
326     char* widget;
327 } DropMenuEnables;
328
329 DropMenuEnables dmEnables[] = {
330     { 'P', "Pawn" },
331     { 'N', "Knight" },
332     { 'B', "Bishop" },
333     { 'R', "Rook" },
334     { 'Q', "Queen" }
335 };
336
337 Arg shellArgs[] = {
338     { XtNwidth, 0 },
339     { XtNheight, 0 },
340     { XtNminWidth, 0 },
341     { XtNminHeight, 0 },
342     { XtNmaxWidth, 0 },
343     { XtNmaxHeight, 0 }
344 };
345
346 XtResource clientResources[] = {
347     { "flashCount", "flashCount", XtRInt, sizeof(int),
348         XtOffset(AppDataPtr, flashCount), XtRImmediate,
349         (XtPointer) FLASH_COUNT  },
350 };
351
352 XrmOptionDescRec shellOptions[] = {
353     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
354     { "-flash", "flashCount", XrmoptionNoArg, "3" },
355     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
356 };
357
358 XtActionsRec boardActions[] = {
359     { "DrawPosition", DrawPositionProc },
360     { "HandlePV", HandlePV },
361     { "SelectPV", SelectPV },
362     { "StopPV", StopPV },
363     { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
364     { "QuitProc", QuitWrapper },
365     { "ManProc", ManInner },
366     { "TempBackwardProc", TempBackwardProc },
367     { "TempForwardProc", TempForwardProc },
368     { "CommentClick", (XtActionProc) CommentClick },
369     { "GenericPopDown", (XtActionProc) GenericPopDown },
370     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
371     { "CopyMemoProc", (XtActionProc) CopyMemoProc },
372     { "SelectMove", (XtActionProc) SelectMoveX },
373     { "LoadSelectedProc", LoadSelectedProc },
374     { "SetFilterProc", SetFilterProc },
375     { "TypeInProc", TypeInProc },
376     { "EnterKeyProc", EnterKeyProc },
377     { "UpKeyProc", UpKeyProc },
378     { "DownKeyProc", DownKeyProc },
379     { "WheelProc", WheelProc },
380     { "TabProc", TabProc },
381 };
382
383 char globalTranslations[] =
384   ":Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
385    :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
386    :Ctrl<Key>Down: LoadSelectedProc(3) \n \
387    :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
388    :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
389    :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
390    :<Key>Pause: MenuItem(Mode.Pause) \n \
391    :Ctrl<Key>d: MenuItem(DebugProc) \n \
392    :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
393    :<Key>Left: MenuItem(Edit.Backward) \n \
394    :<Key>Right: MenuItem(Edit.Forward) \n \
395    :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
396 #ifndef OPTIONSDIALOG
397     "\
398    :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
399    :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
400    :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
401    :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
402    :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
403 #endif
404    "\
405    :<KeyDown>Return: TempBackwardProc() \n \
406    :<KeyUp>Return: TempForwardProc() \n";
407
408 char ICSInputTranslations[] =
409     "<Key>Up: UpKeyProc() \n "
410     "<Key>Down: DownKeyProc() \n "
411     "<Key>Return: EnterKeyProc() \n";
412
413 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
414 //             as the widget is destroyed before the up-click can call extend-end
415 char commentTranslations[] = "<Btn3Down>: extend-end(PRIMARY) select-start() CommentClick() \n";
416
417 String xboardResources[] = {
418     "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
419     NULL
420   };
421
422
423 /* Max possible square size */
424 #define MAXSQSIZE 256
425
426 /* Arrange to catch delete-window events */
427 Atom wm_delete_window;
428 void
429 CatchDeleteWindow (Widget w, String procname)
430 {
431   char buf[MSG_SIZ];
432   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
433   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
434   XtAugmentTranslations(w, XtParseTranslationTable(buf));
435 }
436
437 void
438 BoardToTop ()
439 {
440   Arg args[16];
441   XtSetArg(args[0], XtNiconic, False);
442   XtSetValues(shellWidget, args, 1);
443
444   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
445 }
446
447 //---------------------------------------------------------------------------------------------------------
448 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
449 #define XBOARD True
450 #define JAWS_ARGS
451 #define CW_USEDEFAULT (1<<31)
452 #define ICS_TEXT_MENU_SIZE 90
453 #define DEBUG_FILE "xboard.debug"
454 #define SetCurrentDirectory chdir
455 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
456 #define OPTCHAR "-"
457 #define SEPCHAR " "
458
459 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
460 #include "args.h"
461
462 // front-end part of option handling
463
464 // [HGM] This platform-dependent table provides the location for storing the color info
465 extern char *crWhite, * crBlack;
466
467 void *
468 colorVariable[] = {
469   &appData.whitePieceColor,
470   &appData.blackPieceColor,
471   &appData.lightSquareColor,
472   &appData.darkSquareColor,
473   &appData.highlightSquareColor,
474   &appData.premoveHighlightColor,
475   &appData.lowTimeWarningColor,
476   NULL,
477   NULL,
478   NULL,
479   NULL,
480   NULL,
481   &crWhite,
482   &crBlack,
483   NULL
484 };
485
486 // [HGM] font: keep a font for each square size, even non-stndard ones
487 #define NUM_SIZES 18
488 #define MAX_SIZE 130
489 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
490 char *fontTable[NUM_FONTS][MAX_SIZE];
491
492 void
493 ParseFont (char *name, int number)
494 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
495   int size;
496   if(sscanf(name, "size%d:", &size)) {
497     // [HGM] font: font is meant for specific boardSize (likely from settings file);
498     //       defer processing it until we know if it matches our board size
499     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
500         fontTable[number][size] = strdup(strchr(name, ':')+1);
501         fontValid[number][size] = True;
502     }
503     return;
504   }
505   switch(number) {
506     case 0: // CLOCK_FONT
507         appData.clockFont = strdup(name);
508       break;
509     case 1: // MESSAGE_FONT
510         appData.font = strdup(name);
511       break;
512     case 2: // COORD_FONT
513         appData.coordFont = strdup(name);
514       break;
515     default:
516       return;
517   }
518   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
519 }
520
521 void
522 SetFontDefaults ()
523 { // only 2 fonts currently
524   appData.clockFont = CLOCK_FONT_NAME;
525   appData.coordFont = COORD_FONT_NAME;
526   appData.font  =   DEFAULT_FONT_NAME;
527 }
528
529 void
530 CreateFonts ()
531 { // no-op, until we identify the code for this already in XBoard and move it here
532 }
533
534 void
535 ParseColor (int n, char *name)
536 { // in XBoard, just copy the color-name string
537   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
538 }
539
540 void
541 ParseTextAttribs (ColorClass cc, char *s)
542 {
543     (&appData.colorShout)[cc] = strdup(s);
544 }
545
546 void
547 ParseBoardSize (void *addr, char *name)
548 {
549     appData.boardSize = strdup(name);
550 }
551
552 void
553 LoadAllSounds ()
554 { // In XBoard the sound-playing program takes care of obtaining the actual sound
555 }
556
557 void
558 SetCommPortDefaults ()
559 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
560 }
561
562 // [HGM] args: these three cases taken out to stay in front-end
563 void
564 SaveFontArg (FILE *f, ArgDescriptor *ad)
565 {
566   char *name;
567   int i, n = (int)(intptr_t)ad->argLoc;
568   switch(n) {
569     case 0: // CLOCK_FONT
570         name = appData.clockFont;
571       break;
572     case 1: // MESSAGE_FONT
573         name = appData.font;
574       break;
575     case 2: // COORD_FONT
576         name = appData.coordFont;
577       break;
578     default:
579       return;
580   }
581   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
582     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
583         fontTable[n][squareSize] = strdup(name);
584         fontValid[n][squareSize] = True;
585         break;
586   }
587   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
588     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
589 }
590
591 void
592 ExportSounds ()
593 { // nothing to do, as the sounds are at all times represented by their text-string names already
594 }
595
596 void
597 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
598 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
599         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
600 }
601
602 void
603 SaveColor (FILE *f, ArgDescriptor *ad)
604 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
605         if(colorVariable[(int)(intptr_t)ad->argLoc])
606         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
607 }
608
609 void
610 SaveBoardSize (FILE *f, char *name, void *addr)
611 { // wrapper to shield back-end from BoardSize & sizeInfo
612   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
613 }
614
615 void
616 ParseCommPortSettings (char *s)
617 { // no such option in XBoard (yet)
618 }
619
620 int frameX, frameY;
621
622 void
623 GetActualPlacement (Widget wg, WindowPlacement *wp)
624 {
625   XWindowAttributes winAt;
626   Window win, dummy;
627   int rx, ry;
628
629   if(!wg) return;
630
631   win = XtWindow(wg);
632   XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
633   XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
634   wp->x = rx - winAt.x;
635   wp->y = ry - winAt.y;
636   wp->height = winAt.height;
637   wp->width = winAt.width;
638   frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
639 }
640
641 void
642 GetWindowCoords ()
643 { // wrapper to shield use of window handles from back-end (make addressible by number?)
644   // In XBoard this will have to wait until awareness of window parameters is implemented
645   GetActualPlacement(shellWidget, &wpMain);
646   if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
647   if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
648   if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
649   if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
650   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
651   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
652 }
653
654 void
655 PrintCommPortSettings (FILE *f, char *name)
656 { // This option does not exist in XBoard
657 }
658
659 void
660 EnsureOnScreen (int *x, int *y, int minX, int minY)
661 {
662   return;
663 }
664
665 int
666 MainWindowUp ()
667 { // [HGM] args: allows testing if main window is realized from back-end
668   return xBoardWindow != 0;
669 }
670
671 void
672 PopUpStartupDialog ()
673 {  // start menu not implemented in XBoard
674 }
675
676 char *
677 ConvertToLine (int argc, char **argv)
678 {
679   static char line[128*1024], buf[1024];
680   int i;
681
682   line[0] = NULLCHAR;
683   for(i=1; i<argc; i++)
684     {
685       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
686           && argv[i][0] != '{' )
687         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
688       else
689         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
690       strncat(line, buf, 128*1024 - strlen(line) - 1 );
691     }
692
693   line[strlen(line)-1] = NULLCHAR;
694   return line;
695 }
696
697 //--------------------------------------------------------------------------------------------
698
699 void
700 ResizeBoardWindow (int w, int h, int inhibit)
701 {
702     w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
703     h += marginH;
704     shellArgs[0].value = w;
705     shellArgs[1].value = h;
706     shellArgs[4].value = shellArgs[2].value = w;
707     shellArgs[5].value = shellArgs[3].value = h;
708     XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
709
710     XSync(xDisplay, False);
711 }
712
713 static int
714 MakeOneColor (char *name, Pixel *color)
715 {
716     XrmValue vFrom, vTo;
717     if (!appData.monoMode) {
718         vFrom.addr = (caddr_t) name;
719         vFrom.size = strlen(name);
720         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
721         if (vTo.addr == NULL) {
722           appData.monoMode = True;
723           return True;
724         } else {
725           *color = *(Pixel *) vTo.addr;
726         }
727     }
728     return False;
729 }
730
731 int
732 MakeColors ()
733 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
734     int forceMono = False;
735
736     if (appData.lowTimeWarning)
737         forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
738     if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
739     if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
740
741     return forceMono;
742 }
743
744 void
745 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
746 {   // detervtomine what fonts to use, and create them
747     XrmValue vTo;
748     XrmDatabase xdb;
749
750     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
751         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
752     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
753         appData.font = fontTable[MESSAGE_FONT][squareSize];
754     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
755         appData.coordFont = fontTable[COORD_FONT][squareSize];
756
757 #if ENABLE_NLS
758     appData.font = InsertPxlSize(appData.font, fontPxlSize);
759     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
760     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
761     fontSet = CreateFontSet(appData.font);
762     clockFontSet = CreateFontSet(appData.clockFont);
763     {
764       /* For the coordFont, use the 0th font of the fontset. */
765       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
766       XFontStruct **font_struct_list;
767       XFontSetExtents *fontSize;
768       char **font_name_list;
769       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
770       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
771       coordFontStruct = XQueryFont(xDisplay, coordFontID);
772       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
773       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
774     }
775 #else
776     appData.font = FindFont(appData.font, fontPxlSize);
777     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
778     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
779     clockFontID = XLoadFont(xDisplay, appData.clockFont);
780     clockFontStruct = XQueryFont(xDisplay, clockFontID);
781     coordFontID = XLoadFont(xDisplay, appData.coordFont);
782     coordFontStruct = XQueryFont(xDisplay, coordFontID);
783     // textHeight in !NLS mode!
784 #endif
785     countFontID = coordFontID;  // [HGM] holdings
786     countFontStruct = coordFontStruct;
787
788     xdb = XtDatabase(xDisplay);
789 #if ENABLE_NLS
790     XrmPutLineResource(&xdb, "*international: True");
791     vTo.size = sizeof(XFontSet);
792     vTo.addr = (XtPointer) &fontSet;
793     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
794 #else
795     XrmPutStringResource(&xdb, "*font", appData.font);
796 #endif
797 }
798
799 char *
800 PrintArg (ArgType t)
801 {
802   char *p="";
803   switch(t) {
804     case ArgZ:
805     case ArgInt:      p = " N"; break;
806     case ArgString:   p = " STR"; break;
807     case ArgBoolean:  p = " TF"; break;
808     case ArgSettingsFilename:
809     case ArgBackupSettingsFile:
810     case ArgFilename: p = " FILE"; break;
811     case ArgX:        p = " Nx"; break;
812     case ArgY:        p = " Ny"; break;
813     case ArgAttribs:  p = " TEXTCOL"; break;
814     case ArgColor:    p = " COL"; break;
815     case ArgFont:     p = " FONT"; break;
816     case ArgBoardSize: p = " SIZE"; break;
817     case ArgFloat: p = " FLOAT"; break;
818     case ArgTrue:
819     case ArgFalse:
820     case ArgTwo:
821     case ArgNone:
822     case ArgCommSettings:
823     case ArgMaster:
824     case ArgInstall:
825       break;
826   }
827   return p;
828 }
829
830 char *
831 GenerateGlobalTranslationTable (void)
832 {
833   /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
834   char *output;
835
836   int i,j;
837   MenuItem *mi;
838
839   output = strdup("");
840
841   /* loop over all menu entries */
842   for( i=0; menuBar[i].mi ; i++)
843     {
844       mi = menuBar[i].mi;
845       for(j=0; mi[j].proc; j++)
846         {
847           if (mi[j].accel)
848             {
849               int ctrl  = 0;
850               int shift = 0;
851               int alt   = 0;
852
853               char *key,*test, *mods;
854
855               /* check for Ctrl/Alt */
856               if( strstr(mi[j].accel, "<Ctrl>")  ) ctrl  = 1;
857               if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
858               if( strstr(mi[j].accel, "<Alt>")   ) alt   = 1;
859
860               /* remove all <...> */
861               test = strrchr(mi[j].accel, '>');
862               if ( test==NULL )
863                 key = strdup(mi[j].accel);
864               else
865                 key = strdup(++test); // remove ">"
866
867               /* instead of shift X11 uses the uppercase letter directly*/
868               if (shift && strlen(key)==1 )
869                 {
870                   *key  = toupper(*key);
871                   shift = 0;
872                 }
873
874               /* handle some special cases which have different names in X11 */
875               if ( strncmp(key, "Page_Down", 9) == 0 )
876                 {
877                   free(key);
878                   key=strdup("Next");
879                 }
880               else if ( strncmp(key, "Page_Up", 7) == 0 )
881                 {
882                   free(key);
883                   key=strdup("Prior");
884                 };
885
886               /* create string of mods */
887               if (ctrl)
888                 mods = strdup("Ctrl ");
889               else
890                 mods = strdup("");
891
892               if(alt)
893                 {
894                   mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
895                   strncat(mods, "Meta ", 5);
896                 };
897
898               if(shift)
899                 {
900                   mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
901                   strncat(mods, "Shift ", 6);
902                 };
903
904               // remove trailing space
905               if( isspace(mods[strlen(mods)-1]) )
906                 mods[strlen(mods)-1]='\0';
907
908               /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
909               size_t namesize = snprintf(NULL, 0, "%s.%s", menuBar[i].ref, mi[j].ref);
910               char *name = malloc(namesize+1);
911               snprintf(name, namesize+1, "%s.%s", menuBar[i].ref, mi[j].ref);
912
913               size_t buffersize = snprintf(NULL, 0, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
914               char *buffer = malloc(buffersize+1);
915               snprintf(buffer, buffersize+1, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
916
917               /* add string to the output */
918               output = realloc(output, strlen(output) + strlen(buffer)+1);
919               strncat(output, buffer, strlen(buffer));
920
921               /* clean up */
922               free(key);
923               free(buffer);
924               free(name);
925               free(mods);
926             }
927         }
928     }
929   return output;
930 }
931
932
933 void
934 PrintOptions ()
935 {
936   char buf[MSG_SIZ];
937   int len=0;
938   ArgDescriptor *q, *p = argDescriptors+5;
939   printf("\nXBoard accepts the following options:\n"
940          "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
941          " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
942          " SIZE = board-size spec(s)\n"
943          " Within parentheses are short forms, or options to set to true or false.\n"
944          " Persistent options (saved in the settings file) are marked with *)\n\n");
945   while(p->argName) {
946     if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
947     snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
948     if(p->save) strcat(buf+len, "*");
949     for(q=p+1; q->argLoc == p->argLoc; q++) {
950       if(q->argName[0] == '-') continue;
951       strcat(buf+len, q == p+1 ? " (" : " ");
952       sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
953     }
954     if(q != p+1) strcat(buf+len, ")");
955     len = strlen(buf);
956     if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
957     p = q;
958   }
959   if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
960 }
961
962 void
963 SlaveResize (Option *opt)
964 {
965 }
966
967 int
968 main (int argc, char **argv)
969 {
970     int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
971     XSetWindowAttributes window_attributes;
972     Arg args[16];
973     Dimension boardWidth, boardHeight, w, h;
974     char *p;
975     int forceMono = False;
976
977     srandom(time(0)); // [HGM] book: make random truly random
978
979     setbuf(stdout, NULL);
980     setbuf(stderr, NULL);
981     debugFP = stderr;
982
983     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
984       printf("%s version %s\n\n  configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
985         exit(0);
986     }
987
988     if(argc > 1 && !strcmp(argv[1], "--help" )) {
989         PrintOptions();
990         exit(0);
991     }
992
993     if(argc > 1 && !strcmp(argv[1], "--show-config")) { // [HGM] install: called to print config info
994         typedef struct {char *name, *value; } Config;
995         static Config configList[] = {
996           { "Datadir", DATADIR },
997           { "Sysconfdir", SYSCONFDIR },
998           { NULL }
999         };
1000         int i;
1001
1002         for(i=0; configList[i].name; i++) {
1003             if(argc > 2 && strcmp(argv[2], configList[i].name)) continue;
1004             if(argc > 2) printf("%s", configList[i].value);
1005             else printf("%-12s: %s\n", configList[i].name, configList[i].value);
1006         }
1007         exit(0);
1008     }
1009
1010     programName = strrchr(argv[0], '/');
1011     if (programName == NULL)
1012       programName = argv[0];
1013     else
1014       programName++;
1015
1016 #ifdef ENABLE_NLS
1017     XtSetLanguageProc(NULL, NULL, NULL);
1018     if (appData.debugMode) {
1019       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1020     }
1021
1022     bindtextdomain(PACKAGE, LOCALEDIR);
1023     textdomain(PACKAGE);
1024 #endif
1025
1026     appData.boardSize = "";
1027     InitAppData(ConvertToLine(argc, argv));
1028     p = getenv("HOME");
1029     if (p == NULL) p = "/tmp";
1030     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1031     gameCopyFilename = (char*) malloc(i);
1032     gamePasteFilename = (char*) malloc(i);
1033     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1034     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1035
1036     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1037         static char buf[MSG_SIZ];
1038         EscapeExpand(buf, appData.firstInitString);
1039         appData.firstInitString = strdup(buf);
1040         EscapeExpand(buf, appData.secondInitString);
1041         appData.secondInitString = strdup(buf);
1042         EscapeExpand(buf, appData.firstComputerString);
1043         appData.firstComputerString = strdup(buf);
1044         EscapeExpand(buf, appData.secondComputerString);
1045         appData.secondComputerString = strdup(buf);
1046     }
1047
1048     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1049         chessDir = ".";
1050     } else {
1051         if (chdir(chessDir) != 0) {
1052             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1053             perror(chessDir);
1054             exit(1);
1055         }
1056     }
1057
1058     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1059         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1060         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1061            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1062            exit(errno);
1063         }
1064         setbuf(debugFP, NULL);
1065     }
1066
1067     /* [HGM,HR] make sure board size is acceptable */
1068     if(appData.NrFiles > BOARD_FILES ||
1069        appData.NrRanks > BOARD_RANKS   )
1070          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1071
1072 #if !HIGHDRAG
1073     /* This feature does not work; animation needs a rewrite */
1074     appData.highlightDragging = FALSE;
1075 #endif
1076     InitBackEnd1();
1077
1078         gameInfo.variant = StringToVariant(appData.variant);
1079         InitPosition(FALSE);
1080
1081     shellWidget =
1082       XtAppInitialize(&appContext, "XBoard", shellOptions,
1083                       XtNumber(shellOptions),
1084                       &argc, argv, xboardResources, NULL, 0);
1085
1086     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1087                               clientResources, XtNumber(clientResources),
1088                               NULL, 0);
1089
1090     xDisplay = XtDisplay(shellWidget);
1091     xScreen = DefaultScreen(xDisplay);
1092     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1093
1094     /*
1095      * determine size, based on supplied or remembered -size, or screen size
1096      */
1097     if (isdigit(appData.boardSize[0])) {
1098         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1099                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1100                    &fontPxlSize, &smallLayout, &tinyLayout);
1101         if (i == 0) {
1102             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1103                     programName, appData.boardSize);
1104             exit(2);
1105         }
1106         if (i < 7) {
1107             /* Find some defaults; use the nearest known size */
1108             SizeDefaults *szd, *nearest;
1109             int distance = 99999;
1110             nearest = szd = sizeDefaults;
1111             while (szd->name != NULL) {
1112                 if (abs(szd->squareSize - squareSize) < distance) {
1113                     nearest = szd;
1114                     distance = abs(szd->squareSize - squareSize);
1115                     if (distance == 0) break;
1116                 }
1117                 szd++;
1118             }
1119             if (i < 2) lineGap = nearest->lineGap;
1120             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1121             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1122             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1123             if (i < 6) smallLayout = nearest->smallLayout;
1124             if (i < 7) tinyLayout = nearest->tinyLayout;
1125         }
1126     } else {
1127         SizeDefaults *szd = sizeDefaults;
1128         if (*appData.boardSize == NULLCHAR) {
1129             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1130                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1131               szd++;
1132             }
1133             if (szd->name == NULL) szd--;
1134             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1135         } else {
1136             while (szd->name != NULL &&
1137                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1138             if (szd->name == NULL) {
1139                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1140                         programName, appData.boardSize);
1141                 exit(2);
1142             }
1143         }
1144         squareSize = szd->squareSize;
1145         lineGap = szd->lineGap;
1146         clockFontPxlSize = szd->clockFontPxlSize;
1147         coordFontPxlSize = szd->coordFontPxlSize;
1148         fontPxlSize = szd->fontPxlSize;
1149         smallLayout = szd->smallLayout;
1150         tinyLayout = szd->tinyLayout;
1151         // [HGM] font: use defaults from settings file if available and not overruled
1152     }
1153
1154     defaultLineGap = lineGap;
1155     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1156
1157     /* [HR] height treated separately (hacked) */
1158     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1159     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1160
1161     /*
1162      * Determine what fonts to use.
1163      */
1164     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1165
1166     /*
1167      * Detect if there are not enough colors available and adapt.
1168      */
1169     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1170       appData.monoMode = True;
1171     }
1172
1173     forceMono = MakeColors();
1174
1175     if (forceMono) {
1176       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1177               programName);
1178         appData.monoMode = True;
1179     }
1180
1181     if (appData.monoMode && appData.debugMode) {
1182         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1183                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1184                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1185     }
1186
1187     ParseIcsTextColors();
1188
1189     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1190
1191     /*
1192      * widget hierarchy
1193      */
1194     if (tinyLayout) {
1195         layoutName = "tinyLayout";
1196     } else if (smallLayout) {
1197         layoutName = "smallLayout";
1198     } else {
1199         layoutName = "normalLayout";
1200     }
1201
1202     optList = BoardPopUp(squareSize, lineGap, (void*)
1203 #if ENABLE_NLS
1204                                                 &clockFontSet);
1205 #else
1206                                                 clockFontStruct);
1207 #endif
1208     InitDrawingHandle(optList + W_BOARD);
1209     currBoard        = &optList[W_BOARD];
1210     boardWidget      = optList[W_BOARD].handle;
1211     menuBarWidget    = optList[W_MENU].handle;
1212     dropMenu         = optList[W_DROP].handle;
1213     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1214     formWidget  = XtParent(boardWidget);
1215     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1216     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1217     XtGetValues(optList[W_WHITE].handle, args, 2);
1218     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1219       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1220       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1221       XtGetValues(optList[W_PAUSE].handle, args, 2);
1222     }
1223
1224     xBoardWindow = XtWindow(boardWidget);
1225
1226     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1227     //       not need to go into InitDrawingSizes().
1228
1229     /*
1230      * Create X checkmark bitmap and initialize option menu checks.
1231      */
1232     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1233                checkmark_bits, checkmark_width, checkmark_height);
1234     InitMenuMarkers();
1235
1236     /*
1237      * Create an icon.
1238      */
1239     ReadBitmap(&wIconPixmap, "icon_white.bm",
1240                icon_white_bits, icon_white_width, icon_white_height);
1241     ReadBitmap(&bIconPixmap, "icon_black.bm",
1242                icon_black_bits, icon_black_width, icon_black_height);
1243     iconPixmap = wIconPixmap;
1244     i = 0;
1245     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1246     XtSetValues(shellWidget, args, i);
1247
1248     /*
1249      * Create a cursor for the board widget.
1250      */
1251     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1252     XChangeWindowAttributes(xDisplay, xBoardWindow,
1253                             CWCursor, &window_attributes);
1254
1255     /*
1256      * Inhibit shell resizing.
1257      */
1258     shellArgs[0].value = (XtArgVal) &w;
1259     shellArgs[1].value = (XtArgVal) &h;
1260     XtGetValues(shellWidget, shellArgs, 2);
1261     shellArgs[4].value = shellArgs[2].value = w;
1262     shellArgs[5].value = shellArgs[3].value = h;
1263 //    XtSetValues(shellWidget, &shellArgs[2], 4);
1264     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1265     marginH =  h - boardHeight;
1266
1267     CatchDeleteWindow(shellWidget, "QuitProc");
1268
1269     CreateAnyPieces();
1270     CreateGrid();
1271
1272     if(appData.logoSize)
1273     {   // locate and read user logo
1274         char buf[MSG_SIZ];
1275         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1276         ASSIGN(userLogo, buf);
1277     }
1278
1279     if (appData.animate || appData.animateDragging)
1280       CreateAnimVars();
1281
1282
1283     char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1284
1285     XtAugmentTranslations(formWidget,
1286                           XtParseTranslationTable(globalTranslations));
1287     XtAugmentTranslations(formWidget,
1288                           XtParseTranslationTable(TranslationsTableMenus));
1289
1290     XtAddEventHandler(formWidget, KeyPressMask, False,
1291                       (XtEventHandler) MoveTypeInProc, NULL);
1292     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1293                       (XtEventHandler) EventProc, NULL);
1294
1295     /* [AS] Restore layout */
1296     if( wpMoveHistory.visible ) {
1297       HistoryPopUp();
1298     }
1299
1300     if( wpEvalGraph.visible )
1301       {
1302         EvalGraphPopUp();
1303       };
1304
1305     if( wpEngineOutput.visible ) {
1306       EngineOutputPopUp();
1307     }
1308
1309     InitBackEnd2();
1310
1311     if (errorExitStatus == -1) {
1312         if (appData.icsActive) {
1313             /* We now wait until we see "login:" from the ICS before
1314                sending the logon script (problems with timestamp otherwise) */
1315             /*ICSInitScript();*/
1316             if (appData.icsInputBox) ICSInputBoxPopUp();
1317         }
1318
1319     #ifdef SIGWINCH
1320     signal(SIGWINCH, TermSizeSigHandler);
1321     #endif
1322         signal(SIGINT, IntSigHandler);
1323         signal(SIGTERM, IntSigHandler);
1324         if (*appData.cmailGameName != NULLCHAR) {
1325             signal(SIGUSR1, CmailSigHandler);
1326         }
1327     }
1328
1329     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1330     InitPosition(TRUE);
1331     UpdateLogos(TRUE);
1332 //    XtSetKeyboardFocus(shellWidget, formWidget);
1333     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1334
1335     XtAppMainLoop(appContext);
1336     if (appData.debugMode) fclose(debugFP); // [DM] debug
1337     return 0;
1338 }
1339
1340 RETSIGTYPE
1341 TermSizeSigHandler (int sig)
1342 {
1343     update_ics_width();
1344 }
1345
1346 RETSIGTYPE
1347 IntSigHandler (int sig)
1348 {
1349     ExitEvent(sig);
1350 }
1351
1352 RETSIGTYPE
1353 CmailSigHandler (int sig)
1354 {
1355     int dummy = 0;
1356     int error;
1357
1358     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1359
1360     /* Activate call-back function CmailSigHandlerCallBack()             */
1361     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1362
1363     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1364 }
1365
1366 void
1367 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1368 {
1369     BoardToTop();
1370     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1371 }
1372 /**** end signal code ****/
1373
1374
1375 #define Abs(n) ((n)<0 ? -(n) : (n))
1376
1377 #ifdef ENABLE_NLS
1378 char *
1379 InsertPxlSize (char *pattern, int targetPxlSize)
1380 {
1381     char *base_fnt_lst, strInt[12], *p, *q;
1382     int alternatives, i, len, strIntLen;
1383
1384     /*
1385      * Replace the "*" (if present) in the pixel-size slot of each
1386      * alternative with the targetPxlSize.
1387      */
1388     p = pattern;
1389     alternatives = 1;
1390     while ((p = strchr(p, ',')) != NULL) {
1391       alternatives++;
1392       p++;
1393     }
1394     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1395     strIntLen = strlen(strInt);
1396     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1397
1398     p = pattern;
1399     q = base_fnt_lst;
1400     while (alternatives--) {
1401       char *comma = strchr(p, ',');
1402       for (i=0; i<14; i++) {
1403         char *hyphen = strchr(p, '-');
1404         if (!hyphen) break;
1405         if (comma && hyphen > comma) break;
1406         len = hyphen + 1 - p;
1407         if (i == 7 && *p == '*' && len == 2) {
1408           p += len;
1409           memcpy(q, strInt, strIntLen);
1410           q += strIntLen;
1411           *q++ = '-';
1412         } else {
1413           memcpy(q, p, len);
1414           p += len;
1415           q += len;
1416         }
1417       }
1418       if (!comma) break;
1419       len = comma + 1 - p;
1420       memcpy(q, p, len);
1421       p += len;
1422       q += len;
1423     }
1424     strcpy(q, p);
1425
1426     return base_fnt_lst;
1427 }
1428
1429 XFontSet
1430 CreateFontSet (char *base_fnt_lst)
1431 {
1432     XFontSet fntSet;
1433     char **missing_list;
1434     int missing_count;
1435     char *def_string;
1436
1437     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1438                             &missing_list, &missing_count, &def_string);
1439     if (appData.debugMode) {
1440       int i, count;
1441       XFontStruct **font_struct_list;
1442       char **font_name_list;
1443       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1444       if (fntSet) {
1445         fprintf(debugFP, " got list %s, locale %s\n",
1446                 XBaseFontNameListOfFontSet(fntSet),
1447                 XLocaleOfFontSet(fntSet));
1448         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1449         for (i = 0; i < count; i++) {
1450           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1451         }
1452       }
1453       for (i = 0; i < missing_count; i++) {
1454         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1455       }
1456     }
1457     if (fntSet == NULL) {
1458       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1459       exit(2);
1460     }
1461     return fntSet;
1462 }
1463 #else // not ENABLE_NLS
1464 /*
1465  * Find a font that matches "pattern" that is as close as
1466  * possible to the targetPxlSize.  Prefer fonts that are k
1467  * pixels smaller to fonts that are k pixels larger.  The
1468  * pattern must be in the X Consortium standard format,
1469  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1470  * The return value should be freed with XtFree when no
1471  * longer needed.
1472  */
1473 char *
1474 FindFont (char *pattern, int targetPxlSize)
1475 {
1476     char **fonts, *p, *best, *scalable, *scalableTail;
1477     int i, j, nfonts, minerr, err, pxlSize;
1478
1479     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1480     if (nfonts < 1) {
1481         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1482                 programName, pattern);
1483         exit(2);
1484     }
1485
1486     best = fonts[0];
1487     scalable = NULL;
1488     minerr = 999999;
1489     for (i=0; i<nfonts; i++) {
1490         j = 0;
1491         p = fonts[i];
1492         if (*p != '-') continue;
1493         while (j < 7) {
1494             if (*p == NULLCHAR) break;
1495             if (*p++ == '-') j++;
1496         }
1497         if (j < 7) continue;
1498         pxlSize = atoi(p);
1499         if (pxlSize == 0) {
1500             scalable = fonts[i];
1501             scalableTail = p;
1502         } else {
1503             err = pxlSize - targetPxlSize;
1504             if (Abs(err) < Abs(minerr) ||
1505                 (minerr > 0 && err < 0 && -err == minerr)) {
1506                 best = fonts[i];
1507                 minerr = err;
1508             }
1509         }
1510     }
1511     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1512         /* If the error is too big and there is a scalable font,
1513            use the scalable font. */
1514         int headlen = scalableTail - scalable;
1515         p = (char *) XtMalloc(strlen(scalable) + 10);
1516         while (isdigit(*scalableTail)) scalableTail++;
1517         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1518     } else {
1519         p = (char *) XtMalloc(strlen(best) + 2);
1520         safeStrCpy(p, best, strlen(best)+1 );
1521     }
1522     if (appData.debugMode) {
1523         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
1524                 pattern, targetPxlSize, p);
1525     }
1526     XFreeFontNames(fonts);
1527     return p;
1528 }
1529 #endif
1530
1531 void
1532 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1533 {
1534     if (bits != NULL) {
1535         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1536                                     wreq, hreq);
1537     }
1538 }
1539
1540 void
1541 MarkMenuItem (char *menuRef, int state)
1542 {
1543     MenuItem *item = MenuNameToItem(menuRef);
1544
1545     if(item) {
1546         Arg args[2];
1547         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1548         XtSetValues(item->handle, args, 1);
1549     }
1550 }
1551
1552 void
1553 EnableNamedMenuItem (char *menuRef, int state)
1554 {
1555     MenuItem *item = MenuNameToItem(menuRef);
1556
1557     if(item) XtSetSensitive(item->handle, state);
1558 }
1559
1560 void
1561 EnableButtonBar (int state)
1562 {
1563     XtSetSensitive(optList[W_BUTTON].handle, state);
1564 }
1565
1566
1567 void
1568 SetMenuEnables (Enables *enab)
1569 {
1570   while (enab->name != NULL) {
1571     EnableNamedMenuItem(enab->name, enab->value);
1572     enab++;
1573   }
1574 }
1575
1576 void
1577 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1578 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1579     MenuItem *item;
1580     if(*nprms == 0) return;
1581     item = MenuNameToItem(prms[0]);
1582     if(item) ((MenuProc *) item->proc) ();
1583 }
1584
1585 void
1586 SetupDropMenu ()
1587 {
1588     int i, j, count;
1589     char label[32];
1590     Arg args[16];
1591     Widget entry;
1592     char* p;
1593
1594     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1595         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1596         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1597                    dmEnables[i].piece);
1598         XtSetSensitive(entry, p != NULL || !appData.testLegality
1599                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1600                                        && !appData.icsActive));
1601         count = 0;
1602         while (p && *p++ == dmEnables[i].piece) count++;
1603         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1604         j = 0;
1605         XtSetArg(args[j], XtNlabel, label); j++;
1606         XtSetValues(entry, args, j);
1607     }
1608 }
1609
1610 static void
1611 do_flash_delay (unsigned long msec)
1612 {
1613     TimeDelay(msec);
1614 }
1615
1616 void
1617 FlashDelay (int flash_delay)
1618 {
1619         XSync(xDisplay, False);
1620         if(flash_delay) do_flash_delay(flash_delay);
1621 }
1622
1623 double
1624 Fraction (int x, int start, int stop)
1625 {
1626    double f = ((double) x - start)/(stop - start);
1627    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1628    return f;
1629 }
1630
1631 static WindowPlacement wpNew;
1632
1633 void
1634 CoDrag (Widget sh, WindowPlacement *wp)
1635 {
1636     Arg args[16];
1637     int j=0, touch=0, fudge = 2;
1638     GetActualPlacement(sh, wp);
1639     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1640     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1641     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1642     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1643     if(!touch ) return; // only windows that touch co-move
1644     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1645         int heightInc = wpNew.height - wpMain.height;
1646         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1647         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1648         wp->y += fracTop * heightInc;
1649         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1650         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1651     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1652         int widthInc = wpNew.width - wpMain.width;
1653         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1654         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1655         wp->y += fracLeft * widthInc;
1656         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1657         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1658     }
1659     wp->x += wpNew.x - wpMain.x;
1660     wp->y += wpNew.y - wpMain.y;
1661     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1662     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1663     XtSetArg(args[j], XtNx, wp->x); j++;
1664     XtSetArg(args[j], XtNy, wp->y); j++;
1665     XtSetValues(sh, args, j);
1666 }
1667
1668 void
1669 ReSize (WindowPlacement *wp)
1670 {
1671         int sqx, sqy, w, h;
1672         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1673         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1674         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1675         if(sqy < sqx) sqx = sqy;
1676         if(sqx != squareSize) {
1677             squareSize = sqx; // adopt new square size
1678             CreatePNGPieces(); // make newly scaled pieces
1679             InitDrawingSizes(0, 0); // creates grid etc.
1680         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1681         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1682         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1683         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1684         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1685 }
1686
1687 static XtIntervalId delayedDragID = 0;
1688
1689 void
1690 DragProc ()
1691 {
1692         static int busy;
1693         if(busy) return;
1694
1695         busy = 1;
1696         GetActualPlacement(shellWidget, &wpNew);
1697         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1698            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1699             busy = 0; return; // false alarm
1700         }
1701         ReSize(&wpNew);
1702         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1703         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1704         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1705         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1706         wpMain = wpNew;
1707         DrawPosition(True, NULL);
1708         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1709         busy = 0;
1710 }
1711
1712
1713 void
1714 DelayedDrag ()
1715 {
1716     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1717     delayedDragID =
1718       XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1719 }
1720
1721 void
1722 EventProc (Widget widget, caddr_t unused, XEvent *event)
1723 {
1724     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1725         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1726 }
1727
1728 /*
1729  * event handler for redrawing the board
1730  */
1731 void
1732 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1733 {
1734     DrawPosition(True, NULL);
1735 }
1736
1737
1738 void
1739 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1740 {   // [HGM] pv: walk PV
1741     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1742 }
1743
1744 extern int savedIndex;  /* gross that this is global */
1745
1746 void
1747 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1748 {
1749         String val;
1750         XawTextPosition index, dummy;
1751         Arg arg;
1752
1753         XawTextGetSelectionPos(w, &index, &dummy);
1754         XtSetArg(arg, XtNstring, &val);
1755         XtGetValues(w, &arg, 1);
1756         ReplaceComment(savedIndex, val);
1757         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1758         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1759 }
1760
1761
1762 /* Disable all user input other than deleting the window */
1763 static int frozen = 0;
1764
1765 void
1766 FreezeUI ()
1767 {
1768   if (frozen) return;
1769   /* Grab by a widget that doesn't accept input */
1770   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1771   frozen = 1;
1772 }
1773
1774 /* Undo a FreezeUI */
1775 void
1776 ThawUI ()
1777 {
1778   if (!frozen) return;
1779   XtRemoveGrab(optList[W_MESSG].handle);
1780   frozen = 0;
1781 }
1782
1783 void
1784 ModeHighlight ()
1785 {
1786     Arg args[16];
1787     static int oldPausing = FALSE;
1788     static GameMode oldmode = (GameMode) -1;
1789     char *wname;
1790
1791     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1792
1793     if (pausing != oldPausing) {
1794         oldPausing = pausing;
1795         MarkMenuItem("Mode.Pause", pausing);
1796
1797         if (appData.showButtonBar) {
1798           /* Always toggle, don't set.  Previous code messes up when
1799              invoked while the button is pressed, as releasing it
1800              toggles the state again. */
1801           {
1802             Pixel oldbg, oldfg;
1803             XtSetArg(args[0], XtNbackground, &oldbg);
1804             XtSetArg(args[1], XtNforeground, &oldfg);
1805             XtGetValues(optList[W_PAUSE].handle,
1806                         args, 2);
1807             XtSetArg(args[0], XtNbackground, oldfg);
1808             XtSetArg(args[1], XtNforeground, oldbg);
1809           }
1810           XtSetValues(optList[W_PAUSE].handle, args, 2);
1811         }
1812     }
1813
1814     wname = ModeToWidgetName(oldmode);
1815     if (wname != NULL) {
1816         MarkMenuItem(wname, False);
1817     }
1818     wname = ModeToWidgetName(gameMode);
1819     if (wname != NULL) {
1820         MarkMenuItem(wname, True);
1821     }
1822     oldmode = gameMode;
1823     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1824
1825     /* Maybe all the enables should be handled here, not just this one */
1826     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1827
1828     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1829 }
1830
1831
1832 /*
1833  * Button/menu procedures
1834  */
1835
1836 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1837 char *selected_fen_position=NULL;
1838
1839 Boolean
1840 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1841                        Atom *type_return, XtPointer *value_return,
1842                        unsigned long *length_return, int *format_return)
1843 {
1844   char *selection_tmp;
1845
1846 //  if (!selected_fen_position) return False; /* should never happen */
1847   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1848    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1849     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1850     long len;
1851     size_t count;
1852     if (f == NULL) return False;
1853     fseek(f, 0, 2);
1854     len = ftell(f);
1855     rewind(f);
1856     selection_tmp = XtMalloc(len + 1);
1857     count = fread(selection_tmp, 1, len, f);
1858     fclose(f);
1859     if (len != count) {
1860       XtFree(selection_tmp);
1861       return False;
1862     }
1863     selection_tmp[len] = NULLCHAR;
1864    } else {
1865     /* note: since no XtSelectionDoneProc was registered, Xt will
1866      * automatically call XtFree on the value returned.  So have to
1867      * make a copy of it allocated with XtMalloc */
1868     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1869     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1870    }
1871
1872     *value_return=selection_tmp;
1873     *length_return=strlen(selection_tmp);
1874     *type_return=*target;
1875     *format_return = 8; /* bits per byte */
1876     return True;
1877   } else if (*target == XA_TARGETS(xDisplay)) {
1878     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1879     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1880     targets_tmp[1] = XA_STRING;
1881     *value_return = targets_tmp;
1882     *type_return = XA_ATOM;
1883     *length_return = 2;
1884 #if 0
1885     // This code leads to a read of value_return out of bounds on 64-bit systems.
1886     // Other code which I have seen always sets *format_return to 32 independent of
1887     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1888     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1889     *format_return = 8 * sizeof(Atom);
1890     if (*format_return > 32) {
1891       *length_return *= *format_return / 32;
1892       *format_return = 32;
1893     }
1894 #else
1895     *format_return = 32;
1896 #endif
1897     return True;
1898   } else {
1899     return False;
1900   }
1901 }
1902
1903 /* note: when called from menu all parameters are NULL, so no clue what the
1904  * Widget which was clicked on was, or what the click event was
1905  */
1906 void
1907 CopySomething (char *src)
1908 {
1909     selected_fen_position = src;
1910     /*
1911      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1912      * have a notion of a position that is selected but not copied.
1913      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1914      */
1915     XtOwnSelection(menuBarWidget, XA_PRIMARY,
1916                    CurrentTime,
1917                    SendPositionSelection,
1918                    NULL/* lose_ownership_proc */ ,
1919                    NULL/* transfer_done_proc */);
1920     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1921                    CurrentTime,
1922                    SendPositionSelection,
1923                    NULL/* lose_ownership_proc */ ,
1924                    NULL/* transfer_done_proc */);
1925 }
1926
1927 /* function called when the data to Paste is ready */
1928 static void
1929 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
1930                  Atom *type, XtPointer value, unsigned long *len, int *format)
1931 {
1932   char *fenstr=value;
1933   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
1934   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
1935   EditPositionPasteFEN(fenstr);
1936   XtFree(value);
1937 }
1938
1939 /* called when Paste Position button is pressed,
1940  * all parameters will be NULL */
1941 void
1942 PastePositionProc ()
1943 {
1944     XtGetSelectionValue(menuBarWidget,
1945       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1946       /* (XtSelectionCallbackProc) */ PastePositionCB,
1947       NULL, /* client_data passed to PastePositionCB */
1948
1949       /* better to use the time field from the event that triggered the
1950        * call to this function, but that isn't trivial to get
1951        */
1952       CurrentTime
1953     );
1954     return;
1955 }
1956
1957 /* note: when called from menu all parameters are NULL, so no clue what the
1958  * Widget which was clicked on was, or what the click event was
1959  */
1960 /* function called when the data to Paste is ready */
1961 static void
1962 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
1963              Atom *type, XtPointer value, unsigned long *len, int *format)
1964 {
1965   FILE* f;
1966   if (value == NULL || *len == 0) {
1967     return; /* nothing had been selected to copy */
1968   }
1969   f = fopen(gamePasteFilename, "w");
1970   if (f == NULL) {
1971     DisplayError(_("Can't open temp file"), errno);
1972     return;
1973   }
1974   fwrite(value, 1, *len, f);
1975   fclose(f);
1976   XtFree(value);
1977   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1978 }
1979
1980 /* called when Paste Game button is pressed,
1981  * all parameters will be NULL */
1982 void
1983 PasteGameProc ()
1984 {
1985     XtGetSelectionValue(menuBarWidget,
1986       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1987       /* (XtSelectionCallbackProc) */ PasteGameCB,
1988       NULL, /* client_data passed to PasteGameCB */
1989
1990       /* better to use the time field from the event that triggered the
1991        * call to this function, but that isn't trivial to get
1992        */
1993       CurrentTime
1994     );
1995     return;
1996 }
1997
1998
1999 void
2000 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2001 {
2002     QuitProc();
2003 }
2004
2005 int
2006 ShiftKeys ()
2007 {   // bassic primitive for determining if modifier keys are pressed
2008     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2009     char keys[32];
2010     int i,j,  k=0;
2011     XQueryKeymap(xDisplay,keys);
2012     for(i=0; i<6; i++) {
2013         k <<= 1;
2014         j = XKeysymToKeycode(xDisplay, codes[i]);
2015         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2016     }
2017     return k;
2018 }
2019
2020 static void
2021 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2022 {
2023     char buf[10];
2024     KeySym sym;
2025     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2026     if ( n == 1 && *buf >= 32 // printable
2027          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2028         ) BoxAutoPopUp (buf);
2029 }
2030
2031 static void
2032 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2033 {   // [HGM] input: let up-arrow recall previous line from history
2034     IcsKey(1);
2035 }
2036
2037 static void
2038 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2039 {   // [HGM] input: let down-arrow recall next line from history
2040     IcsKey(-1);
2041 }
2042
2043 static void
2044 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2045 {
2046     IcsKey(0);
2047 }
2048
2049 void
2050 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2051 {
2052         if (!TempBackwardActive) {
2053                 TempBackwardActive = True;
2054                 BackwardEvent();
2055         }
2056 }
2057
2058 void
2059 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2060 {
2061         /* Check to see if triggered by a key release event for a repeating key.
2062          * If so the next queued event will be a key press of the same key at the same time */
2063         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2064                 XEvent next;
2065                 XPeekEvent(xDisplay, &next);
2066                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2067                         next.xkey.keycode == event->xkey.keycode)
2068                                 return;
2069         }
2070     ForwardEvent();
2071         TempBackwardActive = False;
2072 }
2073
2074 void
2075 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2076 {   // called as key binding
2077     char buf[MSG_SIZ];
2078     String name;
2079     if (nprms && *nprms > 0)
2080       name = prms[0];
2081     else
2082       name = "xboard";
2083     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2084     system(buf);
2085 }
2086
2087 void
2088 ManProc ()
2089 {   // called from menu
2090     ManInner(NULL, NULL, NULL, NULL);
2091 }
2092
2093 void
2094 SetWindowTitle (char *text, char *title, char *icon)
2095 {
2096     Arg args[16];
2097     int i;
2098     if (appData.titleInWindow) {
2099         i = 0;
2100         XtSetArg(args[i], XtNlabel, text);   i++;
2101         XtSetValues(titleWidget, args, i);
2102     }
2103     i = 0;
2104     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2105     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2106     XtSetValues(shellWidget, args, i);
2107     XSync(xDisplay, False);
2108 }
2109
2110
2111 static int
2112 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2113 {
2114     return 0;
2115 }
2116
2117 void
2118 DisplayIcsInteractionTitle (String message)
2119 {
2120   if (oldICSInteractionTitle == NULL) {
2121     /* Magic to find the old window title, adapted from vim */
2122     char *wina = getenv("WINDOWID");
2123     if (wina != NULL) {
2124       Window win = (Window) atoi(wina);
2125       Window root, parent, *children;
2126       unsigned int nchildren;
2127       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2128       for (;;) {
2129         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2130         if (!XQueryTree(xDisplay, win, &root, &parent,
2131                         &children, &nchildren)) break;
2132         if (children) XFree((void *)children);
2133         if (parent == root || parent == 0) break;
2134         win = parent;
2135       }
2136       XSetErrorHandler(oldHandler);
2137     }
2138     if (oldICSInteractionTitle == NULL) {
2139       oldICSInteractionTitle = "xterm";
2140     }
2141   }
2142   printf("\033]0;%s\007", message);
2143   fflush(stdout);
2144 }
2145
2146
2147 XtIntervalId delayedEventTimerXID = 0;
2148 DelayedEventCallback delayedEventCallback = 0;
2149
2150 void
2151 FireDelayedEvent ()
2152 {
2153     delayedEventTimerXID = 0;
2154     delayedEventCallback();
2155 }
2156
2157 void
2158 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2159 {
2160     if(delayedEventTimerXID && delayedEventCallback == cb)
2161         // [HGM] alive: replace, rather than add or flush identical event
2162         XtRemoveTimeOut(delayedEventTimerXID);
2163     delayedEventCallback = cb;
2164     delayedEventTimerXID =
2165       XtAppAddTimeOut(appContext, millisec,
2166                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2167 }
2168
2169 DelayedEventCallback
2170 GetDelayedEvent ()
2171 {
2172   if (delayedEventTimerXID) {
2173     return delayedEventCallback;
2174   } else {
2175     return NULL;
2176   }
2177 }
2178
2179 void
2180 CancelDelayedEvent ()
2181 {
2182   if (delayedEventTimerXID) {
2183     XtRemoveTimeOut(delayedEventTimerXID);
2184     delayedEventTimerXID = 0;
2185   }
2186 }
2187
2188 XtIntervalId loadGameTimerXID = 0;
2189
2190 int
2191 LoadGameTimerRunning ()
2192 {
2193     return loadGameTimerXID != 0;
2194 }
2195
2196 int
2197 StopLoadGameTimer ()
2198 {
2199     if (loadGameTimerXID != 0) {
2200         XtRemoveTimeOut(loadGameTimerXID);
2201         loadGameTimerXID = 0;
2202         return TRUE;
2203     } else {
2204         return FALSE;
2205     }
2206 }
2207
2208 void
2209 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2210 {
2211     loadGameTimerXID = 0;
2212     AutoPlayGameLoop();
2213 }
2214
2215 void
2216 StartLoadGameTimer (long millisec)
2217 {
2218     loadGameTimerXID =
2219       XtAppAddTimeOut(appContext, millisec,
2220                       (XtTimerCallbackProc) LoadGameTimerCallback,
2221                       (XtPointer) 0);
2222 }
2223
2224 XtIntervalId analysisClockXID = 0;
2225
2226 void
2227 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2228 {
2229     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2230          || appData.icsEngineAnalyze) { // [DM]
2231         AnalysisPeriodicEvent(0);
2232         StartAnalysisClock();
2233     }
2234 }
2235
2236 void
2237 StartAnalysisClock ()
2238 {
2239     analysisClockXID =
2240       XtAppAddTimeOut(appContext, 2000,
2241                       (XtTimerCallbackProc) AnalysisClockCallback,
2242                       (XtPointer) 0);
2243 }
2244
2245 XtIntervalId clockTimerXID = 0;
2246
2247 int
2248 ClockTimerRunning ()
2249 {
2250     return clockTimerXID != 0;
2251 }
2252
2253 int
2254 StopClockTimer ()
2255 {
2256     if (clockTimerXID != 0) {
2257         XtRemoveTimeOut(clockTimerXID);
2258         clockTimerXID = 0;
2259         return TRUE;
2260     } else {
2261         return FALSE;
2262     }
2263 }
2264
2265 void
2266 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2267 {
2268     clockTimerXID = 0;
2269     DecrementClocks();
2270 }
2271
2272 void
2273 StartClockTimer (long millisec)
2274 {
2275     clockTimerXID =
2276       XtAppAddTimeOut(appContext, millisec,
2277                       (XtTimerCallbackProc) ClockTimerCallback,
2278                       (XtPointer) 0);
2279 }
2280
2281 void
2282 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2283 {
2284     char buf[MSG_SIZ];
2285     Arg args[16];
2286     Widget w = (Widget) opt->handle;
2287
2288     /* check for low time warning */
2289     Pixel foregroundOrWarningColor = timerForegroundPixel;
2290
2291     if (timer > 0 &&
2292         appData.lowTimeWarning &&
2293         (timer / 1000) < appData.icsAlarmTime)
2294       foregroundOrWarningColor = lowTimeWarningColor;
2295
2296     if (appData.clockMode) {
2297       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2298       XtSetArg(args[0], XtNlabel, buf);
2299     } else {
2300       snprintf(buf, MSG_SIZ, "%s  ", color);
2301       XtSetArg(args[0], XtNlabel, buf);
2302     }
2303
2304     if (highlight) {
2305
2306         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2307         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2308     } else {
2309         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2310         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2311     }
2312
2313     XtSetValues(w, args, 3);
2314 }
2315
2316 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2317
2318 void
2319 SetClockIcon (int color)
2320 {
2321     Arg args[16];
2322     Pixmap pm = *clockIcons[color];
2323     if (iconPixmap != pm) {
2324         iconPixmap = pm;
2325         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2326         XtSetValues(shellWidget, args, 1);
2327     }
2328 }
2329
2330 #define INPUT_SOURCE_BUF_SIZE 8192
2331
2332 typedef struct {
2333     CPKind kind;
2334     int fd;
2335     int lineByLine;
2336     char *unused;
2337     InputCallback func;
2338     XtInputId xid;
2339     char buf[INPUT_SOURCE_BUF_SIZE];
2340     VOIDSTAR closure;
2341 } InputSource;
2342
2343 void
2344 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2345 {
2346     InputSource *is = (InputSource *) closure;
2347     int count;
2348     int error;
2349     char *p, *q;
2350
2351     if (is->lineByLine) {
2352         count = read(is->fd, is->unused,
2353                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2354         if (count <= 0) {
2355             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2356             return;
2357         }
2358         is->unused += count;
2359         p = is->buf;
2360         while (p < is->unused) {
2361             q = memchr(p, '\n', is->unused - p);
2362             if (q == NULL) break;
2363             q++;
2364             (is->func)(is, is->closure, p, q - p, 0);
2365             p = q;
2366         }
2367         q = is->buf;
2368         while (p < is->unused) {
2369             *q++ = *p++;
2370         }
2371         is->unused = q;
2372     } else {
2373         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2374         if (count == -1)
2375           error = errno;
2376         else
2377           error = 0;
2378         (is->func)(is, is->closure, is->buf, count, error);
2379     }
2380 }
2381
2382 InputSourceRef
2383 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2384 {
2385     InputSource *is;
2386     ChildProc *cp = (ChildProc *) pr;
2387
2388     is = (InputSource *) calloc(1, sizeof(InputSource));
2389     is->lineByLine = lineByLine;
2390     is->func = func;
2391     if (pr == NoProc) {
2392         is->kind = CPReal;
2393         is->fd = fileno(stdin);
2394     } else {
2395         is->kind = cp->kind;
2396         is->fd = cp->fdFrom;
2397     }
2398     if (lineByLine) {
2399         is->unused = is->buf;
2400     }
2401
2402     is->xid = XtAppAddInput(appContext, is->fd,
2403                             (XtPointer) (XtInputReadMask),
2404                             (XtInputCallbackProc) DoInputCallback,
2405                             (XtPointer) is);
2406     is->closure = closure;
2407     return (InputSourceRef) is;
2408 }
2409
2410 void
2411 RemoveInputSource (InputSourceRef isr)
2412 {
2413     InputSource *is = (InputSource *) isr;
2414
2415     if (is->xid == 0) return;
2416     XtRemoveInput(is->xid);
2417     is->xid = 0;
2418 }
2419
2420 #ifndef HAVE_USLEEP
2421
2422 static Boolean frameWaiting;
2423
2424 static RETSIGTYPE
2425 FrameAlarm (int sig)
2426 {
2427   frameWaiting = False;
2428   /* In case System-V style signals.  Needed?? */
2429   signal(SIGALRM, FrameAlarm);
2430 }
2431
2432 void
2433 FrameDelay (int time)
2434 {
2435   struct itimerval delay;
2436
2437   XSync(xDisplay, False);
2438
2439   if (time > 0) {
2440     frameWaiting = True;
2441     signal(SIGALRM, FrameAlarm);
2442     delay.it_interval.tv_sec =
2443       delay.it_value.tv_sec = time / 1000;
2444     delay.it_interval.tv_usec =
2445       delay.it_value.tv_usec = (time % 1000) * 1000;
2446     setitimer(ITIMER_REAL, &delay, NULL);
2447     while (frameWaiting) pause();
2448     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2449     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2450     setitimer(ITIMER_REAL, &delay, NULL);
2451   }
2452 }
2453
2454 #else
2455
2456 void
2457 FrameDelay (int time)
2458 {
2459   XSync(xDisplay, False);
2460   if (time > 0)
2461     usleep(time * 1000);
2462 }
2463
2464 #endif
2465
2466 static void
2467 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2468 {
2469     char buf[MSG_SIZ], *logoName = buf;
2470     if(appData.logo[n][0]) {
2471         logoName = appData.logo[n];
2472     } else if(appData.autoLogo) {
2473         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2474             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2475         } else if(appData.directory[n] && appData.directory[n][0]) {
2476             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2477         }
2478     }
2479     if(logoName[0])
2480         { ASSIGN(cps->programLogo, logoName); }
2481 }
2482
2483 void
2484 UpdateLogos (int displ)
2485 {
2486     if(optList[W_WHITE-1].handle == NULL) return;
2487     LoadLogo(&first, 0, 0);
2488     LoadLogo(&second, 1, appData.icsActive);
2489     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2490     return;
2491 }
2492