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