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