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