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