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