Merge branch 'v4.7.x' into master
[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 void
541 ParseTextAttribs (ColorClass cc, char *s)
542 {
543     (&appData.colorShout)[cc] = strdup(s);
544 }
545
546 void
547 ParseBoardSize (void *addr, char *name)
548 {
549     appData.boardSize = strdup(name);
550 }
551
552 void
553 LoadAllSounds ()
554 { // In XBoard the sound-playing program takes care of obtaining the actual sound
555 }
556
557 void
558 SetCommPortDefaults ()
559 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
560 }
561
562 // [HGM] args: these three cases taken out to stay in front-end
563 void
564 SaveFontArg (FILE *f, ArgDescriptor *ad)
565 {
566   char *name;
567   int i, n = (int)(intptr_t)ad->argLoc;
568   switch(n) {
569     case 0: // CLOCK_FONT
570         name = appData.clockFont;
571       break;
572     case 1: // MESSAGE_FONT
573         name = appData.font;
574       break;
575     case 2: // COORD_FONT
576         name = appData.coordFont;
577       break;
578     default:
579       return;
580   }
581   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
582     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
583         fontTable[n][squareSize] = strdup(name);
584         fontValid[n][squareSize] = True;
585         break;
586   }
587   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
588     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
589 }
590
591 void
592 ExportSounds ()
593 { // nothing to do, as the sounds are at all times represented by their text-string names already
594 }
595
596 void
597 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
598 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
599         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
600 }
601
602 void
603 SaveColor (FILE *f, ArgDescriptor *ad)
604 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
605         if(colorVariable[(int)(intptr_t)ad->argLoc])
606         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
607 }
608
609 void
610 SaveBoardSize (FILE *f, char *name, void *addr)
611 { // wrapper to shield back-end from BoardSize & sizeInfo
612   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
613 }
614
615 void
616 ParseCommPortSettings (char *s)
617 { // no such option in XBoard (yet)
618 }
619
620 int frameX, frameY;
621
622 void
623 GetActualPlacement (Widget wg, WindowPlacement *wp)
624 {
625   XWindowAttributes winAt;
626   Window win, dummy;
627   int rx, ry;
628
629   if(!wg) return;
630
631   win = XtWindow(wg);
632   XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
633   XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
634   wp->x = rx - winAt.x;
635   wp->y = ry - winAt.y;
636   wp->height = winAt.height;
637   wp->width = winAt.width;
638   frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
639 }
640
641 void
642 GetWindowCoords ()
643 { // wrapper to shield use of window handles from back-end (make addressible by number?)
644   // In XBoard this will have to wait until awareness of window parameters is implemented
645   GetActualPlacement(shellWidget, &wpMain);
646   if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
647   if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
648   if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
649   if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
650   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
651   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
652 }
653
654 void
655 PrintCommPortSettings (FILE *f, char *name)
656 { // This option does not exist in XBoard
657 }
658
659 void
660 EnsureOnScreen (int *x, int *y, int minX, int minY)
661 {
662   return;
663 }
664
665 int
666 MainWindowUp ()
667 { // [HGM] args: allows testing if main window is realized from back-end
668   return xBoardWindow != 0;
669 }
670
671 void
672 PopUpStartupDialog ()
673 {  // start menu not implemented in XBoard
674 }
675
676 char *
677 ConvertToLine (int argc, char **argv)
678 {
679   static char line[128*1024], buf[1024];
680   int i;
681
682   line[0] = NULLCHAR;
683   for(i=1; i<argc; i++)
684     {
685       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
686           && argv[i][0] != '{' )
687         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
688       else
689         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
690       strncat(line, buf, 128*1024 - strlen(line) - 1 );
691     }
692
693   line[strlen(line)-1] = NULLCHAR;
694   return line;
695 }
696
697 //--------------------------------------------------------------------------------------------
698
699 void
700 ResizeBoardWindow (int w, int h, int inhibit)
701 {
702     w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
703     h += marginH;
704     shellArgs[0].value = w;
705     shellArgs[1].value = h;
706     shellArgs[4].value = shellArgs[2].value = w;
707     shellArgs[5].value = shellArgs[3].value = h;
708     XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
709
710     XSync(xDisplay, False);
711 }
712
713 static int
714 MakeOneColor (char *name, Pixel *color)
715 {
716     XrmValue vFrom, vTo;
717     if (!appData.monoMode) {
718         vFrom.addr = (caddr_t) name;
719         vFrom.size = strlen(name);
720         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
721         if (vTo.addr == NULL) {
722           appData.monoMode = True;
723           return True;
724         } else {
725           *color = *(Pixel *) vTo.addr;
726         }
727     }
728     return False;
729 }
730
731 int
732 MakeColors ()
733 {   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
734     int forceMono = False;
735
736     if (appData.lowTimeWarning)
737         forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
738     if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
739     if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
740
741     return forceMono;
742 }
743
744 void
745 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
746 {   // detervtomine what fonts to use, and create them
747     XrmValue vTo;
748     XrmDatabase xdb;
749
750     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
751         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
752     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
753         appData.font = fontTable[MESSAGE_FONT][squareSize];
754     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
755         appData.coordFont = fontTable[COORD_FONT][squareSize];
756
757 #if ENABLE_NLS
758     appData.font = InsertPxlSize(appData.font, fontPxlSize);
759     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
760     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
761     fontSet = CreateFontSet(appData.font);
762     clockFontSet = CreateFontSet(appData.clockFont);
763     {
764       /* For the coordFont, use the 0th font of the fontset. */
765       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
766       XFontStruct **font_struct_list;
767       XFontSetExtents *fontSize;
768       char **font_name_list;
769       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
770       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
771       coordFontStruct = XQueryFont(xDisplay, coordFontID);
772       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
773       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
774     }
775 #else
776     appData.font = FindFont(appData.font, fontPxlSize);
777     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
778     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
779     clockFontID = XLoadFont(xDisplay, appData.clockFont);
780     clockFontStruct = XQueryFont(xDisplay, clockFontID);
781     coordFontID = XLoadFont(xDisplay, appData.coordFont);
782     coordFontStruct = XQueryFont(xDisplay, coordFontID);
783     // textHeight in !NLS mode!
784 #endif
785     countFontID = coordFontID;  // [HGM] holdings
786     countFontStruct = coordFontStruct;
787
788     xdb = XtDatabase(xDisplay);
789 #if ENABLE_NLS
790     XrmPutLineResource(&xdb, "*international: True");
791     vTo.size = sizeof(XFontSet);
792     vTo.addr = (XtPointer) &fontSet;
793     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
794 #else
795     XrmPutStringResource(&xdb, "*font", appData.font);
796 #endif
797 }
798
799 char *
800 PrintArg (ArgType t)
801 {
802   char *p="";
803   switch(t) {
804     case ArgZ:
805     case ArgInt:      p = " N"; break;
806     case ArgString:   p = " STR"; break;
807     case ArgBoolean:  p = " TF"; break;
808     case ArgSettingsFilename:
809     case ArgBackupSettingsFile:
810     case ArgFilename: p = " FILE"; break;
811     case ArgX:        p = " Nx"; break;
812     case ArgY:        p = " Ny"; break;
813     case ArgAttribs:  p = " TEXTCOL"; break;
814     case ArgColor:    p = " COL"; break;
815     case ArgFont:     p = " FONT"; break;
816     case ArgBoardSize: p = " SIZE"; break;
817     case ArgFloat: p = " FLOAT"; break;
818     case ArgTrue:
819     case ArgFalse:
820     case ArgTwo:
821     case ArgNone:
822     case ArgCommSettings:
823     case ArgMaster:
824     case ArgInstall:
825       break;
826   }
827   return p;
828 }
829
830 char *
831 GenerateGlobalTranslationTable (void)
832 {
833   /* go through all menu items and extract the keyboard shortcuts, so that X11 can load them */
834   char *output;
835
836   int i,j;
837   MenuItem *mi;
838
839   output = strdup("");
840
841   /* loop over all menu entries */
842   for( i=0; menuBar[i].mi ; i++)
843     {
844       mi = menuBar[i].mi;
845       for(j=0; mi[j].proc; j++)
846         {
847           if (mi[j].accel)
848             {
849               int ctrl  = 0;
850               int shift = 0;
851               int alt   = 0;
852
853               char *key,*test, *mods;
854
855               /* check for Ctrl/Alt */
856               if( strstr(mi[j].accel, "<Ctrl>")  ) ctrl  = 1;
857               if( strstr(mi[j].accel, "<Shift>") ) shift = 1;
858               if( strstr(mi[j].accel, "<Alt>")   ) alt   = 1;
859
860               /* remove all <...> */
861               test = strrchr(mi[j].accel, '>');
862               if ( test==NULL )
863                 key = strdup(mi[j].accel);
864               else
865                 key = strdup(++test); // remove ">"
866
867               /* instead of shift X11 uses the uppercase letter directly*/
868               if (shift && strlen(key)==1 )
869                 {
870                   *key  = toupper(*key);
871                   shift = 0;
872                 }
873
874               /* handle some special cases which have different names in X11 */
875               if ( strncmp(key, "Page_Down", 9) == 0 )
876                 {
877                   free(key);
878                   key=strdup("Next");
879                 }
880               else if ( strncmp(key, "Page_Up", 7) == 0 )
881                 {
882                   free(key);
883                   key=strdup("Prior");
884                 };
885
886               /* create string of mods */
887               if (ctrl)
888                 mods = strdup("Ctrl ");
889               else
890                 mods = strdup("");
891
892               if(alt)
893                 {
894                   mods = realloc(mods, strlen(mods) + strlen("Meta ")+1);
895                   strncat(mods, "Meta ", 5);
896                 };
897
898               if(shift)
899                 {
900                   mods = realloc(mods, strlen(mods) + strlen("Shift ")+1);
901                   strncat(mods, "Shift ", 6);
902                 };
903
904               // remove trailing space
905               if( isspace(mods[strlen(mods)-1]) )
906                 mods[strlen(mods)-1]='\0';
907
908               /* get the name for the callback, we can use MenuItem() here that will call KeyBindingProc */
909               size_t namesize = snprintf(NULL, 0, "%s.%s", menuBar[i].ref, mi[j].ref);
910               char *name = malloc(namesize+1);
911               snprintf(name, namesize+1, "%s.%s", menuBar[i].ref, mi[j].ref);
912
913               size_t buffersize = snprintf(NULL, 0, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
914               char *buffer = malloc(buffersize+1);
915               snprintf(buffer, buffersize+1, ":%s<Key>%s: MenuItem(%s) \n ", mods, key, name);
916
917               /* add string to the output */
918               output = realloc(output, strlen(output) + strlen(buffer)+1);
919               strncat(output, buffer, strlen(buffer));
920
921               /* clean up */
922               free(key);
923               free(buffer);
924               free(name);
925               free(mods);
926             }
927         }
928     }
929   return output;
930 }
931
932
933 void
934 PrintOptions ()
935 {
936   char buf[MSG_SIZ];
937   int len=0;
938   ArgDescriptor *q, *p = argDescriptors+5;
939   printf("\nXBoard accepts the following options:\n"
940          "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
941          " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
942          " SIZE = board-size spec(s)\n"
943          " Within parentheses are short forms, or options to set to true or false.\n"
944          " Persistent options (saved in the settings file) are marked with *)\n\n");
945   while(p->argName) {
946     if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
947     snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
948     if(p->save) strcat(buf+len, "*");
949     for(q=p+1; q->argLoc == p->argLoc; q++) {
950       if(q->argName[0] == '-') continue;
951       strcat(buf+len, q == p+1 ? " (" : " ");
952       sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
953     }
954     if(q != p+1) strcat(buf+len, ")");
955     len = strlen(buf);
956     if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
957     p = q;
958   }
959   if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
960 }
961
962 void
963 SlaveResize (Option *opt)
964 {
965 }
966
967 int
968 main (int argc, char **argv)
969 {
970     int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
971     XSetWindowAttributes window_attributes;
972     Arg args[16];
973     Dimension boardWidth, boardHeight, w, h;
974     char *p;
975     int forceMono = False;
976
977     srandom(time(0)); // [HGM] book: make random truly random
978
979     setbuf(stdout, NULL);
980     setbuf(stderr, NULL);
981     debugFP = stderr;
982
983     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
984       printf("%s version %s\n\n  configure options: %s\n", PACKAGE_NAME, PACKAGE_VERSION, CONFIGURE_OPTIONS);
985         exit(0);
986     }
987
988     if(argc > 1 && !strcmp(argv[1], "--help" )) {
989         PrintOptions();
990         exit(0);
991     }
992
993     programName = strrchr(argv[0], '/');
994     if (programName == NULL)
995       programName = argv[0];
996     else
997       programName++;
998
999 #ifdef ENABLE_NLS
1000     XtSetLanguageProc(NULL, NULL, NULL);
1001     if (appData.debugMode) {
1002       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
1003     }
1004
1005     bindtextdomain(PACKAGE, LOCALEDIR);
1006     textdomain(PACKAGE);
1007 #endif
1008
1009     appData.boardSize = "";
1010     InitAppData(ConvertToLine(argc, argv));
1011     p = getenv("HOME");
1012     if (p == NULL) p = "/tmp";
1013     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1014     gameCopyFilename = (char*) malloc(i);
1015     gamePasteFilename = (char*) malloc(i);
1016     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1017     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1018
1019     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1020         static char buf[MSG_SIZ];
1021         EscapeExpand(buf, appData.firstInitString);
1022         appData.firstInitString = strdup(buf);
1023         EscapeExpand(buf, appData.secondInitString);
1024         appData.secondInitString = strdup(buf);
1025         EscapeExpand(buf, appData.firstComputerString);
1026         appData.firstComputerString = strdup(buf);
1027         EscapeExpand(buf, appData.secondComputerString);
1028         appData.secondComputerString = strdup(buf);
1029     }
1030
1031     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1032         chessDir = ".";
1033     } else {
1034         if (chdir(chessDir) != 0) {
1035             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1036             perror(chessDir);
1037             exit(1);
1038         }
1039     }
1040
1041     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1042         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1043         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1044            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1045            exit(errno);
1046         }
1047         setbuf(debugFP, NULL);
1048     }
1049
1050     /* [HGM,HR] make sure board size is acceptable */
1051     if(appData.NrFiles > BOARD_FILES ||
1052        appData.NrRanks > BOARD_RANKS   )
1053          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1054
1055 #if !HIGHDRAG
1056     /* This feature does not work; animation needs a rewrite */
1057     appData.highlightDragging = FALSE;
1058 #endif
1059     InitBackEnd1();
1060
1061         gameInfo.variant = StringToVariant(appData.variant);
1062         InitPosition(FALSE);
1063
1064     shellWidget =
1065       XtAppInitialize(&appContext, "XBoard", shellOptions,
1066                       XtNumber(shellOptions),
1067                       &argc, argv, xboardResources, NULL, 0);
1068
1069     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1070                               clientResources, XtNumber(clientResources),
1071                               NULL, 0);
1072
1073     xDisplay = XtDisplay(shellWidget);
1074     xScreen = DefaultScreen(xDisplay);
1075     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1076
1077     /*
1078      * determine size, based on supplied or remembered -size, or screen size
1079      */
1080     if (isdigit(appData.boardSize[0])) {
1081         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1082                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1083                    &fontPxlSize, &smallLayout, &tinyLayout);
1084         if (i == 0) {
1085             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1086                     programName, appData.boardSize);
1087             exit(2);
1088         }
1089         if (i < 7) {
1090             /* Find some defaults; use the nearest known size */
1091             SizeDefaults *szd, *nearest;
1092             int distance = 99999;
1093             nearest = szd = sizeDefaults;
1094             while (szd->name != NULL) {
1095                 if (abs(szd->squareSize - squareSize) < distance) {
1096                     nearest = szd;
1097                     distance = abs(szd->squareSize - squareSize);
1098                     if (distance == 0) break;
1099                 }
1100                 szd++;
1101             }
1102             if (i < 2) lineGap = nearest->lineGap;
1103             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1104             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1105             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1106             if (i < 6) smallLayout = nearest->smallLayout;
1107             if (i < 7) tinyLayout = nearest->tinyLayout;
1108         }
1109     } else {
1110         SizeDefaults *szd = sizeDefaults;
1111         if (*appData.boardSize == NULLCHAR) {
1112             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1113                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1114               szd++;
1115             }
1116             if (szd->name == NULL) szd--;
1117             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1118         } else {
1119             while (szd->name != NULL &&
1120                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1121             if (szd->name == NULL) {
1122                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1123                         programName, appData.boardSize);
1124                 exit(2);
1125             }
1126         }
1127         squareSize = szd->squareSize;
1128         lineGap = szd->lineGap;
1129         clockFontPxlSize = szd->clockFontPxlSize;
1130         coordFontPxlSize = szd->coordFontPxlSize;
1131         fontPxlSize = szd->fontPxlSize;
1132         smallLayout = szd->smallLayout;
1133         tinyLayout = szd->tinyLayout;
1134         // [HGM] font: use defaults from settings file if available and not overruled
1135     }
1136
1137     defaultLineGap = lineGap;
1138     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
1139
1140     /* [HR] height treated separately (hacked) */
1141     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1142     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1143
1144     /*
1145      * Determine what fonts to use.
1146      */
1147     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
1148
1149     /*
1150      * Detect if there are not enough colors available and adapt.
1151      */
1152     if (DefaultDepth(xDisplay, xScreen) <= 2) {
1153       appData.monoMode = True;
1154     }
1155
1156     forceMono = MakeColors();
1157
1158     if (forceMono) {
1159       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1160               programName);
1161         appData.monoMode = True;
1162     }
1163
1164     if (appData.monoMode && appData.debugMode) {
1165         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
1166                 (unsigned long) XWhitePixel(xDisplay, xScreen),
1167                 (unsigned long) XBlackPixel(xDisplay, xScreen));
1168     }
1169
1170     ParseIcsTextColors();
1171
1172     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1173
1174     /*
1175      * widget hierarchy
1176      */
1177     if (tinyLayout) {
1178         layoutName = "tinyLayout";
1179     } else if (smallLayout) {
1180         layoutName = "smallLayout";
1181     } else {
1182         layoutName = "normalLayout";
1183     }
1184
1185     optList = BoardPopUp(squareSize, lineGap, (void*)
1186 #if ENABLE_NLS
1187                                                 &clockFontSet);
1188 #else
1189                                                 clockFontStruct);
1190 #endif
1191     InitDrawingHandle(optList + W_BOARD);
1192     currBoard        = &optList[W_BOARD];
1193     boardWidget      = optList[W_BOARD].handle;
1194     menuBarWidget    = optList[W_MENU].handle;
1195     dropMenu         = optList[W_DROP].handle;
1196     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
1197     formWidget  = XtParent(boardWidget);
1198     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
1199     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
1200     XtGetValues(optList[W_WHITE].handle, args, 2);
1201     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
1202       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
1203       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
1204       XtGetValues(optList[W_PAUSE].handle, args, 2);
1205     }
1206
1207     xBoardWindow = XtWindow(boardWidget);
1208
1209     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
1210     //       not need to go into InitDrawingSizes().
1211
1212     /*
1213      * Create X checkmark bitmap and initialize option menu checks.
1214      */
1215     ReadBitmap(&xMarkPixmap, "checkmark.bm",
1216                checkmark_bits, checkmark_width, checkmark_height);
1217     InitMenuMarkers();
1218
1219     /*
1220      * Create an icon.
1221      */
1222     ReadBitmap(&wIconPixmap, "icon_white.bm",
1223                icon_white_bits, icon_white_width, icon_white_height);
1224     ReadBitmap(&bIconPixmap, "icon_black.bm",
1225                icon_black_bits, icon_black_width, icon_black_height);
1226     iconPixmap = wIconPixmap;
1227     i = 0;
1228     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
1229     XtSetValues(shellWidget, args, i);
1230
1231     /*
1232      * Create a cursor for the board widget.
1233      */
1234     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1235     XChangeWindowAttributes(xDisplay, xBoardWindow,
1236                             CWCursor, &window_attributes);
1237
1238     /*
1239      * Inhibit shell resizing.
1240      */
1241     shellArgs[0].value = (XtArgVal) &w;
1242     shellArgs[1].value = (XtArgVal) &h;
1243     XtGetValues(shellWidget, shellArgs, 2);
1244     shellArgs[4].value = shellArgs[2].value = w;
1245     shellArgs[5].value = shellArgs[3].value = h;
1246 //    XtSetValues(shellWidget, &shellArgs[2], 4);
1247     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1248     marginH =  h - boardHeight;
1249
1250     CatchDeleteWindow(shellWidget, "QuitProc");
1251
1252     CreateAnyPieces();
1253     CreateGrid();
1254
1255     if(appData.logoSize)
1256     {   // locate and read user logo
1257         char buf[MSG_SIZ];
1258         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1259         ASSIGN(userLogo, buf);
1260     }
1261
1262     if (appData.animate || appData.animateDragging)
1263       CreateAnimVars();
1264
1265
1266     char *TranslationsTableMenus=GenerateGlobalTranslationTable ();
1267
1268     XtAugmentTranslations(formWidget,
1269                           XtParseTranslationTable(globalTranslations));
1270     XtAugmentTranslations(formWidget,
1271                           XtParseTranslationTable(TranslationsTableMenus));
1272
1273     XtAddEventHandler(formWidget, KeyPressMask, False,
1274                       (XtEventHandler) MoveTypeInProc, NULL);
1275     XtAddEventHandler(shellWidget, StructureNotifyMask, False,
1276                       (XtEventHandler) EventProc, NULL);
1277
1278     /* [AS] Restore layout */
1279     if( wpMoveHistory.visible ) {
1280       HistoryPopUp();
1281     }
1282
1283     if( wpEvalGraph.visible )
1284       {
1285         EvalGraphPopUp();
1286       };
1287
1288     if( wpEngineOutput.visible ) {
1289       EngineOutputPopUp();
1290     }
1291
1292     InitBackEnd2();
1293
1294     if (errorExitStatus == -1) {
1295         if (appData.icsActive) {
1296             /* We now wait until we see "login:" from the ICS before
1297                sending the logon script (problems with timestamp otherwise) */
1298             /*ICSInitScript();*/
1299             if (appData.icsInputBox) ICSInputBoxPopUp();
1300         }
1301
1302     #ifdef SIGWINCH
1303     signal(SIGWINCH, TermSizeSigHandler);
1304     #endif
1305         signal(SIGINT, IntSigHandler);
1306         signal(SIGTERM, IntSigHandler);
1307         if (*appData.cmailGameName != NULLCHAR) {
1308             signal(SIGUSR1, CmailSigHandler);
1309         }
1310     }
1311
1312     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1313     InitPosition(TRUE);
1314     UpdateLogos(TRUE);
1315 //    XtSetKeyboardFocus(shellWidget, formWidget);
1316     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1317
1318     XtAppMainLoop(appContext);
1319     if (appData.debugMode) fclose(debugFP); // [DM] debug
1320     return 0;
1321 }
1322
1323 RETSIGTYPE
1324 TermSizeSigHandler (int sig)
1325 {
1326     update_ics_width();
1327 }
1328
1329 RETSIGTYPE
1330 IntSigHandler (int sig)
1331 {
1332     ExitEvent(sig);
1333 }
1334
1335 RETSIGTYPE
1336 CmailSigHandler (int sig)
1337 {
1338     int dummy = 0;
1339     int error;
1340
1341     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1342
1343     /* Activate call-back function CmailSigHandlerCallBack()             */
1344     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1345
1346     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1347 }
1348
1349 void
1350 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1351 {
1352     BoardToTop();
1353     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1354 }
1355 /**** end signal code ****/
1356
1357
1358 #define Abs(n) ((n)<0 ? -(n) : (n))
1359
1360 #ifdef ENABLE_NLS
1361 char *
1362 InsertPxlSize (char *pattern, int targetPxlSize)
1363 {
1364     char *base_fnt_lst, strInt[12], *p, *q;
1365     int alternatives, i, len, strIntLen;
1366
1367     /*
1368      * Replace the "*" (if present) in the pixel-size slot of each
1369      * alternative with the targetPxlSize.
1370      */
1371     p = pattern;
1372     alternatives = 1;
1373     while ((p = strchr(p, ',')) != NULL) {
1374       alternatives++;
1375       p++;
1376     }
1377     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1378     strIntLen = strlen(strInt);
1379     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1380
1381     p = pattern;
1382     q = base_fnt_lst;
1383     while (alternatives--) {
1384       char *comma = strchr(p, ',');
1385       for (i=0; i<14; i++) {
1386         char *hyphen = strchr(p, '-');
1387         if (!hyphen) break;
1388         if (comma && hyphen > comma) break;
1389         len = hyphen + 1 - p;
1390         if (i == 7 && *p == '*' && len == 2) {
1391           p += len;
1392           memcpy(q, strInt, strIntLen);
1393           q += strIntLen;
1394           *q++ = '-';
1395         } else {
1396           memcpy(q, p, len);
1397           p += len;
1398           q += len;
1399         }
1400       }
1401       if (!comma) break;
1402       len = comma + 1 - p;
1403       memcpy(q, p, len);
1404       p += len;
1405       q += len;
1406     }
1407     strcpy(q, p);
1408
1409     return base_fnt_lst;
1410 }
1411
1412 XFontSet
1413 CreateFontSet (char *base_fnt_lst)
1414 {
1415     XFontSet fntSet;
1416     char **missing_list;
1417     int missing_count;
1418     char *def_string;
1419
1420     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1421                             &missing_list, &missing_count, &def_string);
1422     if (appData.debugMode) {
1423       int i, count;
1424       XFontStruct **font_struct_list;
1425       char **font_name_list;
1426       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1427       if (fntSet) {
1428         fprintf(debugFP, " got list %s, locale %s\n",
1429                 XBaseFontNameListOfFontSet(fntSet),
1430                 XLocaleOfFontSet(fntSet));
1431         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1432         for (i = 0; i < count; i++) {
1433           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1434         }
1435       }
1436       for (i = 0; i < missing_count; i++) {
1437         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1438       }
1439     }
1440     if (fntSet == NULL) {
1441       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1442       exit(2);
1443     }
1444     return fntSet;
1445 }
1446 #else // not ENABLE_NLS
1447 /*
1448  * Find a font that matches "pattern" that is as close as
1449  * possible to the targetPxlSize.  Prefer fonts that are k
1450  * pixels smaller to fonts that are k pixels larger.  The
1451  * pattern must be in the X Consortium standard format,
1452  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1453  * The return value should be freed with XtFree when no
1454  * longer needed.
1455  */
1456 char *
1457 FindFont (char *pattern, int targetPxlSize)
1458 {
1459     char **fonts, *p, *best, *scalable, *scalableTail;
1460     int i, j, nfonts, minerr, err, pxlSize;
1461
1462     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1463     if (nfonts < 1) {
1464         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1465                 programName, pattern);
1466         exit(2);
1467     }
1468
1469     best = fonts[0];
1470     scalable = NULL;
1471     minerr = 999999;
1472     for (i=0; i<nfonts; i++) {
1473         j = 0;
1474         p = fonts[i];
1475         if (*p != '-') continue;
1476         while (j < 7) {
1477             if (*p == NULLCHAR) break;
1478             if (*p++ == '-') j++;
1479         }
1480         if (j < 7) continue;
1481         pxlSize = atoi(p);
1482         if (pxlSize == 0) {
1483             scalable = fonts[i];
1484             scalableTail = p;
1485         } else {
1486             err = pxlSize - targetPxlSize;
1487             if (Abs(err) < Abs(minerr) ||
1488                 (minerr > 0 && err < 0 && -err == minerr)) {
1489                 best = fonts[i];
1490                 minerr = err;
1491             }
1492         }
1493     }
1494     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1495         /* If the error is too big and there is a scalable font,
1496            use the scalable font. */
1497         int headlen = scalableTail - scalable;
1498         p = (char *) XtMalloc(strlen(scalable) + 10);
1499         while (isdigit(*scalableTail)) scalableTail++;
1500         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1501     } else {
1502         p = (char *) XtMalloc(strlen(best) + 2);
1503         safeStrCpy(p, best, strlen(best)+1 );
1504     }
1505     if (appData.debugMode) {
1506         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
1507                 pattern, targetPxlSize, p);
1508     }
1509     XFreeFontNames(fonts);
1510     return p;
1511 }
1512 #endif
1513
1514 void
1515 ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
1516 {
1517     if (bits != NULL) {
1518         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
1519                                     wreq, hreq);
1520     }
1521 }
1522
1523 void
1524 MarkMenuItem (char *menuRef, int state)
1525 {
1526     MenuItem *item = MenuNameToItem(menuRef);
1527
1528     if(item) {
1529         Arg args[2];
1530         XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
1531         XtSetValues(item->handle, args, 1);
1532     }
1533 }
1534
1535 void
1536 EnableNamedMenuItem (char *menuRef, int state)
1537 {
1538     MenuItem *item = MenuNameToItem(menuRef);
1539
1540     if(item) XtSetSensitive(item->handle, state);
1541 }
1542
1543 void
1544 EnableButtonBar (int state)
1545 {
1546     XtSetSensitive(optList[W_BUTTON].handle, state);
1547 }
1548
1549
1550 void
1551 SetMenuEnables (Enables *enab)
1552 {
1553   while (enab->name != NULL) {
1554     EnableNamedMenuItem(enab->name, enab->value);
1555     enab++;
1556   }
1557 }
1558
1559 void
1560 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1561 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1562     MenuItem *item;
1563     if(*nprms == 0) return;
1564     item = MenuNameToItem(prms[0]);
1565     if(item) ((MenuProc *) item->proc) ();
1566 }
1567
1568 void
1569 SetupDropMenu ()
1570 {
1571     int i, j, count;
1572     char label[32];
1573     Arg args[16];
1574     Widget entry;
1575     char* p;
1576
1577     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1578         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1579         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1580                    dmEnables[i].piece);
1581         XtSetSensitive(entry, p != NULL || !appData.testLegality
1582                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1583                                        && !appData.icsActive));
1584         count = 0;
1585         while (p && *p++ == dmEnables[i].piece) count++;
1586         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1587         j = 0;
1588         XtSetArg(args[j], XtNlabel, label); j++;
1589         XtSetValues(entry, args, j);
1590     }
1591 }
1592
1593 static void
1594 do_flash_delay (unsigned long msec)
1595 {
1596     TimeDelay(msec);
1597 }
1598
1599 void
1600 FlashDelay (int flash_delay)
1601 {
1602         XSync(xDisplay, False);
1603         if(flash_delay) do_flash_delay(flash_delay);
1604 }
1605
1606 double
1607 Fraction (int x, int start, int stop)
1608 {
1609    double f = ((double) x - start)/(stop - start);
1610    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1611    return f;
1612 }
1613
1614 static WindowPlacement wpNew;
1615
1616 void
1617 CoDrag (Widget sh, WindowPlacement *wp)
1618 {
1619     Arg args[16];
1620     int j=0, touch=0, fudge = 2;
1621     GetActualPlacement(sh, wp);
1622     if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
1623     if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
1624     if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1625     if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
1626     if(!touch ) return; // only windows that touch co-move
1627     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1628         int heightInc = wpNew.height - wpMain.height;
1629         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1630         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1631         wp->y += fracTop * heightInc;
1632         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1633         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1634     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1635         int widthInc = wpNew.width - wpMain.width;
1636         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1637         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1638         wp->y += fracLeft * widthInc;
1639         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1640         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1641     }
1642     wp->x += wpNew.x - wpMain.x;
1643     wp->y += wpNew.y - wpMain.y;
1644     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1645     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1646     XtSetArg(args[j], XtNx, wp->x); j++;
1647     XtSetArg(args[j], XtNy, wp->y); j++;
1648     XtSetValues(sh, args, j);
1649 }
1650
1651 void
1652 ReSize (WindowPlacement *wp)
1653 {
1654         int sqx, sqy, w, h;
1655         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1656         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1657         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1658         if(sqy < sqx) sqx = sqy;
1659         if(sqx != squareSize) {
1660             squareSize = sqx; // adopt new square size
1661             CreatePNGPieces(); // make newly scaled pieces
1662             InitDrawingSizes(0, 0); // creates grid etc.
1663         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1664         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1665         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1666         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1667         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1668 }
1669
1670 static XtIntervalId delayedDragID = 0;
1671
1672 void
1673 DragProc ()
1674 {
1675         static int busy;
1676         if(busy) return;
1677
1678         busy = 1;
1679         GetActualPlacement(shellWidget, &wpNew);
1680         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1681            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1682             busy = 0; return; // false alarm
1683         }
1684         ReSize(&wpNew);
1685         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1686         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1687         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1688         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1689         wpMain = wpNew;
1690         DrawPosition(True, NULL);
1691         delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1692         busy = 0;
1693 }
1694
1695
1696 void
1697 DelayedDrag ()
1698 {
1699     if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
1700     delayedDragID =
1701       XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
1702 }
1703
1704 void
1705 EventProc (Widget widget, caddr_t unused, XEvent *event)
1706 {
1707     if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
1708         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1709 }
1710
1711 /*
1712  * event handler for redrawing the board
1713  */
1714 void
1715 DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1716 {
1717     DrawPosition(True, NULL);
1718 }
1719
1720
1721 void
1722 HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
1723 {   // [HGM] pv: walk PV
1724     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
1725 }
1726
1727 extern int savedIndex;  /* gross that this is global */
1728
1729 void
1730 CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
1731 {
1732         String val;
1733         XawTextPosition index, dummy;
1734         Arg arg;
1735
1736         XawTextGetSelectionPos(w, &index, &dummy);
1737         XtSetArg(arg, XtNstring, &val);
1738         XtGetValues(w, &arg, 1);
1739         ReplaceComment(savedIndex, val);
1740         if(savedIndex != currentMove) ToNrEvent(savedIndex);
1741         LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
1742 }
1743
1744
1745 /* Disable all user input other than deleting the window */
1746 static int frozen = 0;
1747
1748 void
1749 FreezeUI ()
1750 {
1751   if (frozen) return;
1752   /* Grab by a widget that doesn't accept input */
1753   XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
1754   frozen = 1;
1755 }
1756
1757 /* Undo a FreezeUI */
1758 void
1759 ThawUI ()
1760 {
1761   if (!frozen) return;
1762   XtRemoveGrab(optList[W_MESSG].handle);
1763   frozen = 0;
1764 }
1765
1766 void
1767 ModeHighlight ()
1768 {
1769     Arg args[16];
1770     static int oldPausing = FALSE;
1771     static GameMode oldmode = (GameMode) -1;
1772     char *wname;
1773
1774     if (!boardWidget || !XtIsRealized(boardWidget)) return;
1775
1776     if (pausing != oldPausing) {
1777         oldPausing = pausing;
1778         MarkMenuItem("Mode.Pause", pausing);
1779
1780         if (appData.showButtonBar) {
1781           /* Always toggle, don't set.  Previous code messes up when
1782              invoked while the button is pressed, as releasing it
1783              toggles the state again. */
1784           {
1785             Pixel oldbg, oldfg;
1786             XtSetArg(args[0], XtNbackground, &oldbg);
1787             XtSetArg(args[1], XtNforeground, &oldfg);
1788             XtGetValues(optList[W_PAUSE].handle,
1789                         args, 2);
1790             XtSetArg(args[0], XtNbackground, oldfg);
1791             XtSetArg(args[1], XtNforeground, oldbg);
1792           }
1793           XtSetValues(optList[W_PAUSE].handle, args, 2);
1794         }
1795     }
1796
1797     wname = ModeToWidgetName(oldmode);
1798     if (wname != NULL) {
1799         MarkMenuItem(wname, False);
1800     }
1801     wname = ModeToWidgetName(gameMode);
1802     if (wname != NULL) {
1803         MarkMenuItem(wname, True);
1804     }
1805     oldmode = gameMode;
1806     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1807
1808     /* Maybe all the enables should be handled here, not just this one */
1809     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1810
1811     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1812 }
1813
1814
1815 /*
1816  * Button/menu procedures
1817  */
1818
1819 /* this variable is shared between CopyPositionProc and SendPositionSelection */
1820 char *selected_fen_position=NULL;
1821
1822 Boolean
1823 SendPositionSelection (Widget w, Atom *selection, Atom *target,
1824                        Atom *type_return, XtPointer *value_return,
1825                        unsigned long *length_return, int *format_return)
1826 {
1827   char *selection_tmp;
1828
1829 //  if (!selected_fen_position) return False; /* should never happen */
1830   if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
1831    if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
1832     FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
1833     long len;
1834     size_t count;
1835     if (f == NULL) return False;
1836     fseek(f, 0, 2);
1837     len = ftell(f);
1838     rewind(f);
1839     selection_tmp = XtMalloc(len + 1);
1840     count = fread(selection_tmp, 1, len, f);
1841     fclose(f);
1842     if (len != count) {
1843       XtFree(selection_tmp);
1844       return False;
1845     }
1846     selection_tmp[len] = NULLCHAR;
1847    } else {
1848     /* note: since no XtSelectionDoneProc was registered, Xt will
1849      * automatically call XtFree on the value returned.  So have to
1850      * make a copy of it allocated with XtMalloc */
1851     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
1852     safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
1853    }
1854
1855     *value_return=selection_tmp;
1856     *length_return=strlen(selection_tmp);
1857     *type_return=*target;
1858     *format_return = 8; /* bits per byte */
1859     return True;
1860   } else if (*target == XA_TARGETS(xDisplay)) {
1861     Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
1862     targets_tmp[0] = XA_UTF8_STRING(xDisplay);
1863     targets_tmp[1] = XA_STRING;
1864     *value_return = targets_tmp;
1865     *type_return = XA_ATOM;
1866     *length_return = 2;
1867 #if 0
1868     // This code leads to a read of value_return out of bounds on 64-bit systems.
1869     // Other code which I have seen always sets *format_return to 32 independent of
1870     // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
1871     // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
1872     *format_return = 8 * sizeof(Atom);
1873     if (*format_return > 32) {
1874       *length_return *= *format_return / 32;
1875       *format_return = 32;
1876     }
1877 #else
1878     *format_return = 32;
1879 #endif
1880     return True;
1881   } else {
1882     return False;
1883   }
1884 }
1885
1886 /* note: when called from menu all parameters are NULL, so no clue what the
1887  * Widget which was clicked on was, or what the click event was
1888  */
1889 void
1890 CopySomething (char *src)
1891 {
1892     selected_fen_position = src;
1893     /*
1894      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
1895      * have a notion of a position that is selected but not copied.
1896      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
1897      */
1898     XtOwnSelection(menuBarWidget, XA_PRIMARY,
1899                    CurrentTime,
1900                    SendPositionSelection,
1901                    NULL/* lose_ownership_proc */ ,
1902                    NULL/* transfer_done_proc */);
1903     XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
1904                    CurrentTime,
1905                    SendPositionSelection,
1906                    NULL/* lose_ownership_proc */ ,
1907                    NULL/* transfer_done_proc */);
1908 }
1909
1910 /* function called when the data to Paste is ready */
1911 static void
1912 PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
1913                  Atom *type, XtPointer value, unsigned long *len, int *format)
1914 {
1915   char *fenstr=value;
1916   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
1917   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
1918   EditPositionPasteFEN(fenstr);
1919   XtFree(value);
1920 }
1921
1922 /* called when Paste Position button is pressed,
1923  * all parameters will be NULL */
1924 void
1925 PastePositionProc ()
1926 {
1927     XtGetSelectionValue(menuBarWidget,
1928       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1929       /* (XtSelectionCallbackProc) */ PastePositionCB,
1930       NULL, /* client_data passed to PastePositionCB */
1931
1932       /* better to use the time field from the event that triggered the
1933        * call to this function, but that isn't trivial to get
1934        */
1935       CurrentTime
1936     );
1937     return;
1938 }
1939
1940 /* note: when called from menu all parameters are NULL, so no clue what the
1941  * Widget which was clicked on was, or what the click event was
1942  */
1943 /* function called when the data to Paste is ready */
1944 static void
1945 PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
1946              Atom *type, XtPointer value, unsigned long *len, int *format)
1947 {
1948   FILE* f;
1949   if (value == NULL || *len == 0) {
1950     return; /* nothing had been selected to copy */
1951   }
1952   f = fopen(gamePasteFilename, "w");
1953   if (f == NULL) {
1954     DisplayError(_("Can't open temp file"), errno);
1955     return;
1956   }
1957   fwrite(value, 1, *len, f);
1958   fclose(f);
1959   XtFree(value);
1960   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1961 }
1962
1963 /* called when Paste Game button is pressed,
1964  * all parameters will be NULL */
1965 void
1966 PasteGameProc ()
1967 {
1968     XtGetSelectionValue(menuBarWidget,
1969       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
1970       /* (XtSelectionCallbackProc) */ PasteGameCB,
1971       NULL, /* client_data passed to PasteGameCB */
1972
1973       /* better to use the time field from the event that triggered the
1974        * call to this function, but that isn't trivial to get
1975        */
1976       CurrentTime
1977     );
1978     return;
1979 }
1980
1981
1982 void
1983 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1984 {
1985     QuitProc();
1986 }
1987
1988 int
1989 ShiftKeys ()
1990 {   // bassic primitive for determining if modifier keys are pressed
1991     long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
1992     char keys[32];
1993     int i,j,  k=0;
1994     XQueryKeymap(xDisplay,keys);
1995     for(i=0; i<6; i++) {
1996         k <<= 1;
1997         j = XKeysymToKeycode(xDisplay, codes[i]);
1998         k += ( (keys[j>>3]&1<<(j&7)) != 0 );
1999     }
2000     return k;
2001 }
2002
2003 static void
2004 MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
2005 {
2006     char buf[10];
2007     KeySym sym;
2008     int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
2009     if ( n == 1 && *buf >= 32 // printable
2010          && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
2011         ) BoxAutoPopUp (buf);
2012 }
2013
2014 static void
2015 UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2016 {   // [HGM] input: let up-arrow recall previous line from history
2017     IcsKey(1);
2018 }
2019
2020 static void
2021 DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2022 {   // [HGM] input: let down-arrow recall next line from history
2023     IcsKey(-1);
2024 }
2025
2026 static void
2027 EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2028 {
2029     IcsKey(0);
2030 }
2031
2032 void
2033 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2034 {
2035         if (!TempBackwardActive) {
2036                 TempBackwardActive = True;
2037                 BackwardEvent();
2038         }
2039 }
2040
2041 void
2042 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2043 {
2044         /* Check to see if triggered by a key release event for a repeating key.
2045          * If so the next queued event will be a key press of the same key at the same time */
2046         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
2047                 XEvent next;
2048                 XPeekEvent(xDisplay, &next);
2049                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
2050                         next.xkey.keycode == event->xkey.keycode)
2051                                 return;
2052         }
2053     ForwardEvent();
2054         TempBackwardActive = False;
2055 }
2056
2057 void
2058 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
2059 {   // called as key binding
2060     char buf[MSG_SIZ];
2061     String name;
2062     if (nprms && *nprms > 0)
2063       name = prms[0];
2064     else
2065       name = "xboard";
2066     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
2067     system(buf);
2068 }
2069
2070 void
2071 ManProc ()
2072 {   // called from menu
2073     ManInner(NULL, NULL, NULL, NULL);
2074 }
2075
2076 void
2077 SetWindowTitle (char *text, char *title, char *icon)
2078 {
2079     Arg args[16];
2080     int i;
2081     if (appData.titleInWindow) {
2082         i = 0;
2083         XtSetArg(args[i], XtNlabel, text);   i++;
2084         XtSetValues(titleWidget, args, i);
2085     }
2086     i = 0;
2087     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
2088     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
2089     XtSetValues(shellWidget, args, i);
2090     XSync(xDisplay, False);
2091 }
2092
2093
2094 static int
2095 NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
2096 {
2097     return 0;
2098 }
2099
2100 void
2101 DisplayIcsInteractionTitle (String message)
2102 {
2103   if (oldICSInteractionTitle == NULL) {
2104     /* Magic to find the old window title, adapted from vim */
2105     char *wina = getenv("WINDOWID");
2106     if (wina != NULL) {
2107       Window win = (Window) atoi(wina);
2108       Window root, parent, *children;
2109       unsigned int nchildren;
2110       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
2111       for (;;) {
2112         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
2113         if (!XQueryTree(xDisplay, win, &root, &parent,
2114                         &children, &nchildren)) break;
2115         if (children) XFree((void *)children);
2116         if (parent == root || parent == 0) break;
2117         win = parent;
2118       }
2119       XSetErrorHandler(oldHandler);
2120     }
2121     if (oldICSInteractionTitle == NULL) {
2122       oldICSInteractionTitle = "xterm";
2123     }
2124   }
2125   printf("\033]0;%s\007", message);
2126   fflush(stdout);
2127 }
2128
2129
2130 XtIntervalId delayedEventTimerXID = 0;
2131 DelayedEventCallback delayedEventCallback = 0;
2132
2133 void
2134 FireDelayedEvent ()
2135 {
2136     delayedEventTimerXID = 0;
2137     delayedEventCallback();
2138 }
2139
2140 void
2141 ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
2142 {
2143     if(delayedEventTimerXID && delayedEventCallback == cb)
2144         // [HGM] alive: replace, rather than add or flush identical event
2145         XtRemoveTimeOut(delayedEventTimerXID);
2146     delayedEventCallback = cb;
2147     delayedEventTimerXID =
2148       XtAppAddTimeOut(appContext, millisec,
2149                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
2150 }
2151
2152 DelayedEventCallback
2153 GetDelayedEvent ()
2154 {
2155   if (delayedEventTimerXID) {
2156     return delayedEventCallback;
2157   } else {
2158     return NULL;
2159   }
2160 }
2161
2162 void
2163 CancelDelayedEvent ()
2164 {
2165   if (delayedEventTimerXID) {
2166     XtRemoveTimeOut(delayedEventTimerXID);
2167     delayedEventTimerXID = 0;
2168   }
2169 }
2170
2171 XtIntervalId loadGameTimerXID = 0;
2172
2173 int
2174 LoadGameTimerRunning ()
2175 {
2176     return loadGameTimerXID != 0;
2177 }
2178
2179 int
2180 StopLoadGameTimer ()
2181 {
2182     if (loadGameTimerXID != 0) {
2183         XtRemoveTimeOut(loadGameTimerXID);
2184         loadGameTimerXID = 0;
2185         return TRUE;
2186     } else {
2187         return FALSE;
2188     }
2189 }
2190
2191 void
2192 LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
2193 {
2194     loadGameTimerXID = 0;
2195     AutoPlayGameLoop();
2196 }
2197
2198 void
2199 StartLoadGameTimer (long millisec)
2200 {
2201     loadGameTimerXID =
2202       XtAppAddTimeOut(appContext, millisec,
2203                       (XtTimerCallbackProc) LoadGameTimerCallback,
2204                       (XtPointer) 0);
2205 }
2206
2207 XtIntervalId analysisClockXID = 0;
2208
2209 void
2210 AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
2211 {
2212     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
2213          || appData.icsEngineAnalyze) { // [DM]
2214         AnalysisPeriodicEvent(0);
2215         StartAnalysisClock();
2216     }
2217 }
2218
2219 void
2220 StartAnalysisClock ()
2221 {
2222     analysisClockXID =
2223       XtAppAddTimeOut(appContext, 2000,
2224                       (XtTimerCallbackProc) AnalysisClockCallback,
2225                       (XtPointer) 0);
2226 }
2227
2228 XtIntervalId clockTimerXID = 0;
2229
2230 int
2231 ClockTimerRunning ()
2232 {
2233     return clockTimerXID != 0;
2234 }
2235
2236 int
2237 StopClockTimer ()
2238 {
2239     if (clockTimerXID != 0) {
2240         XtRemoveTimeOut(clockTimerXID);
2241         clockTimerXID = 0;
2242         return TRUE;
2243     } else {
2244         return FALSE;
2245     }
2246 }
2247
2248 void
2249 ClockTimerCallback (XtPointer arg, XtIntervalId *id)
2250 {
2251     clockTimerXID = 0;
2252     DecrementClocks();
2253 }
2254
2255 void
2256 StartClockTimer (long millisec)
2257 {
2258     clockTimerXID =
2259       XtAppAddTimeOut(appContext, millisec,
2260                       (XtTimerCallbackProc) ClockTimerCallback,
2261                       (XtPointer) 0);
2262 }
2263
2264 void
2265 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
2266 {
2267     char buf[MSG_SIZ];
2268     Arg args[16];
2269     Widget w = (Widget) opt->handle;
2270
2271     /* check for low time warning */
2272     Pixel foregroundOrWarningColor = timerForegroundPixel;
2273
2274     if (timer > 0 &&
2275         appData.lowTimeWarning &&
2276         (timer / 1000) < appData.icsAlarmTime)
2277       foregroundOrWarningColor = lowTimeWarningColor;
2278
2279     if (appData.clockMode) {
2280       snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
2281       XtSetArg(args[0], XtNlabel, buf);
2282     } else {
2283       snprintf(buf, MSG_SIZ, "%s  ", color);
2284       XtSetArg(args[0], XtNlabel, buf);
2285     }
2286
2287     if (highlight) {
2288
2289         XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
2290         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
2291     } else {
2292         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
2293         XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
2294     }
2295
2296     XtSetValues(w, args, 3);
2297 }
2298
2299 static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
2300
2301 void
2302 SetClockIcon (int color)
2303 {
2304     Arg args[16];
2305     Pixmap pm = *clockIcons[color];
2306     if (iconPixmap != pm) {
2307         iconPixmap = pm;
2308         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
2309         XtSetValues(shellWidget, args, 1);
2310     }
2311 }
2312
2313 #define INPUT_SOURCE_BUF_SIZE 8192
2314
2315 typedef struct {
2316     CPKind kind;
2317     int fd;
2318     int lineByLine;
2319     char *unused;
2320     InputCallback func;
2321     XtInputId xid;
2322     char buf[INPUT_SOURCE_BUF_SIZE];
2323     VOIDSTAR closure;
2324 } InputSource;
2325
2326 void
2327 DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
2328 {
2329     InputSource *is = (InputSource *) closure;
2330     int count;
2331     int error;
2332     char *p, *q;
2333
2334     if (is->lineByLine) {
2335         count = read(is->fd, is->unused,
2336                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
2337         if (count <= 0) {
2338             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
2339             return;
2340         }
2341         is->unused += count;
2342         p = is->buf;
2343         while (p < is->unused) {
2344             q = memchr(p, '\n', is->unused - p);
2345             if (q == NULL) break;
2346             q++;
2347             (is->func)(is, is->closure, p, q - p, 0);
2348             p = q;
2349         }
2350         q = is->buf;
2351         while (p < is->unused) {
2352             *q++ = *p++;
2353         }
2354         is->unused = q;
2355     } else {
2356         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
2357         if (count == -1)
2358           error = errno;
2359         else
2360           error = 0;
2361         (is->func)(is, is->closure, is->buf, count, error);
2362     }
2363 }
2364
2365 InputSourceRef
2366 AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
2367 {
2368     InputSource *is;
2369     ChildProc *cp = (ChildProc *) pr;
2370
2371     is = (InputSource *) calloc(1, sizeof(InputSource));
2372     is->lineByLine = lineByLine;
2373     is->func = func;
2374     if (pr == NoProc) {
2375         is->kind = CPReal;
2376         is->fd = fileno(stdin);
2377     } else {
2378         is->kind = cp->kind;
2379         is->fd = cp->fdFrom;
2380     }
2381     if (lineByLine) {
2382         is->unused = is->buf;
2383     }
2384
2385     is->xid = XtAppAddInput(appContext, is->fd,
2386                             (XtPointer) (XtInputReadMask),
2387                             (XtInputCallbackProc) DoInputCallback,
2388                             (XtPointer) is);
2389     is->closure = closure;
2390     return (InputSourceRef) is;
2391 }
2392
2393 void
2394 RemoveInputSource (InputSourceRef isr)
2395 {
2396     InputSource *is = (InputSource *) isr;
2397
2398     if (is->xid == 0) return;
2399     XtRemoveInput(is->xid);
2400     is->xid = 0;
2401 }
2402
2403 #ifndef HAVE_USLEEP
2404
2405 static Boolean frameWaiting;
2406
2407 static RETSIGTYPE
2408 FrameAlarm (int sig)
2409 {
2410   frameWaiting = False;
2411   /* In case System-V style signals.  Needed?? */
2412   signal(SIGALRM, FrameAlarm);
2413 }
2414
2415 void
2416 FrameDelay (int time)
2417 {
2418   struct itimerval delay;
2419
2420   XSync(xDisplay, False);
2421
2422   if (time > 0) {
2423     frameWaiting = True;
2424     signal(SIGALRM, FrameAlarm);
2425     delay.it_interval.tv_sec =
2426       delay.it_value.tv_sec = time / 1000;
2427     delay.it_interval.tv_usec =
2428       delay.it_value.tv_usec = (time % 1000) * 1000;
2429     setitimer(ITIMER_REAL, &delay, NULL);
2430     while (frameWaiting) pause();
2431     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2432     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2433     setitimer(ITIMER_REAL, &delay, NULL);
2434   }
2435 }
2436
2437 #else
2438
2439 void
2440 FrameDelay (int time)
2441 {
2442   XSync(xDisplay, False);
2443   if (time > 0)
2444     usleep(time * 1000);
2445 }
2446
2447 #endif
2448
2449 static void
2450 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2451 {
2452     char buf[MSG_SIZ], *logoName = buf;
2453     if(appData.logo[n][0]) {
2454         logoName = appData.logo[n];
2455     } else if(appData.autoLogo) {
2456         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2457             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2458         } else if(appData.directory[n] && appData.directory[n][0]) {
2459             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2460         }
2461     }
2462     if(logoName[0])
2463         { ASSIGN(cps->programLogo, logoName); }
2464 }
2465
2466 void
2467 UpdateLogos (int displ)
2468 {
2469     if(optList[W_WHITE-1].handle == NULL) return;
2470     LoadLogo(&first, 0, 0);
2471     LoadLogo(&second, 1, appData.icsActive);
2472     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2473     return;
2474 }
2475