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