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