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