Preserve window width on board-format change
[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     InitBackEnd2();
1324
1325     if (errorExitStatus == -1) {
1326         if (appData.icsActive) {
1327             /* We now wait until we see "login:" from the ICS before
1328                sending the logon script (problems with timestamp otherwise) */
1329             /*ICSInitScript();*/
1330             if (appData.icsInputBox) ICSInputBoxPopUp();
1331         }
1332
1333     #ifdef SIGWINCH
1334     signal(SIGWINCH, TermSizeSigHandler);
1335     #endif
1336         signal(SIGINT, IntSigHandler);
1337         signal(SIGTERM, IntSigHandler);
1338         if (*appData.cmailGameName != NULLCHAR) {
1339             signal(SIGUSR1, CmailSigHandler);
1340         }
1341     }
1342
1343     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1344     InitPosition(TRUE);
1345     UpdateLogos(TRUE);
1346 //    XtSetKeyboardFocus(shellWidget, formWidget);
1347     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1348
1349     XtAppMainLoop(appContext);
1350     if (appData.debugMode) fclose(debugFP); // [DM] debug
1351     return 0;
1352 }
1353
1354 RETSIGTYPE
1355 TermSizeSigHandler (int sig)
1356 {
1357     update_ics_width();
1358 }
1359
1360 RETSIGTYPE
1361 IntSigHandler (int sig)
1362 {
1363     ExitEvent(sig);
1364 }
1365
1366 RETSIGTYPE
1367 CmailSigHandler (int sig)
1368 {
1369     int dummy = 0;
1370     int error;
1371
1372     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1373
1374     /* Activate call-back function CmailSigHandlerCallBack()             */
1375     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1376
1377     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1378 }
1379
1380 void
1381 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1382 {
1383     BoardToTop();
1384     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1385 }
1386 /**** end signal code ****/
1387
1388
1389 #define Abs(n) ((n)<0 ? -(n) : (n))
1390
1391 #ifdef ENABLE_NLS
1392 char *
1393 InsertPxlSize (char *pattern, int targetPxlSize)
1394 {
1395     char *base_fnt_lst, strInt[12], *p, *q;
1396     int alternatives, i, len, strIntLen;
1397
1398     /*
1399      * Replace the "*" (if present) in the pixel-size slot of each
1400      * alternative with the targetPxlSize.
1401      */
1402     p = pattern;
1403     alternatives = 1;
1404     while ((p = strchr(p, ',')) != NULL) {
1405       alternatives++;
1406       p++;
1407     }
1408     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1409     strIntLen = strlen(strInt);
1410     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1411
1412     p = pattern;
1413     q = base_fnt_lst;
1414     while (alternatives--) {
1415       char *comma = strchr(p, ',');
1416       for (i=0; i<14; i++) {
1417         char *hyphen = strchr(p, '-');
1418         if (!hyphen) break;
1419         if (comma && hyphen > comma) break;
1420         len = hyphen + 1 - p;
1421         if (i == 7 && *p == '*' && len == 2) {
1422           p += len;
1423           memcpy(q, strInt, strIntLen);
1424           q += strIntLen;
1425           *q++ = '-';
1426         } else {
1427           memcpy(q, p, len);
1428           p += len;
1429           q += len;
1430         }
1431       }
1432       if (!comma) break;
1433       len = comma + 1 - p;
1434       memcpy(q, p, len);
1435       p += len;
1436       q += len;
1437     }
1438     strcpy(q, p);
1439
1440     return base_fnt_lst;
1441 }
1442
1443 XFontSet
1444 CreateFontSet (char *base_fnt_lst)
1445 {
1446     XFontSet fntSet;
1447     char **missing_list;
1448     int missing_count;
1449     char *def_string;
1450
1451     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1452                             &missing_list, &missing_count, &def_string);
1453     if (appData.debugMode) {
1454       int i, count;
1455       XFontStruct **font_struct_list;
1456       char **font_name_list;
1457       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1458       if (fntSet) {
1459         fprintf(debugFP, " got list %s, locale %s\n",
1460                 XBaseFontNameListOfFontSet(fntSet),
1461                 XLocaleOfFontSet(fntSet));
1462         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1463         for (i = 0; i < count; i++) {
1464           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1465         }
1466       }
1467       for (i = 0; i < missing_count; i++) {
1468         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1469       }
1470     }
1471     if (fntSet == NULL) {
1472       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1473       exit(2);
1474     }
1475     return fntSet;
1476 }
1477 #else // not ENABLE_NLS
1478 /*
1479  * Find a font that matches "pattern" that is as close as
1480  * possible to the targetPxlSize.  Prefer fonts that are k
1481  * pixels smaller to fonts that are k pixels larger.  The
1482  * pattern must be in the X Consortium standard format,
1483  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1484  * The return value should be freed with XtFree when no
1485  * longer needed.
1486  */
1487 char *
1488 FindFont (char *pattern, int targetPxlSize)
1489 {
1490     char **fonts, *p, *best, *scalable, *scalableTail;
1491     int i, j, nfonts, minerr, err, pxlSize;
1492
1493     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1494     if (nfonts < 1) {
1495         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1496                 programName, pattern);
1497         exit(2);
1498     }
1499
1500     best = fonts[0];
1501     scalable = NULL;
1502     minerr = 999999;
1503     for (i=0; i<nfonts; i++) {
1504         j = 0;
1505         p = fonts[i];
1506         if (*p != '-') continue;
1507         while (j < 7) {
1508             if (*p == NULLCHAR) break;
1509             if (*p++ == '-') j++;
1510         }
1511         if (j < 7) continue;
1512         pxlSize = atoi(p);
1513         if (pxlSize == 0) {
1514             scalable = fonts[i];
1515             scalableTail = p;
1516         } else {
1517             err = pxlSize - targetPxlSize;
1518             if (Abs(err) < Abs(minerr) ||
1519                 (minerr > 0 && err < 0 && -err == minerr)) {
1520                 best = fonts[i];
1521                 minerr = err;
1522             }
1523         }
1524     }
1525     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1526         /* If the error is too big and there is a scalable font,
1527            use the scalable font. */
1528         int headlen = scalableTail - scalable;
1529         p = (char *) XtMalloc(strlen(scalable) + 10);
1530         while (isdigit(*scalableTail)) scalableTail++;
1531         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1532     } else {
1533         p = (char *) XtMalloc(strlen(best) + 2);
1534         safeStrCpy(p, best, strlen(best)+1 );
1535     }
1536     if (appData.debugMode) {
1537         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
1538                 pattern, targetPxlSize, p);
1539     }
1540     XFreeFontNames(fonts);
1541     return p;
1542 }
1543 #endif
1544
1545 void
1546 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1547 {
1548     if (bits != NULL) {
1549         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1550                                     wreq, hreq);
1551     }
1552 }
1553
1554 void
1555 MarkMenuItem (char *menuRef, int state)
1556 {
1557     MenuItem *item = MenuNameToItem(menuRef);
1558
1559     if(item) {
1560         Arg args[2];
1561         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1562         XtSetValues(item->handle, args, 1);
1563     }
1564 }
1565
1566 void
1567 EnableNamedMenuItem (char *menuRef, int state)
1568 {
1569     MenuItem *item = MenuNameToItem(menuRef);
1570
1571     if(item) XtSetSensitive(item->handle, state);
1572 }
1573
1574 void
1575 EnableButtonBar (int state)
1576 {
1577     XtSetSensitive(optList[W_BUTTON].handle, state);
1578 }
1579
1580
1581 void
1582 SetMenuEnables (Enables *enab)
1583 {
1584   while (enab->name != NULL) {
1585     EnableNamedMenuItem(enab->name, enab->value);
1586     enab++;
1587   }
1588 }
1589
1590 void
1591 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1592 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1593     MenuItem *item;
1594     if(*nprms == 0) return;
1595     item = MenuNameToItem(prms[0]);
1596     if(item) ((MenuProc *) item->proc) ();
1597 }
1598
1599 void
1600 SetupDropMenu ()
1601 {
1602     int i, j, count;
1603     char label[32];
1604     Arg args[16];
1605     Widget entry;
1606     char* p;
1607
1608     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1609         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1610         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1611                    dmEnables[i].piece);
1612         XtSetSensitive(entry, p != NULL || !appData.testLegality
1613                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1614                                        && !appData.icsActive));
1615         count = 0;
1616         while (p && *p++ == dmEnables[i].piece) count++;
1617         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1618         j = 0;
1619         XtSetArg(args[j], XtNlabel, label); j++;
1620         XtSetValues(entry, args, j);
1621     }
1622 }
1623
1624 static void
1625 do_flash_delay (unsigned long msec)
1626 {
1627     TimeDelay(msec);
1628 }
1629
1630 void
1631 FlashDelay (int flash_delay)
1632 {
1633         XSync(xDisplay, False);
1634         if(flash_delay) do_flash_delay(flash_delay);
1635 }
1636
1637 double
1638 Fraction (int x, int start, int stop)
1639 {
1640    double f = ((double) x - start)/(stop - start);
1641    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1642    return f;
1643 }
1644
1645 static WindowPlacement wpNew;
1646
1647 void
1648 CoDrag (Widget sh, WindowPlacement *wp)
1649 {
1650     Arg args[16];
1651     int j=0, touch=0, fudge = 2;
1652     GetActualPlacement(sh, wp);
1653     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1654     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1655     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1656     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1657     if(!touch ) return; // only windows that touch co-move
1658     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1659         int heightInc = wpNew.height - wpMain.height;
1660         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1661         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1662         wp->y += fracTop * heightInc;
1663         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1664         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1665     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1666         int widthInc = wpNew.width - wpMain.width;
1667         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1668         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1669         wp->y += fracLeft * widthInc;
1670         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1671         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1672     }
1673     wp->x += wpNew.x - wpMain.x;
1674     wp->y += wpNew.y - wpMain.y;
1675     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1676     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1677     XtSetArg(args[j], XtNx, wp->x); j++;
1678     XtSetArg(args[j], XtNy, wp->y); j++;
1679     XtSetValues(sh, args, j);
1680 }
1681
1682 void
1683 ReSize (WindowPlacement *wp)
1684 {
1685         int sqx, sqy, w, h;
1686         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1687         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1688         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1689         if(sqy < sqx) sqx = sqy;
1690         if(sqx != squareSize) {
1691             squareSize = sqx; // adopt new square size
1692             CreatePNGPieces(); // make newly scaled pieces
1693             InitDrawingSizes(0, 0); // creates grid etc.
1694         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1695         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1696         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1697         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1698         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1699 }
1700
1701 static XtIntervalId delayedDragID = 0;
1702
1703 void
1704 DragProc ()
1705 {
1706         static int busy;
1707         if(busy) return;
1708
1709         busy = 1;
1710         GetActualPlacement(shellWidget, &wpNew);
1711         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1712            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1713             busy = 0; return; // false alarm
1714         }
1715         ReSize(&wpNew);
1716         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1717         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1718         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1719         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1720         wpMain = wpNew;
1721         DrawPosition(True, NULL);
1722         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1723         busy = 0;
1724 }
1725
1726
1727 void
1728 DelayedDrag ()
1729 {
1730     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1731     delayedDragID =
1732       XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1733 }
1734
1735 void
1736 EventProc (Widget widget, caddr_t unused, XEvent *event)
1737 {
1738     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1739         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1740 }
1741
1742 /*
1743  * event handler for redrawing the board
1744  */
1745 void
1746 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1747 {
1748     DrawPosition(True, NULL);
1749 }
1750
1751
1752 void
1753 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1754 {   // [HGM] pv: walk PV
1755     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1756 }
1757
1758 extern int savedIndex;  /* gross that this is global */
1759
1760 void
1761 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1762 {
1763         String val;
1764         XawTextPosition index, dummy;
1765         Arg arg;
1766
1767         XawTextGetSelectionPos(w, &index, &dummy);
1768         XtSetArg(arg, XtNstring, &val);
1769         XtGetValues(w, &arg, 1);
1770         ReplaceComment(savedIndex, val);
1771         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1772         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1773 }
1774
1775
1776 /* Disable all user input other than deleting the window */
1777 static int frozen = 0;
1778
1779 void
1780 FreezeUI ()
1781 {
1782   if (frozen) return;
1783   /* Grab by a widget that doesn't accept input */
1784   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1785   frozen = 1;
1786 }
1787
1788 /* Undo a FreezeUI */
1789 void
1790 ThawUI ()
1791 {
1792   if (!frozen) return;
1793   XtRemoveGrab(optList[W_MESSG].handle);
1794   frozen = 0;
1795 }
1796
1797 void
1798 ModeHighlight ()
1799 {
1800     Arg args[16];
1801     static int oldPausing = FALSE;
1802     static GameMode oldmode = (GameMode) -1;
1803     char *wname;
1804
1805     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1806
1807     if (pausing != oldPausing) {
1808         oldPausing = pausing;
1809         MarkMenuItem("Mode.Pause", pausing);
1810
1811         if (appData.showButtonBar) {
1812           /* Always toggle, don't set.  Previous code messes up when
1813              invoked while the button is pressed, as releasing it
1814              toggles the state again. */
1815           {
1816             Pixel oldbg, oldfg;
1817             XtSetArg(args[0], XtNbackground, &oldbg);
1818             XtSetArg(args[1], XtNforeground, &oldfg);
1819             XtGetValues(optList[W_PAUSE].handle,
1820                         args, 2);
1821             XtSetArg(args[0], XtNbackground, oldfg);
1822             XtSetArg(args[1], XtNforeground, oldbg);
1823           }
1824           XtSetValues(optList[W_PAUSE].handle, args, 2);
1825         }
1826     }
1827
1828     wname = ModeToWidgetName(oldmode);
1829     if (wname != NULL) {
1830         MarkMenuItem(wname, False);
1831     }
1832     wname = ModeToWidgetName(gameMode);
1833     if (wname != NULL) {
1834         MarkMenuItem(wname, True);
1835     }
1836     oldmode = gameMode;
1837     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1838
1839     /* Maybe all the enables should be handled here, not just this one */
1840     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1841
1842     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1843 }
1844
1845
1846 /*
1847  * Button/menu procedures
1848  */
1849
1850 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1851 char *selected_fen_position=NULL;
1852
1853 Boolean
1854 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1855                        Atom *type_return, XtPointer *value_return,
1856                        unsigned long *length_return, int *format_return)
1857 {
1858   char *selection_tmp;
1859
1860 //  if (!selected_fen_position) return False; /* should never happen */
1861   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1862    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1863     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1864     long len;
1865     size_t count;
1866     if (f == NULL) return False;
1867     fseek(f, 0, 2);
1868     len = ftell(f);
1869     rewind(f);
1870     selection_tmp = XtMalloc(len + 1);
1871     count = fread(selection_tmp, 1, len, f);
1872     fclose(f);
1873     if (len != count) {
1874       XtFree(selection_tmp);
1875       return False;
1876     }
1877     selection_tmp[len] = NULLCHAR;
1878    } else {
1879     /* note: since no XtSelectionDoneProc was registered, Xt will
1880      * automatically call XtFree on the value returned.  So have to
1881      * make a copy of it allocated with XtMalloc */
1882     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1883     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1884    }
1885
1886     *value_return=selection_tmp;
1887     *length_return=strlen(selection_tmp);
1888     *type_return=*target;
1889     *format_return = 8; /* bits per byte */
1890     return True;
1891   } else if (*target == XA_TARGETS(xDisplay)) {
1892     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1893     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1894     targets_tmp[1] = XA_STRING;
1895     *value_return = targets_tmp;
1896     *type_return = XA_ATOM;
1897     *length_return = 2;
1898 #if 0
1899     // This code leads to a read of value_return out of bounds on 64-bit systems.
1900     // Other code which I have seen always sets *format_return to 32 independent of
1901     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1902     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1903     *format_return = 8 * sizeof(Atom);
1904     if (*format_return > 32) {
1905       *length_return *= *format_return / 32;
1906       *format_return = 32;
1907     }
1908 #else
1909     *format_return = 32;
1910 #endif
1911     return True;
1912   } else {
1913     return False;
1914   }
1915 }
1916
1917 /* note: when called from menu all parameters are NULL, so no clue what the
1918  * Widget which was clicked on was, or what the click event was
1919  */
1920 void
1921 CopySomething (char *src)
1922 {
1923     selected_fen_position = src;
1924     /*
1925      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1926      * have a notion of a position that is selected but not copied.
1927      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1928      */
1929     XtOwnSelection(menuBarWidget, XA_PRIMARY,
1930                    CurrentTime,
1931                    SendPositionSelection,
1932                    NULL/* lose_ownership_proc */ ,
1933                    NULL/* transfer_done_proc */);
1934     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1935                    CurrentTime,
1936                    SendPositionSelection,
1937                    NULL/* lose_ownership_proc */ ,
1938                    NULL/* transfer_done_proc */);
1939 }
1940
1941 /* function called when the data to Paste is ready */
1942 static void
1943 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
1944                  Atom *type, XtPointer value, unsigned long *len, int *format)
1945 {
1946   char *fenstr=value;
1947   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
1948   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
1949   EditPositionPasteFEN(fenstr);
1950   XtFree(value);
1951 }
1952
1953 /* called when Paste Position button is pressed,
1954  * all parameters will be NULL */
1955 void
1956 PastePositionProc ()
1957 {
1958     XtGetSelectionValue(menuBarWidget,
1959       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1960       /* (XtSelectionCallbackProc) */ PastePositionCB,
1961       NULL, /* client_data passed to PastePositionCB */
1962
1963       /* better to use the time field from the event that triggered the
1964        * call to this function, but that isn't trivial to get
1965        */
1966       CurrentTime
1967     );
1968     return;
1969 }
1970
1971 /* note: when called from menu all parameters are NULL, so no clue what the
1972  * Widget which was clicked on was, or what the click event was
1973  */
1974 /* function called when the data to Paste is ready */
1975 static void
1976 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
1977              Atom *type, XtPointer value, unsigned long *len, int *format)
1978 {
1979   FILE* f;
1980   if (value == NULL || *len == 0) {
1981     return; /* nothing had been selected to copy */
1982   }
1983   f = fopen(gamePasteFilename, "w");
1984   if (f == NULL) {
1985     DisplayError(_("Can't open temp file"), errno);
1986     return;
1987   }
1988   fwrite(value, 1, *len, f);
1989   fclose(f);
1990   XtFree(value);
1991   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1992 }
1993
1994 /* called when Paste Game button is pressed,
1995  * all parameters will be NULL */
1996 void
1997 PasteGameProc ()
1998 {
1999     XtGetSelectionValue(menuBarWidget,
2000       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
2001       /* (XtSelectionCallbackProc) */ PasteGameCB,
2002       NULL, /* client_data passed to PasteGameCB */
2003
2004       /* better to use the time field from the event that triggered the
2005        * call to this function, but that isn't trivial to get
2006        */
2007       CurrentTime
2008     );
2009     return;
2010 }
2011
2012
2013 void
2014 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2015 {
2016     QuitProc();
2017 }
2018
2019 int
2020 ShiftKeys ()
2021 {   // bassic primitive for determining if modifier keys are pressed
2022     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
2023     char keys[32];
2024     int i,j,  k=0;
2025     XQueryKeymap(xDisplay,keys);
2026     for(i=0; i<6; i++) {
2027         k <<= 1;
2028         j = XKeysymToKeycode(xDisplay, codes[i]);
2029         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
2030     }
2031     return k;
2032 }
2033
2034 static void
2035 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2036 {
2037     char buf[10];
2038     KeySym sym;
2039     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2040     if ( n == 1 && *buf >= 32 // printable
2041          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2042         ) BoxAutoPopUp (buf);
2043 }
2044
2045 static void
2046 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2047 {   // [HGM] input: let up-arrow recall previous line from history
2048     IcsKey(1);
2049 }
2050
2051 static void
2052 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2053 {   // [HGM] input: let down-arrow recall next line from history
2054     IcsKey(-1);
2055 }
2056
2057 static void
2058 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2059 {
2060     IcsKey(0);
2061 }
2062
2063 void
2064 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2065 {
2066         if (!TempBackwardActive) {
2067                 TempBackwardActive = True;
2068                 BackwardEvent();
2069         }
2070 }
2071
2072 void
2073 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2074 {
2075         /* Check to see if triggered by a key release event for a repeating key.
2076          * If so the next queued event will be a key press of the same key at the same time */
2077         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2078                 XEvent next;
2079                 XPeekEvent(xDisplay, &next);
2080                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2081                         next.xkey.keycode == event->xkey.keycode)
2082                                 return;
2083         }
2084     ForwardEvent();
2085         TempBackwardActive = False;
2086 }
2087
2088 void
2089 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2090 {   // called as key binding
2091     char buf[MSG_SIZ];
2092     String name;
2093     if (nprms && *nprms > 0)
2094       name = prms[0];
2095     else
2096       name = "xboard";
2097     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2098     system(buf);
2099 }
2100
2101 void
2102 ManProc ()
2103 {   // called from menu
2104     ManInner(NULL, NULL, NULL, NULL);
2105 }
2106
2107 void
2108 SetWindowTitle (char *text, char *title, char *icon)
2109 {
2110     Arg args[16];
2111     int i;
2112     if (appData.titleInWindow) {
2113         i = 0;
2114         XtSetArg(args[i], XtNlabel, text);   i++;
2115         XtSetValues(titleWidget, args, i);
2116     }
2117     i = 0;
2118     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2119     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2120     XtSetValues(shellWidget, args, i);
2121     XSync(xDisplay, False);
2122 }
2123
2124
2125 static int
2126 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2127 {
2128     return 0;
2129 }
2130
2131 void
2132 DisplayIcsInteractionTitle (String message)
2133 {
2134   if (oldICSInteractionTitle == NULL) {
2135     /* Magic to find the old window title, adapted from vim */
2136     char *wina = getenv("WINDOWID");
2137     if (wina != NULL) {
2138       Window win = (Window) atoi(wina);
2139       Window root, parent, *children;
2140       unsigned int nchildren;
2141       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2142       for (;;) {
2143         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2144         if (!XQueryTree(xDisplay, win, &root, &parent,
2145                         &children, &nchildren)) break;
2146         if (children) XFree((void *)children);
2147         if (parent == root || parent == 0) break;
2148         win = parent;
2149       }
2150       XSetErrorHandler(oldHandler);
2151     }
2152     if (oldICSInteractionTitle == NULL) {
2153       oldICSInteractionTitle = "xterm";
2154     }
2155   }
2156   printf("\033]0;%s\007", message);
2157   fflush(stdout);
2158 }
2159
2160
2161 XtIntervalId delayedEventTimerXID = 0;
2162 DelayedEventCallback delayedEventCallback = 0;
2163
2164 void
2165 FireDelayedEvent ()
2166 {
2167     delayedEventTimerXID = 0;
2168     delayedEventCallback();
2169 }
2170
2171 void
2172 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2173 {
2174     if(delayedEventTimerXID && delayedEventCallback == cb)
2175         // [HGM] alive: replace, rather than add or flush identical event
2176         XtRemoveTimeOut(delayedEventTimerXID);
2177     delayedEventCallback = cb;
2178     delayedEventTimerXID =
2179       XtAppAddTimeOut(appContext, millisec,
2180                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2181 }
2182
2183 DelayedEventCallback
2184 GetDelayedEvent ()
2185 {
2186   if (delayedEventTimerXID) {
2187     return delayedEventCallback;
2188   } else {
2189     return NULL;
2190   }
2191 }
2192
2193 void
2194 CancelDelayedEvent ()
2195 {
2196   if (delayedEventTimerXID) {
2197     XtRemoveTimeOut(delayedEventTimerXID);
2198     delayedEventTimerXID = 0;
2199   }
2200 }
2201
2202 XtIntervalId loadGameTimerXID = 0;
2203
2204 int
2205 LoadGameTimerRunning ()
2206 {
2207     return loadGameTimerXID != 0;
2208 }
2209
2210 int
2211 StopLoadGameTimer ()
2212 {
2213     if (loadGameTimerXID != 0) {
2214         XtRemoveTimeOut(loadGameTimerXID);
2215         loadGameTimerXID = 0;
2216         return TRUE;
2217     } else {
2218         return FALSE;
2219     }
2220 }
2221
2222 void
2223 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2224 {
2225     loadGameTimerXID = 0;
2226     AutoPlayGameLoop();
2227 }
2228
2229 void
2230 StartLoadGameTimer (long millisec)
2231 {
2232     loadGameTimerXID =
2233       XtAppAddTimeOut(appContext, millisec,
2234                       (XtTimerCallbackProc) LoadGameTimerCallback,
2235                       (XtPointer) 0);
2236 }
2237
2238 XtIntervalId analysisClockXID = 0;
2239
2240 void
2241 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2242 {
2243     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2244          || appData.icsEngineAnalyze) { // [DM]
2245         AnalysisPeriodicEvent(0);
2246         StartAnalysisClock();
2247     }
2248 }
2249
2250 void
2251 StartAnalysisClock ()
2252 {
2253     analysisClockXID =
2254       XtAppAddTimeOut(appContext, 2000,
2255                       (XtTimerCallbackProc) AnalysisClockCallback,
2256                       (XtPointer) 0);
2257 }
2258
2259 XtIntervalId clockTimerXID = 0;
2260
2261 int
2262 ClockTimerRunning ()
2263 {
2264     return clockTimerXID != 0;
2265 }
2266
2267 int
2268 StopClockTimer ()
2269 {
2270     if (clockTimerXID != 0) {
2271         XtRemoveTimeOut(clockTimerXID);
2272         clockTimerXID = 0;
2273         return TRUE;
2274     } else {
2275         return FALSE;
2276     }
2277 }
2278
2279 void
2280 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2281 {
2282     clockTimerXID = 0;
2283     DecrementClocks();
2284 }
2285
2286 void
2287 StartClockTimer (long millisec)
2288 {
2289     clockTimerXID =
2290       XtAppAddTimeOut(appContext, millisec,
2291                       (XtTimerCallbackProc) ClockTimerCallback,
2292                       (XtPointer) 0);
2293 }
2294
2295 void
2296 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2297 {
2298     char buf[MSG_SIZ];
2299     Arg args[16];
2300     Widget w = (Widget) opt->handle;
2301
2302     /* check for low time warning */
2303     Pixel foregroundOrWarningColor = timerForegroundPixel;
2304
2305     if (timer > 0 &&
2306         appData.lowTimeWarning &&
2307         (timer / 1000) < appData.icsAlarmTime)
2308       foregroundOrWarningColor = lowTimeWarningColor;
2309
2310     if (appData.clockMode) {
2311       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2312       XtSetArg(args[0], XtNlabel, buf);
2313     } else {
2314       snprintf(buf, MSG_SIZ, "%s  ", color);
2315       XtSetArg(args[0], XtNlabel, buf);
2316     }
2317
2318     if (highlight) {
2319
2320         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2321         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2322     } else {
2323         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2324         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2325     }
2326
2327     XtSetValues(w, args, 3);
2328 }
2329
2330 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2331
2332 void
2333 SetClockIcon (int color)
2334 {
2335     Arg args[16];
2336     Pixmap pm = *clockIcons[color];
2337     if (iconPixmap != pm) {
2338         iconPixmap = pm;
2339         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2340         XtSetValues(shellWidget, args, 1);
2341     }
2342 }
2343
2344 #define INPUT_SOURCE_BUF_SIZE 8192
2345
2346 typedef struct {
2347     CPKind kind;
2348     int fd;
2349     int lineByLine;
2350     char *unused;
2351     InputCallback func;
2352     XtInputId xid;
2353     char buf[INPUT_SOURCE_BUF_SIZE];
2354     VOIDSTAR closure;
2355 } InputSource;
2356
2357 void
2358 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2359 {
2360     InputSource *is = (InputSource *) closure;
2361     int count;
2362     int error;
2363     char *p, *q;
2364
2365     if (is->lineByLine) {
2366         count = read(is->fd, is->unused,
2367                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2368         if (count <= 0) {
2369             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2370             return;
2371         }
2372         is->unused += count;
2373         p = is->buf;
2374         while (p < is->unused) {
2375             q = memchr(p, '\n', is->unused - p);
2376             if (q == NULL) break;
2377             q++;
2378             (is->func)(is, is->closure, p, q - p, 0);
2379             p = q;
2380         }
2381         q = is->buf;
2382         while (p < is->unused) {
2383             *q++ = *p++;
2384         }
2385         is->unused = q;
2386     } else {
2387         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2388         if (count == -1)
2389           error = errno;
2390         else
2391           error = 0;
2392         (is->func)(is, is->closure, is->buf, count, error);
2393     }
2394 }
2395
2396 InputSourceRef
2397 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2398 {
2399     InputSource *is;
2400     ChildProc *cp = (ChildProc *) pr;
2401
2402     is = (InputSource *) calloc(1, sizeof(InputSource));
2403     is->lineByLine = lineByLine;
2404     is->func = func;
2405     if (pr == NoProc) {
2406         is->kind = CPReal;
2407         is->fd = fileno(stdin);
2408     } else {
2409         is->kind = cp->kind;
2410         is->fd = cp->fdFrom;
2411     }
2412     if (lineByLine) {
2413         is->unused = is->buf;
2414     }
2415
2416     is->xid = XtAppAddInput(appContext, is->fd,
2417                             (XtPointer) (XtInputReadMask),
2418                             (XtInputCallbackProc) DoInputCallback,
2419                             (XtPointer) is);
2420     is->closure = closure;
2421     return (InputSourceRef) is;
2422 }
2423
2424 void
2425 RemoveInputSource (InputSourceRef isr)
2426 {
2427     InputSource *is = (InputSource *) isr;
2428
2429     if (is->xid == 0) return;
2430     XtRemoveInput(is->xid);
2431     is->xid = 0;
2432 }
2433
2434 #ifndef HAVE_USLEEP
2435
2436 static Boolean frameWaiting;
2437
2438 static RETSIGTYPE
2439 FrameAlarm (int sig)
2440 {
2441   frameWaiting = False;
2442   /* In case System-V style signals.  Needed?? */
2443   signal(SIGALRM, FrameAlarm);
2444 }
2445
2446 void
2447 FrameDelay (int time)
2448 {
2449   struct itimerval delay;
2450
2451   XSync(xDisplay, False);
2452
2453   if (time > 0) {
2454     frameWaiting = True;
2455     signal(SIGALRM, FrameAlarm);
2456     delay.it_interval.tv_sec =
2457       delay.it_value.tv_sec = time / 1000;
2458     delay.it_interval.tv_usec =
2459       delay.it_value.tv_usec = (time % 1000) * 1000;
2460     setitimer(ITIMER_REAL, &delay, NULL);
2461     while (frameWaiting) pause();
2462     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2463     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2464     setitimer(ITIMER_REAL, &delay, NULL);
2465   }
2466 }
2467
2468 #else
2469
2470 void
2471 FrameDelay (int time)
2472 {
2473   XSync(xDisplay, False);
2474   if (time > 0)
2475     usleep(time * 1000);
2476 }
2477
2478 #endif
2479
2480 static void
2481 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2482 {
2483     char buf[MSG_SIZ], *logoName = buf;
2484     if(appData.logo[n][0]) {
2485         logoName = appData.logo[n];
2486     } else if(appData.autoLogo) {
2487         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2488             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2489         } else if(appData.directory[n] && appData.directory[n][0]) {
2490             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2491         }
2492     }
2493     if(logoName[0])
2494         { ASSIGN(cps->programLogo, logoName); }
2495 }
2496
2497 void
2498 UpdateLogos (int displ)
2499 {
2500     if(optList[W_WHITE-1].handle == NULL) return;
2501     LoadLogo(&first, 0, 0);
2502     LoadLogo(&second, 1, appData.icsActive);
2503     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2504     return;
2505 }
2506