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