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