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