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