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