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