removed two more translations from debug output
[xboard.git] / gtk / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  *
4  * Copyright 1991 by Digital Equipment Corporation, Maynard,
5  * Massachusetts.
6  *
7  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
8  * 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
9  *
10  * The following terms apply to Digital Equipment Corporation's copyright
11  * interest in XBoard:
12  * ------------------------------------------------------------------------
13  * All Rights Reserved
14  *
15  * Permission to use, copy, modify, and distribute this software and its
16  * documentation for any purpose and without fee is hereby granted,
17  * provided that the above copyright notice appear in all copies and that
18  * both that copyright notice and this permission notice appear in
19  * supporting documentation, and that the name of Digital not be
20  * used in advertising or publicity pertaining to distribution of the
21  * software without specific, written prior permission.
22  *
23  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29  * SOFTWARE.
30  * ------------------------------------------------------------------------
31  *
32  * The following terms apply to the enhanced version of XBoard
33  * distributed by the Free Software Foundation:
34  * ------------------------------------------------------------------------
35  *
36  * GNU XBoard is free software: you can redistribute it and/or modify
37  * it under the terms of the GNU General Public License as published by
38  * the Free Software Foundation, either version 3 of the License, or (at
39  * your option) any later version.
40  *
41  * GNU XBoard is distributed in the hope that it will be useful, but
42  * WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44  * General Public License for more details.
45  *
46  * You should have received a copy of the GNU General Public License
47  * along with this program. If not, see http://www.gnu.org/licenses/.  *
48  *
49  *------------------------------------------------------------------------
50  ** See the file ChangeLog for a revision history.  */
51
52 #define HIGHDRAG 1
53
54 #include "config.h"
55
56 #include <stdio.h>
57 #include <ctype.h>
58 #include <signal.h>
59 #include <errno.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <pwd.h>
63 #include <math.h>
64 #include <cairo/cairo.h>
65 #include <cairo/cairo-xlib.h>
66 #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 "menus.h"
163 #include "board.h"
164 #include "dialogs.h"
165 #include "engineoutput.h"
166 #include "usystem.h"
167 #include "gettext.h"
168 #include "draw.h"
169
170
171 #ifdef __EMX__
172 #ifndef HAVE_USLEEP
173 #define HAVE_USLEEP
174 #endif
175 #define usleep(t)   _sleep2(((t)+500)/1000)
176 #endif
177
178 #ifdef ENABLE_NLS
179 # define  _(s) gettext (s)
180 # define N_(s) gettext_noop (s)
181 #else
182 # define  _(s) (s)
183 # define N_(s)  s
184 #endif
185
186 int main P((int argc, char **argv));
187 RETSIGTYPE CmailSigHandler P((int sig));
188 RETSIGTYPE IntSigHandler P((int sig));
189 RETSIGTYPE TermSizeSigHandler P((int sig));
190 #if ENABLE_NLS
191 char *InsertPxlSize P((char *pattern, int targetPxlSize));
192 XFontSet CreateFontSet P((char *base_fnt_lst));
193 #else
194 char *FindFont P((char *pattern, int targetPxlSize));
195 #endif
196 void DelayedDrag P((void));
197 void ICSInputBoxPopUp P((void));
198 void MoveTypeInProc P((GdkEventKey *eventkey));
199 gboolean KeyPressProc P((GtkWindow *window, GdkEventKey *eventkey, gpointer data));
200 Boolean TempBackwardActive = False;
201 void DisplayMove P((int moveNumber));
202 void update_ics_width P(());
203 int CopyMemoProc P(());
204 static gboolean EventProc P((GtkWidget *widget, GdkEvent *event, gpointer g));
205
206 #ifdef TODO_GTK
207 #if ENABLE_NLS
208 XFontSet fontSet, clockFontSet;
209 #else
210 Font clockFontID;
211 XFontStruct *clockFontStruct;
212 #endif
213 Font coordFontID, countFontID;
214 XFontStruct *coordFontStruct, *countFontStruct;
215 #else
216 void *shellWidget, *formWidget, *boardWidget, *titleWidget, *dropMenu, *menuBarWidget;
217 GtkWidget       *mainwindow;
218 #endif
219 Option *optList; // contains all widgets of main window
220 char *layoutName;
221
222 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
223
224 /* pixbufs */
225 static GdkPixbuf       *mainwindowIcon=NULL;
226 static GdkPixbuf       *WhiteIcon=NULL;
227 static GdkPixbuf       *BlackIcon=NULL;
228
229 /* key board accelerators */
230 GtkAccelGroup *GtkAccelerators;
231
232 typedef unsigned int BoardSize;
233 BoardSize boardSize;
234 Boolean chessProgram;
235
236 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
237 int smallLayout = 0, tinyLayout = 0,
238   marginW, marginH, // [HGM] for run-time resizing
239   fromX = -1, fromY = -1, toX, toY, commentUp = False,
240   errorExitStatus = -1, defaultLineGap;
241 #ifdef TODO_GTK
242 Dimension textHeight;
243 #endif
244 char *chessDir, *programName, *programVersion;
245 Boolean alwaysOnTop = False;
246 char *icsTextMenuString;
247 char *icsNames;
248 char *firstChessProgramNames;
249 char *secondChessProgramNames;
250
251 WindowPlacement wpMain;
252 WindowPlacement wpConsole;
253 WindowPlacement wpComment;
254 WindowPlacement wpMoveHistory;
255 WindowPlacement wpEvalGraph;
256 WindowPlacement wpEngineOutput;
257 WindowPlacement wpGameList;
258 WindowPlacement wpTags;
259
260 /* This magic number is the number of intermediate frames used
261    in each half of the animation. For short moves it's reduced
262    by 1. The total number of frames will be factor * 2 + 1.  */
263 #define kFactor    4
264
265 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
266
267 typedef struct {
268     char piece;
269     char* widget;
270 } DropMenuEnables;
271
272 DropMenuEnables dmEnables[] = {
273     { 'P', "Pawn" },
274     { 'N', "Knight" },
275     { 'B', "Bishop" },
276     { 'R', "Rook" },
277     { 'Q', "Queen" }
278 };
279
280 #ifdef TODO_GTK
281 XtResource clientResources[] = {
282     { "flashCount", "flashCount", XtRInt, sizeof(int),
283         XtOffset(AppDataPtr, flashCount), XtRImmediate,
284         (XtPointer) FLASH_COUNT  },
285 };
286 #endif
287
288 /* keyboard shortcuts not yet transistioned int menuitem @ menu.c */
289 char globalTranslations[] =
290   ":Ctrl<Key>Down: LoadSelectedProc(3) \n \
291    :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
292    :<KeyDown>Return: TempBackwardProc() \n \
293    :<KeyUp>Return: TempForwardProc() \n";
294
295 char ICSInputTranslations[] =
296     "<Key>Up: UpKeyProc() \n "
297     "<Key>Down: DownKeyProc() \n "
298     "<Key>Return: EnterKeyProc() \n";
299
300 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
301 //             as the widget is destroyed before the up-click can call extend-end
302 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
303
304 #ifdef TODO_GTK
305 String xboardResources[] = {
306     "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
307     NULL
308   };
309 #endif
310
311 void
312 BoardToTop ()
313 {
314   gtk_window_present(GTK_WINDOW(shells[BoardWindow]));
315 }
316
317 //---------------------------------------------------------------------------------------------------------
318 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
319 #define XBOARD True
320 #define JAWS_ARGS
321 #define CW_USEDEFAULT (1<<31)
322 #define ICS_TEXT_MENU_SIZE 90
323 #define DEBUG_FILE "xboard.debug"
324 #define SetCurrentDirectory chdir
325 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
326 #define OPTCHAR "-"
327 #define SEPCHAR " "
328
329 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
330 #include "args.h"
331
332 // front-end part of option handling
333
334 // [HGM] This platform-dependent table provides the location for storing the color info
335 extern char *crWhite, * crBlack;
336
337 void *
338 colorVariable[] = {
339   &appData.whitePieceColor,
340   &appData.blackPieceColor,
341   &appData.lightSquareColor,
342   &appData.darkSquareColor,
343   &appData.highlightSquareColor,
344   &appData.premoveHighlightColor,
345   &appData.lowTimeWarningColor,
346   NULL,
347   NULL,
348   NULL,
349   NULL,
350   NULL,
351   &crWhite,
352   &crBlack,
353   NULL
354 };
355
356 // [HGM] font: keep a font for each square size, even non-stndard ones
357 #define NUM_SIZES 18
358 #define MAX_SIZE 130
359 Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
360 char *fontTable[NUM_FONTS][MAX_SIZE];
361
362 void
363 ParseFont (char *name, int number)
364 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
365   int size;
366   if(sscanf(name, "size%d:", &size)) {
367     // [HGM] font: font is meant for specific boardSize (likely from settings file);
368     //       defer processing it until we know if it matches our board size
369     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
370         fontTable[number][size] = strdup(strchr(name, ':')+1);
371         fontValid[number][size] = True;
372     }
373     return;
374   }
375   switch(number) {
376     case 0: // CLOCK_FONT
377         appData.clockFont = strdup(name);
378       break;
379     case 1: // MESSAGE_FONT
380         appData.font = strdup(name);
381       break;
382     case 2: // COORD_FONT
383         appData.coordFont = strdup(name);
384       break;
385     default:
386       return;
387   }
388   fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
389 }
390
391 void
392 SetFontDefaults ()
393 { // only 2 fonts currently
394   appData.clockFont = CLOCK_FONT_NAME;
395   appData.coordFont = COORD_FONT_NAME;
396   appData.font  =   DEFAULT_FONT_NAME;
397 }
398
399 void
400 CreateFonts ()
401 { // no-op, until we identify the code for this already in XBoard and move it here
402 }
403
404 void
405 ParseColor (int n, char *name)
406 { // in XBoard, just copy the color-name string
407   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
408 }
409
410 void
411 ParseTextAttribs (ColorClass cc, char *s)
412 {
413     (&appData.colorShout)[cc] = strdup(s);
414 }
415
416 void
417 ParseBoardSize (void *addr, char *name)
418 {
419     appData.boardSize = strdup(name);
420 }
421
422 void
423 LoadAllSounds ()
424 { // In XBoard the sound-playing program takes care of obtaining the actual sound
425 }
426
427 void
428 SetCommPortDefaults ()
429 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
430 }
431
432 // [HGM] args: these three cases taken out to stay in front-end
433 void
434 SaveFontArg (FILE *f, ArgDescriptor *ad)
435 {
436   char *name;
437   int i, n = (int)(intptr_t)ad->argLoc;
438   switch(n) {
439     case 0: // CLOCK_FONT
440         name = appData.clockFont;
441       break;
442     case 1: // MESSAGE_FONT
443         name = appData.font;
444       break;
445     case 2: // COORD_FONT
446         name = appData.coordFont;
447       break;
448     default:
449       return;
450   }
451   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
452     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
453         fontTable[n][squareSize] = strdup(name);
454         fontValid[n][squareSize] = True;
455         break;
456   }
457   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
458     fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
459 }
460
461 void
462 ExportSounds ()
463 { // nothing to do, as the sounds are at all times represented by their text-string names already
464 }
465
466 void
467 SaveAttribsArg (FILE *f, ArgDescriptor *ad)
468 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
469         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
470 }
471
472 void
473 SaveColor (FILE *f, ArgDescriptor *ad)
474 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
475         if(colorVariable[(int)(intptr_t)ad->argLoc])
476         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
477 }
478
479 void
480 SaveBoardSize (FILE *f, char *name, void *addr)
481 { // wrapper to shield back-end from BoardSize & sizeInfo
482   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
483 }
484
485 void
486 ParseCommPortSettings (char *s)
487 { // no such option in XBoard (yet)
488 }
489
490 int frameX, frameY;
491
492 void
493 GetActualPlacement (GtkWidget *shell, WindowPlacement *wp)
494 {
495   GtkAllocation a;
496   if(!shell) return;
497   gtk_widget_get_allocation(shell, &a);
498   gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
499   wp->x = a.x;
500   wp->y = a.y;
501   wp->width = a.width;
502   wp->height = a.height;
503 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
504   frameX = 3; frameY = 3; // remember to decide if windows touch
505 }
506
507 void
508 GetWindowCoords ()
509 { // wrapper to shield use of window handles from back-end (make addressible by number?)
510   // In XBoard this will have to wait until awareness of window parameters is implemented
511   GetActualPlacement(shellWidget, &wpMain);
512   if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
513   if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
514   if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
515   if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
516   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
517   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
518 }
519
520 void
521 PrintCommPortSettings (FILE *f, char *name)
522 { // This option does not exist in XBoard
523 }
524
525 void
526 EnsureOnScreen (int *x, int *y, int minX, int minY)
527 {
528   return;
529 }
530
531 int
532 MainWindowUp ()
533 { // [HGM] args: allows testing if main window is realized from back-end
534   return DialogExists(BoardWindow);
535 }
536
537 void
538 PopUpStartupDialog ()
539 {  // start menu not implemented in XBoard
540 }
541
542 char *
543 ConvertToLine (int argc, char **argv)
544 {
545   static char line[128*1024], buf[1024];
546   int i;
547
548   line[0] = NULLCHAR;
549   for(i=1; i<argc; i++)
550     {
551       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
552           && argv[i][0] != '{' )
553         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
554       else
555         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
556       strncat(line, buf, 128*1024 - strlen(line) - 1 );
557     }
558
559   line[strlen(line)-1] = NULLCHAR;
560   return line;
561 }
562
563 //--------------------------------------------------------------------------------------------
564
565 void
566 ResizeBoardWindow (int w, int h, int inhibit)
567 {
568     w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
569     h += marginH;
570     gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
571 }
572
573 int
574 MakeColors ()
575 {   // dummy, as the GTK code does not make colors in advance
576     return FALSE;
577 }
578
579 void
580 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
581 {   // determine what fonts to use, and create them
582 #ifdef TODO_GTK
583     XrmValue vTo;
584     XrmDatabase xdb;
585
586     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
587         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
588     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
589         appData.font = fontTable[MESSAGE_FONT][squareSize];
590     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
591         appData.coordFont = fontTable[COORD_FONT][squareSize];
592
593 #if ENABLE_NLS
594     appData.font = InsertPxlSize(appData.font, fontPxlSize);
595     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
596     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
597     fontSet = CreateFontSet(appData.font);
598     clockFontSet = CreateFontSet(appData.clockFont);
599     {
600       /* For the coordFont, use the 0th font of the fontset. */
601       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
602       XFontStruct **font_struct_list;
603       XFontSetExtents *fontSize;
604       char **font_name_list;
605       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
606       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
607       coordFontStruct = XQueryFont(xDisplay, coordFontID);
608       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
609       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
610     }
611 #else
612     appData.font = FindFont(appData.font, fontPxlSize);
613     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
614     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
615     clockFontID = XLoadFont(xDisplay, appData.clockFont);
616     clockFontStruct = XQueryFont(xDisplay, clockFontID);
617     coordFontID = XLoadFont(xDisplay, appData.coordFont);
618     coordFontStruct = XQueryFont(xDisplay, coordFontID);
619     // textHeight in !NLS mode!
620 #endif
621     countFontID = coordFontID;  // [HGM] holdings
622     countFontStruct = coordFontStruct;
623
624     xdb = XtDatabase(xDisplay);
625 #if ENABLE_NLS
626     XrmPutLineResource(&xdb, "*international: True");
627     vTo.size = sizeof(XFontSet);
628     vTo.addr = (XtPointer) &fontSet;
629     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
630 #else
631     XrmPutStringResource(&xdb, "*font", appData.font);
632 #endif
633 #endif
634 }
635
636 char *
637 PrintArg (ArgType t)
638 {
639   char *p="";
640   switch(t) {
641     case ArgZ:
642     case ArgInt:      p = " N"; break;
643     case ArgString:   p = " STR"; break;
644     case ArgBoolean:  p = " TF"; break;
645     case ArgSettingsFilename:
646     case ArgFilename: p = " FILE"; break;
647     case ArgX:        p = " Nx"; break;
648     case ArgY:        p = " Ny"; break;
649     case ArgAttribs:  p = " TEXTCOL"; break;
650     case ArgColor:    p = " COL"; break;
651     case ArgFont:     p = " FONT"; break;
652     case ArgBoardSize: p = " SIZE"; break;
653     case ArgFloat: p = " FLOAT"; break;
654     case ArgTrue:
655     case ArgFalse:
656     case ArgTwo:
657     case ArgNone:
658     case ArgCommSettings:
659       break;
660   }
661   return p;
662 }
663
664 void
665 PrintOptions ()
666 {
667   char buf[MSG_SIZ];
668   int len=0;
669   ArgDescriptor *q, *p = argDescriptors+5;
670   printf("\nXBoard accepts the following options:\n"
671          "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
672          " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
673          " SIZE = board-size spec(s)\n"
674          " Within parentheses are short forms, or options to set to true or false.\n"
675          " Persistent options (saved in the settings file) are marked with *)\n\n");
676   while(p->argName) {
677     if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
678     snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
679     if(p->save) strcat(buf+len, "*");
680     for(q=p+1; q->argLoc == p->argLoc; q++) {
681       if(q->argName[0] == '-') continue;
682       strcat(buf+len, q == p+1 ? " (" : " ");
683       sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
684     }
685     if(q != p+1) strcat(buf+len, ")");
686     len = strlen(buf);
687     if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
688     p = q;
689   }
690   if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
691 }
692
693 int
694 main (int argc, char **argv)
695 {
696     int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
697     int boardWidth, boardHeight, w, h;
698     char *p;
699     int forceMono = False;
700
701     srandom(time(0)); // [HGM] book: make random truly random
702
703     setbuf(stdout, NULL);
704     setbuf(stderr, NULL);
705     debugFP = stderr;
706
707     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
708         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
709         exit(0);
710     }
711
712     if(argc > 1 && !strcmp(argv[1], "--help" )) {
713         PrintOptions();
714         exit(0);
715     }
716
717     /* set up GTK */
718     gtk_init (&argc, &argv);
719
720     /* set up keyboard accelerators group */
721     GtkAccelerators = gtk_accel_group_new();
722
723     programName = strrchr(argv[0], '/');
724     if (programName == NULL)
725       programName = argv[0];
726     else
727       programName++;
728
729 #ifdef ENABLE_NLS
730 //    if (appData.debugMode) {
731 //      fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
732 //    }
733
734     bindtextdomain(PACKAGE, LOCALEDIR);
735     bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
736     textdomain(PACKAGE);
737 #endif
738
739     appData.boardSize = "";
740     InitAppData(ConvertToLine(argc, argv));
741     p = getenv("HOME");
742     if (p == NULL) p = "/tmp";
743     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
744     gameCopyFilename = (char*) malloc(i);
745     gamePasteFilename = (char*) malloc(i);
746     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
747     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
748
749     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
750         static char buf[MSG_SIZ];
751         EscapeExpand(buf, appData.firstInitString);
752         appData.firstInitString = strdup(buf);
753         EscapeExpand(buf, appData.secondInitString);
754         appData.secondInitString = strdup(buf);
755         EscapeExpand(buf, appData.firstComputerString);
756         appData.firstComputerString = strdup(buf);
757         EscapeExpand(buf, appData.secondComputerString);
758         appData.secondComputerString = strdup(buf);
759     }
760
761     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
762         chessDir = ".";
763     } else {
764         if (chdir(chessDir) != 0) {
765             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
766             perror(chessDir);
767             exit(1);
768         }
769     }
770
771     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
772         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
773         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
774            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
775            exit(errno);
776         }
777         setbuf(debugFP, NULL);
778     }
779
780 #if ENABLE_NLS
781     if (appData.debugMode) {
782       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
783     }
784 #endif
785
786     /* [HGM,HR] make sure board size is acceptable */
787     if(appData.NrFiles > BOARD_FILES ||
788        appData.NrRanks > BOARD_RANKS   )
789          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
790
791 #if !HIGHDRAG
792     /* This feature does not work; animation needs a rewrite */
793     appData.highlightDragging = FALSE;
794 #endif
795     InitBackEnd1();
796
797         gameInfo.variant = StringToVariant(appData.variant);
798         InitPosition(FALSE);
799
800     /*
801      * determine size, based on supplied or remembered -size, or screen size
802      */
803     if (isdigit(appData.boardSize[0])) {
804         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
805                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
806                    &fontPxlSize, &smallLayout, &tinyLayout);
807         if (i == 0) {
808             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
809                     programName, appData.boardSize);
810             exit(2);
811         }
812         if (i < 7) {
813             /* Find some defaults; use the nearest known size */
814             SizeDefaults *szd, *nearest;
815             int distance = 99999;
816             nearest = szd = sizeDefaults;
817             while (szd->name != NULL) {
818                 if (abs(szd->squareSize - squareSize) < distance) {
819                     nearest = szd;
820                     distance = abs(szd->squareSize - squareSize);
821                     if (distance == 0) break;
822                 }
823                 szd++;
824             }
825             if (i < 2) lineGap = nearest->lineGap;
826             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
827             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
828             if (i < 5) fontPxlSize = nearest->fontPxlSize;
829             if (i < 6) smallLayout = nearest->smallLayout;
830             if (i < 7) tinyLayout = nearest->tinyLayout;
831         }
832     } else {
833         SizeDefaults *szd = sizeDefaults;
834         if (*appData.boardSize == NULLCHAR) {
835             GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
836             guint screenwidth = gdk_screen_get_width(screen);
837             guint screenheight = gdk_screen_get_height(screen);
838             while (screenwidth < szd->minScreenSize ||
839                    screenheight < szd->minScreenSize) {
840               szd++;
841             }
842             if (szd->name == NULL) szd--;
843             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
844         } else {
845             while (szd->name != NULL &&
846                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
847             if (szd->name == NULL) {
848                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
849                         programName, appData.boardSize);
850                 exit(2);
851             }
852         }
853         squareSize = szd->squareSize;
854         lineGap = szd->lineGap;
855         clockFontPxlSize = szd->clockFontPxlSize;
856         coordFontPxlSize = szd->coordFontPxlSize;
857         fontPxlSize = szd->fontPxlSize;
858         smallLayout = szd->smallLayout;
859         tinyLayout = szd->tinyLayout;
860         // [HGM] font: use defaults from settings file if available and not overruled
861     }
862
863     defaultLineGap = lineGap;
864     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
865
866     /* [HR] height treated separately (hacked) */
867     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
868     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
869
870     /*
871      * Determine what fonts to use.
872      */
873 #ifdef TODO_GTK
874     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
875 #endif
876
877     /*
878      * Detect if there are not enough colors available and adapt.
879      */
880 #ifdef TODO_GTK
881     if (DefaultDepth(xDisplay, xScreen) <= 2) {
882       appData.monoMode = True;
883     }
884 #endif
885
886     forceMono = MakeColors();
887
888     if (forceMono) {
889       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
890               programName);
891         appData.monoMode = True;
892     }
893
894     ParseIcsTextColors();
895
896     /*
897      * widget hierarchy
898      */
899     if (tinyLayout) {
900         layoutName = "tinyLayout";
901     } else if (smallLayout) {
902         layoutName = "smallLayout";
903     } else {
904         layoutName = "normalLayout";
905     }
906
907     optList = BoardPopUp(squareSize, lineGap, (void*)
908 #ifdef TODO_GTK
909 #if ENABLE_NLS
910                                                 &clockFontSet);
911 #else
912                                                 clockFontStruct);
913 #endif
914 #else
915 0);
916 #endif
917     InitDrawingHandle(optList + W_BOARD);
918     shellWidget      = shells[BoardWindow];
919     currBoard        = &optList[W_BOARD];
920     boardWidget      = optList[W_BOARD].handle;
921     menuBarWidget    = optList[W_MENU].handle;
922     dropMenu         = optList[W_DROP].handle;
923     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
924 #ifdef TODO_GTK
925     formWidget  = XtParent(boardWidget);
926     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
927     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
928     XtGetValues(optList[W_WHITE].handle, args, 2);
929     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
930       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
931       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
932       XtGetValues(optList[W_PAUSE].handle, args, 2);
933     }
934 #endif
935
936     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
937     //       not need to go into InitDrawingSizes().
938
939     InitMenuMarkers();
940
941     // add accelerators to main shell
942     gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
943
944     /*
945      * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
946      */
947     WhiteIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
948     BlackIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
949     mainwindowIcon = WhiteIcon;
950     gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
951
952
953     /*
954      * Create a cursor for the board widget.
955      */
956 #ifdef TODO_GTK
957     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
958     XChangeWindowAttributes(xDisplay, xBoardWindow,
959                             CWCursor, &window_attributes);
960 #endif
961
962     /*
963      * Inhibit shell resizing.
964      */
965 #ifdef TODO_GTK
966     shellArgs[0].value = (XtArgVal) &w;
967     shellArgs[1].value = (XtArgVal) &h;
968     XtGetValues(shellWidget, shellArgs, 2);
969     shellArgs[4].value = shellArgs[2].value = w;
970     shellArgs[5].value = shellArgs[3].value = h;
971 //    XtSetValues(shellWidget, &shellArgs[2], 4);
972 #endif
973     {
974         GtkAllocation a;
975         gtk_widget_get_allocation(shells[BoardWindow], &a);
976         w = a.width; h = a.height;
977 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
978         gtk_widget_get_allocation(boardWidget, &a);
979         marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
980         marginH =  h - a.height + 13;
981         gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
982 //printf("margins h=%d v=%d\n", marginW, marginH);
983     }
984
985     CreateAnyPieces();
986     CreateGrid();
987
988     if(appData.logoSize)
989     {   // locate and read user logo
990         char buf[MSG_SIZ];
991         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
992         ASSIGN(userLogo, buf);
993     }
994
995     if (appData.animate || appData.animateDragging)
996       CreateAnimVars();
997
998     g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
999     g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1000
1001     /* [AS] Restore layout */
1002     if( wpMoveHistory.visible ) {
1003       HistoryPopUp();
1004     }
1005
1006     if( wpEvalGraph.visible )
1007       {
1008         EvalGraphPopUp();
1009       };
1010
1011     if( wpEngineOutput.visible ) {
1012       EngineOutputPopUp();
1013     }
1014
1015     InitBackEnd2();
1016
1017     if (errorExitStatus == -1) {
1018         if (appData.icsActive) {
1019             /* We now wait until we see "login:" from the ICS before
1020                sending the logon script (problems with timestamp otherwise) */
1021             /*ICSInitScript();*/
1022             if (appData.icsInputBox) ICSInputBoxPopUp();
1023         }
1024
1025     #ifdef SIGWINCH
1026     signal(SIGWINCH, TermSizeSigHandler);
1027     #endif
1028         signal(SIGINT, IntSigHandler);
1029         signal(SIGTERM, IntSigHandler);
1030         if (*appData.cmailGameName != NULLCHAR) {
1031             signal(SIGUSR1, CmailSigHandler);
1032         }
1033     }
1034
1035     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1036     InitPosition(TRUE);
1037     UpdateLogos(TRUE);
1038 //    XtSetKeyboardFocus(shellWidget, formWidget);
1039 #ifdef TODO_GTK
1040     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1041 #endif
1042
1043     /* check for GTK events and process them */
1044 //    gtk_main();
1045 while(1) {
1046 gtk_main_iteration();
1047 }
1048
1049     if (appData.debugMode) fclose(debugFP); // [DM] debug
1050     return 0;
1051 }
1052
1053 RETSIGTYPE
1054 TermSizeSigHandler (int sig)
1055 {
1056     update_ics_width();
1057 }
1058
1059 RETSIGTYPE
1060 IntSigHandler (int sig)
1061 {
1062     ExitEvent(sig);
1063 }
1064
1065 RETSIGTYPE
1066 CmailSigHandler (int sig)
1067 {
1068     int dummy = 0;
1069     int error;
1070
1071     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1072
1073     /* Activate call-back function CmailSigHandlerCallBack()             */
1074     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1075
1076     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1077 }
1078
1079 void
1080 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1081 {
1082     BoardToTop();
1083     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1084 }
1085 /**** end signal code ****/
1086
1087
1088 #define Abs(n) ((n)<0 ? -(n) : (n))
1089
1090 #ifdef ENABLE_NLS
1091 char *
1092 InsertPxlSize (char *pattern, int targetPxlSize)
1093 {
1094     char *base_fnt_lst, strInt[12], *p, *q;
1095     int alternatives, i, len, strIntLen;
1096
1097     /*
1098      * Replace the "*" (if present) in the pixel-size slot of each
1099      * alternative with the targetPxlSize.
1100      */
1101     p = pattern;
1102     alternatives = 1;
1103     while ((p = strchr(p, ',')) != NULL) {
1104       alternatives++;
1105       p++;
1106     }
1107     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1108     strIntLen = strlen(strInt);
1109     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1110
1111     p = pattern;
1112     q = base_fnt_lst;
1113     while (alternatives--) {
1114       char *comma = strchr(p, ',');
1115       for (i=0; i<14; i++) {
1116         char *hyphen = strchr(p, '-');
1117         if (!hyphen) break;
1118         if (comma && hyphen > comma) break;
1119         len = hyphen + 1 - p;
1120         if (i == 7 && *p == '*' && len == 2) {
1121           p += len;
1122           memcpy(q, strInt, strIntLen);
1123           q += strIntLen;
1124           *q++ = '-';
1125         } else {
1126           memcpy(q, p, len);
1127           p += len;
1128           q += len;
1129         }
1130       }
1131       if (!comma) break;
1132       len = comma + 1 - p;
1133       memcpy(q, p, len);
1134       p += len;
1135       q += len;
1136     }
1137     strcpy(q, p);
1138
1139     return base_fnt_lst;
1140 }
1141
1142 #ifdef TODO_GTK
1143 XFontSet
1144 CreateFontSet (char *base_fnt_lst)
1145 {
1146     XFontSet fntSet;
1147     char **missing_list;
1148     int missing_count;
1149     char *def_string;
1150
1151     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1152                             &missing_list, &missing_count, &def_string);
1153     if (appData.debugMode) {
1154       int i, count;
1155       XFontStruct **font_struct_list;
1156       char **font_name_list;
1157       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1158       if (fntSet) {
1159         fprintf(debugFP, " got list %s, locale %s\n",
1160                 XBaseFontNameListOfFontSet(fntSet),
1161                 XLocaleOfFontSet(fntSet));
1162         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1163         for (i = 0; i < count; i++) {
1164           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1165         }
1166       }
1167       for (i = 0; i < missing_count; i++) {
1168         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1169       }
1170     }
1171     if (fntSet == NULL) {
1172       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1173       exit(2);
1174     }
1175     return fntSet;
1176 }
1177 #endif
1178 #else // not ENABLE_NLS
1179 /*
1180  * Find a font that matches "pattern" that is as close as
1181  * possible to the targetPxlSize.  Prefer fonts that are k
1182  * pixels smaller to fonts that are k pixels larger.  The
1183  * pattern must be in the X Consortium standard format,
1184  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1185  * The return value should be freed with XtFree when no
1186  * longer needed.
1187  */
1188 char *
1189 FindFont (char *pattern, int targetPxlSize)
1190 {
1191     char **fonts, *p, *best, *scalable, *scalableTail;
1192     int i, j, nfonts, minerr, err, pxlSize;
1193
1194 #ifdef TODO_GTK
1195     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1196     if (nfonts < 1) {
1197         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1198                 programName, pattern);
1199         exit(2);
1200     }
1201
1202     best = fonts[0];
1203     scalable = NULL;
1204     minerr = 999999;
1205     for (i=0; i<nfonts; i++) {
1206         j = 0;
1207         p = fonts[i];
1208         if (*p != '-') continue;
1209         while (j < 7) {
1210             if (*p == NULLCHAR) break;
1211             if (*p++ == '-') j++;
1212         }
1213         if (j < 7) continue;
1214         pxlSize = atoi(p);
1215         if (pxlSize == 0) {
1216             scalable = fonts[i];
1217             scalableTail = p;
1218         } else {
1219             err = pxlSize - targetPxlSize;
1220             if (Abs(err) < Abs(minerr) ||
1221                 (minerr > 0 && err < 0 && -err == minerr)) {
1222                 best = fonts[i];
1223                 minerr = err;
1224             }
1225         }
1226     }
1227     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1228         /* If the error is too big and there is a scalable font,
1229            use the scalable font. */
1230         int headlen = scalableTail - scalable;
1231         p = (char *) XtMalloc(strlen(scalable) + 10);
1232         while (isdigit(*scalableTail)) scalableTail++;
1233         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1234     } else {
1235         p = (char *) XtMalloc(strlen(best) + 2);
1236         safeStrCpy(p, best, strlen(best)+1 );
1237     }
1238     if (appData.debugMode) {
1239         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
1240                 pattern, targetPxlSize, p);
1241     }
1242     XFreeFontNames(fonts);
1243 #endif
1244     return p;
1245 }
1246 #endif
1247
1248 void
1249 EnableNamedMenuItem (char *menuRef, int state)
1250 {
1251     MenuItem *item = MenuNameToItem(menuRef);
1252
1253     if(item) gtk_widget_set_sensitive(item->handle, state);
1254 }
1255
1256 void
1257 EnableButtonBar (int state)
1258 {
1259 #ifdef TODO_GTK
1260     XtSetSensitive(optList[W_BUTTON].handle, state);
1261 #endif
1262 }
1263
1264
1265 void
1266 SetMenuEnables (Enables *enab)
1267 {
1268   while (enab->name != NULL) {
1269     EnableNamedMenuItem(enab->name, enab->value);
1270     enab++;
1271   }
1272 }
1273
1274 gboolean KeyPressProc(window, eventkey, data)
1275      GtkWindow *window;
1276      GdkEventKey  *eventkey;
1277      gpointer data;
1278 {
1279
1280     MoveTypeInProc(eventkey); // pop up for typed in moves
1281
1282 #ifdef TODO_GTK
1283     /* check for other key values */
1284     switch(eventkey->keyval) {
1285         case GDK_question:
1286           AboutGameEvent();
1287           break;
1288         default:
1289           break;
1290     }
1291 #endif
1292     return False;
1293 }
1294 #ifdef TODO_GTK
1295 void
1296 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1297 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1298     MenuItem *item;
1299     if(*nprms == 0) return;
1300     item = MenuNameToItem(prms[0]);
1301     if(item) ((MenuProc *) item->proc) ();
1302 }
1303 #endif
1304
1305 void
1306 SetupDropMenu ()
1307 {
1308 #ifdef TODO_GTK
1309     int i, j, count;
1310     char label[32];
1311     Arg args[16];
1312     Widget entry;
1313     char* p;
1314
1315     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1316         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1317         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1318                    dmEnables[i].piece);
1319         XtSetSensitive(entry, p != NULL || !appData.testLegality
1320                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1321                                        && !appData.icsActive));
1322         count = 0;
1323         while (p && *p++ == dmEnables[i].piece) count++;
1324         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1325         j = 0;
1326         XtSetArg(args[j], XtNlabel, label); j++;
1327         XtSetValues(entry, args, j);
1328     }
1329 #endif
1330 }
1331
1332 static void
1333 do_flash_delay (unsigned long msec)
1334 {
1335     TimeDelay(msec);
1336 }
1337
1338 void
1339 FlashDelay (int flash_delay)
1340 {
1341         if(flash_delay) do_flash_delay(flash_delay);
1342 }
1343
1344 double
1345 Fraction (int x, int start, int stop)
1346 {
1347    double f = ((double) x - start)/(stop - start);
1348    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1349    return f;
1350 }
1351
1352 static WindowPlacement wpNew;
1353
1354 void
1355 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1356 {
1357     int touch=0, fudge = 2, f = 2;
1358     GetActualPlacement(sh, wp);
1359     if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x)         < fudge) touch = 1; else // right touch
1360     if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x)            < fudge) touch = 2; else // left touch
1361     if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1362     if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y)    < fudge) touch = 4;      // top touch
1363 //printf("CoDrag: touch = %d x=%d w=%d x2=%d w2=%d fx=%d\n", touch, wpMain.x, wpMain.width, wp->x, wp->width, frameX);
1364     if(!touch ) return; // only windows that touch co-move
1365     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1366         int heightInc = wpNew.height - wpMain.height;
1367         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1368         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1369         wp->y += fracTop * heightInc;
1370         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1371 #ifdef TODO_GTK
1372         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1373 #endif
1374         wp->height += heightInc;
1375     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1376         int widthInc = wpNew.width - wpMain.width;
1377         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1378         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1379         wp->y += fracLeft * widthInc;
1380         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1381 #ifdef TODO_GTK
1382         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1383 #endif
1384         wp->width += widthInc;
1385     }
1386     wp->x += wpNew.x - wpMain.x;
1387     wp->y += wpNew.y - wpMain.y;
1388     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1389     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1390 #ifdef TODO_GTK
1391     XtSetArg(args[j], XtNx, wp->x); j++;
1392     XtSetArg(args[j], XtNy, wp->y); j++;
1393     XtSetValues(sh, args, j);
1394 #endif
1395         gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1396 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1397         gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1398 }
1399
1400 void
1401 ReSize (WindowPlacement *wp)
1402 {
1403         int sqx, sqy, w, h, lg = lineGap;
1404         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1405         sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1406         sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1407         if(sqy < sqx) sqx = sqy;
1408         if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1409             lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1410             sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1411             sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1412             if(sqy < sqx) sqx = sqy;
1413         }
1414         if(sqx != squareSize) {
1415 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1416             squareSize = sqx; // adopt new square size
1417             CreatePNGPieces(); // make newly scaled pieces
1418             InitDrawingSizes(0, 0); // creates grid etc.
1419         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1420         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1421         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1422         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1423         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1424 }
1425
1426 static guint delayedDragTag = 0;
1427
1428 void
1429 DragProc ()
1430 {
1431         static int busy;
1432         if(busy) return;
1433
1434         busy = 1;
1435 //      GetActualPlacement(shellWidget, &wpNew);
1436 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1437         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1438            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1439             busy = 0; return; // false alarm
1440         }
1441         ReSize(&wpNew);
1442         if(appData.useStickyWindows) {
1443             if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1444             if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1445             if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1446             if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1447         }
1448         wpMain = wpNew;
1449         DrawPosition(True, NULL);
1450         if(delayedDragTag) g_source_remove(delayedDragTag);
1451         delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1452         busy = 0;
1453 }
1454
1455 void
1456 DelayedDrag ()
1457 {
1458 //printf("old timr = %d\n", delayedDragTag);
1459     if(delayedDragTag) g_source_remove(delayedDragTag);
1460     delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1461 //printf("new timr = %d\n", delayedDragTag);
1462 }
1463
1464 static gboolean
1465 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1466 {
1467 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1468     // immediately
1469     wpNew.x = event->configure.x;
1470     wpNew.y = event->configure.y;
1471     wpNew.width  = event->configure.width;
1472     wpNew.height = event->configure.height;
1473     DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1474     return FALSE;
1475 }
1476
1477
1478
1479 /* Disable all user input other than deleting the window */
1480 static int frozen = 0;
1481
1482 void
1483 FreezeUI ()
1484 {
1485   if (frozen) return;
1486   /* Grab by a widget that doesn't accept input */
1487   gtk_grab_add(optList[W_MESSG].handle);
1488   frozen = 1;
1489 }
1490
1491 /* Undo a FreezeUI */
1492 void
1493 ThawUI ()
1494 {
1495   if (!frozen) return;
1496   gtk_grab_remove(optList[W_MESSG].handle);
1497   frozen = 0;
1498 }
1499
1500 void
1501 ModeHighlight ()
1502 {
1503     static int oldPausing = FALSE;
1504     static GameMode oldmode = (GameMode) -1;
1505     char *wname;
1506     if (!boardWidget) return;
1507
1508     if (pausing != oldPausing) {
1509         oldPausing = pausing;
1510         MarkMenuItem("Mode.Pause", pausing);
1511
1512         if (appData.showButtonBar) {
1513           /* Always toggle, don't set.  Previous code messes up when
1514              invoked while the button is pressed, as releasing it
1515              toggles the state again. */
1516             GdkColor color;
1517             gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1518             gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1519         }
1520     }
1521
1522     wname = ModeToWidgetName(oldmode);
1523     if (wname != NULL) {
1524         MarkMenuItem(wname, False);
1525     }
1526     wname = ModeToWidgetName(gameMode);
1527     if (wname != NULL) {
1528         MarkMenuItem(wname, True);
1529     }
1530     oldmode = gameMode;
1531     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1532
1533     /* Maybe all the enables should be handled here, not just this one */
1534     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1535
1536     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1537 }
1538
1539
1540 /*
1541  * Button/menu procedures
1542  */
1543
1544 void CopyFileToClipboard(gchar *filename)
1545 {
1546     gchar *selection_tmp;
1547     GtkClipboard *cb;
1548
1549     // read the file
1550     FILE* f = fopen(filename, "r");
1551     long len;
1552     size_t count;
1553     if (f == NULL) return;
1554     fseek(f, 0, 2);
1555     len = ftell(f);
1556     rewind(f);
1557     selection_tmp = g_try_malloc(len + 1);
1558     if (selection_tmp == NULL) {
1559         printf("Malloc failed in CopyFileToClipboard\n");
1560         return;
1561     }
1562     count = fread(selection_tmp, 1, len, f);
1563     fclose(f);
1564     if (len != count) {
1565       g_free(selection_tmp);
1566       return;
1567     }
1568     selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1569
1570     // copy selection_tmp to clipboard
1571     GdkDisplay *gdisp = gdk_display_get_default();
1572     if (!gdisp) {
1573         g_free(selection_tmp);
1574         return;
1575     }
1576     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1577     gtk_clipboard_set_text(cb, selection_tmp, -1);
1578     g_free(selection_tmp);
1579 }
1580
1581 void
1582 CopySomething (char *src)
1583 {
1584     GdkDisplay *gdisp = gdk_display_get_default();
1585     GtkClipboard *cb;
1586     if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1587     if (gdisp == NULL) return;
1588     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1589     gtk_clipboard_set_text(cb, src, -1);
1590 }
1591
1592 void
1593 PastePositionProc ()
1594 {
1595     GdkDisplay *gdisp = gdk_display_get_default();
1596     GtkClipboard *cb;
1597     gchar *fenstr;
1598
1599     if (gdisp == NULL) return;
1600     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1601     fenstr = gtk_clipboard_wait_for_text(cb);
1602     if (fenstr==NULL) return; // nothing had been selected to copy
1603     EditPositionPasteFEN(fenstr);
1604     return;
1605 }
1606
1607 void
1608 PasteGameProc ()
1609 {
1610     gchar *text=NULL;
1611     GtkClipboard *cb;
1612     guint len=0;
1613     FILE* f;
1614
1615     // get game from clipboard
1616     GdkDisplay *gdisp = gdk_display_get_default();
1617     if (gdisp == NULL) return;
1618     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1619     text = gtk_clipboard_wait_for_text(cb);
1620     if (text == NULL) return; // nothing to paste
1621     len = strlen(text);
1622
1623     // write to temp file
1624     if (text == NULL || len == 0) {
1625       return; //nothing to paste
1626     }
1627     f = fopen(gamePasteFilename, "w");
1628     if (f == NULL) {
1629       DisplayError(_("Can't open temp file"), errno);
1630       return;
1631     }
1632     fwrite(text, 1, len, f);
1633     fclose(f);
1634
1635     // load from file
1636     LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1637     return;
1638 }
1639
1640
1641 #ifdef TODO_GTK
1642 void
1643 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1644 {
1645     QuitProc();
1646 }
1647 #endif
1648
1649 void MoveTypeInProc(eventkey)
1650     GdkEventKey  *eventkey;
1651 {
1652     char buf[10];
1653
1654     // ingnore if ctrl or alt is pressed
1655     if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1656         return;
1657     }
1658
1659     buf[0]=eventkey->keyval;
1660     buf[1]='\0';
1661     if (*buf >= 32)
1662         BoxAutoPopUp (buf);
1663 }
1664
1665 #ifdef TODO_GTK
1666 void
1667 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1668 {
1669         if (!TempBackwardActive) {
1670                 TempBackwardActive = True;
1671                 BackwardEvent();
1672         }
1673 }
1674
1675 void
1676 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1677 {
1678         /* Check to see if triggered by a key release event for a repeating key.
1679          * If so the next queued event will be a key press of the same key at the same time */
1680         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1681                 XEvent next;
1682                 XPeekEvent(xDisplay, &next);
1683                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1684                         next.xkey.keycode == event->xkey.keycode)
1685                                 return;
1686         }
1687     ForwardEvent();
1688         TempBackwardActive = False;
1689 }
1690
1691 void
1692 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1693 {   // called as key binding
1694     char buf[MSG_SIZ];
1695     String name;
1696     if (nprms && *nprms > 0)
1697       name = prms[0];
1698     else
1699       name = "xboard";
1700     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1701     system(buf);
1702 }
1703 #endif
1704
1705 void
1706 ManProc ()
1707 {   // called from menu
1708 #ifdef TODO_GTK
1709     ManInner(NULL, NULL, NULL, NULL);
1710 #endif
1711 }
1712
1713 void
1714 SetWindowTitle (char *text, char *title, char *icon)
1715 {
1716 #ifdef TODO_GTK
1717     Arg args[16];
1718     int i;
1719     if (appData.titleInWindow) {
1720         i = 0;
1721         XtSetArg(args[i], XtNlabel, text);   i++;
1722         XtSetValues(titleWidget, args, i);
1723     }
1724     i = 0;
1725     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
1726     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
1727     XtSetValues(shellWidget, args, i);
1728     XSync(xDisplay, False);
1729 #endif
1730     if (appData.titleInWindow) {
1731         SetWidgetLabel(titleWidget, text);
1732     }
1733     gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1734 }
1735
1736
1737 void
1738 DisplayIcsInteractionTitle (String message)
1739 {
1740 #ifdef TODO_GTK
1741   if (oldICSInteractionTitle == NULL) {
1742     /* Magic to find the old window title, adapted from vim */
1743     char *wina = getenv("WINDOWID");
1744     if (wina != NULL) {
1745       Window win = (Window) atoi(wina);
1746       Window root, parent, *children;
1747       unsigned int nchildren;
1748       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1749       for (;;) {
1750         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1751         if (!XQueryTree(xDisplay, win, &root, &parent,
1752                         &children, &nchildren)) break;
1753         if (children) XFree((void *)children);
1754         if (parent == root || parent == 0) break;
1755         win = parent;
1756       }
1757       XSetErrorHandler(oldHandler);
1758     }
1759     if (oldICSInteractionTitle == NULL) {
1760       oldICSInteractionTitle = "xterm";
1761     }
1762   }
1763   printf("\033]0;%s\007", message);
1764   fflush(stdout);
1765 #endif
1766 }
1767
1768
1769 void
1770 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1771 {
1772     GtkWidget *w = (GtkWidget *) opt->handle;
1773     GdkColor col;
1774     char *markup;
1775     char bgcolor[10];
1776     char fgcolor[10];
1777
1778     if (highlight) {
1779         strcpy(bgcolor, "black");
1780         strcpy(fgcolor, "white");
1781     } else {
1782         strcpy(bgcolor, "white");
1783         strcpy(fgcolor, "black");
1784     }
1785     if (timer > 0 &&
1786         appData.lowTimeWarning &&
1787         (timer / 1000) < appData.icsAlarmTime) {
1788         strcpy(fgcolor, appData.lowTimeWarningColor);
1789     }
1790
1791     gdk_color_parse( bgcolor, &col );
1792     gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1793
1794     if (appData.clockMode) {
1795         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1796                                          bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1797     } else {
1798         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s  </span>",
1799                                          bgcolor, fgcolor, color);
1800     }
1801     gtk_label_set_markup(GTK_LABEL(w), markup);
1802     g_free(markup);
1803 }
1804
1805 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1806
1807 void
1808 SetClockIcon (int color)
1809 {
1810     GdkPixbuf *pm = *clockIcons[color];
1811     if (mainwindowIcon != pm) {
1812         mainwindowIcon = pm;
1813         gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1814     }
1815 }
1816
1817 #define INPUT_SOURCE_BUF_SIZE 8192
1818
1819 typedef struct {
1820     CPKind kind;
1821     int fd;
1822     int lineByLine;
1823     char *unused;
1824     InputCallback func;
1825     guint sid;
1826     char buf[INPUT_SOURCE_BUF_SIZE];
1827     VOIDSTAR closure;
1828 } InputSource;
1829
1830 gboolean
1831 DoInputCallback(io, cond, data)
1832      GIOChannel  *io;
1833      GIOCondition cond;
1834      gpointer    *data;
1835 {
1836   /* read input from one of the input source (for example a chess program, ICS, etc).
1837    * and call a function that will handle the input
1838    */
1839
1840     int count;
1841     int error;
1842     char *p, *q;
1843
1844     /* All information (callback function, file descriptor, etc) is
1845      * saved in an InputSource structure
1846      */
1847     InputSource *is = (InputSource *) data;
1848
1849     if (is->lineByLine) {
1850         count = read(is->fd, is->unused,
1851                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1852         if (count <= 0) {
1853             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1854             return True;
1855         }
1856         is->unused += count;
1857         p = is->buf;
1858         /* break input into lines and call the callback function on each
1859          * line
1860          */
1861         while (p < is->unused) {
1862             q = memchr(p, '\n', is->unused - p);
1863             if (q == NULL) break;
1864             q++;
1865             (is->func)(is, is->closure, p, q - p, 0);
1866             p = q;
1867         }
1868         /* remember not yet used part of the buffer */
1869         q = is->buf;
1870         while (p < is->unused) {
1871             *q++ = *p++;
1872         }
1873         is->unused = q;
1874     } else {
1875       /* read maximum length of input buffer and send the whole buffer
1876        * to the callback function
1877        */
1878         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1879         if (count == -1)
1880           error = errno;
1881         else
1882           error = 0;
1883         (is->func)(is, is->closure, is->buf, count, error);
1884     }
1885     return True; // Must return true or the watch will be removed
1886 }
1887
1888 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1889      ProcRef pr;
1890      int lineByLine;
1891      InputCallback func;
1892      VOIDSTAR closure;
1893 {
1894     InputSource *is;
1895     GIOChannel *channel;
1896     ChildProc *cp = (ChildProc *) pr;
1897
1898     is = (InputSource *) calloc(1, sizeof(InputSource));
1899     is->lineByLine = lineByLine;
1900     is->func = func;
1901     if (pr == NoProc) {
1902         is->kind = CPReal;
1903         is->fd = fileno(stdin);
1904     } else {
1905         is->kind = cp->kind;
1906         is->fd = cp->fdFrom;
1907     }
1908     if (lineByLine)
1909       is->unused = is->buf;
1910     else
1911       is->unused = NULL;
1912
1913    /* GTK-TODO: will this work on windows?*/
1914
1915     channel = g_io_channel_unix_new(is->fd);
1916     g_io_channel_set_close_on_unref (channel, TRUE);
1917     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1918
1919     is->closure = closure;
1920     return (InputSourceRef) is;
1921 }
1922
1923
1924 void
1925 RemoveInputSource(isr)
1926      InputSourceRef isr;
1927 {
1928     InputSource *is = (InputSource *) isr;
1929
1930     if (is->sid == 0) return;
1931     g_source_remove(is->sid);
1932     is->sid = 0;
1933     return;
1934 }
1935
1936 #ifndef HAVE_USLEEP
1937
1938 static Boolean frameWaiting;
1939
1940 static RETSIGTYPE
1941 FrameAlarm (int sig)
1942 {
1943   frameWaiting = False;
1944   /* In case System-V style signals.  Needed?? */
1945   signal(SIGALRM, FrameAlarm);
1946 }
1947
1948 void
1949 FrameDelay (int time)
1950 {
1951   struct itimerval delay;
1952
1953   if (time > 0) {
1954     frameWaiting = True;
1955     signal(SIGALRM, FrameAlarm);
1956     delay.it_interval.tv_sec =
1957       delay.it_value.tv_sec = time / 1000;
1958     delay.it_interval.tv_usec =
1959       delay.it_value.tv_usec = (time % 1000) * 1000;
1960     setitimer(ITIMER_REAL, &delay, NULL);
1961     while (frameWaiting) pause();
1962     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
1963     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
1964     setitimer(ITIMER_REAL, &delay, NULL);
1965   }
1966 }
1967
1968 #else
1969
1970 void
1971 FrameDelay (int time)
1972 {
1973 #ifdef TODO_GTK
1974   XSync(xDisplay, False);
1975 #endif
1976 //  gtk_main_iteration_do(False);
1977
1978   if (time > 0)
1979     usleep(time * 1000);
1980 }
1981
1982 #endif
1983
1984 static void
1985 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
1986 {
1987     char buf[MSG_SIZ], *logoName = buf;
1988     if(appData.logo[n][0]) {
1989         logoName = appData.logo[n];
1990     } else if(appData.autoLogo) {
1991         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
1992             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
1993         } else if(appData.directory[n] && appData.directory[n][0]) {
1994             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
1995         }
1996     }
1997     if(logoName[0])
1998         { ASSIGN(cps->programLogo, logoName); }
1999 }
2000
2001 void
2002 UpdateLogos (int displ)
2003 {
2004     if(optList[W_WHITE-1].handle == NULL) return;
2005     LoadLogo(&first, 0, 0);
2006     LoadLogo(&second, 1, appData.icsActive);
2007     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2008     return;
2009 }
2010
2011 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2012      char *label;
2013      char *def;
2014      char *filter;
2015      FileProc proc;
2016      char *openMode;
2017      Boolean pathFlag;
2018      char **name;
2019      FILE **fp;
2020 {
2021   GtkWidget     *dialog;
2022   GtkFileFilter *gtkfilter;
2023   GtkFileFilter *gtkfilter_all;
2024   char space[]     = " ";
2025   char fileext[10] = "";
2026   char *result     = NULL;
2027   char *cp;
2028
2029   /* make a copy of the filter string, so that strtok can work with it*/
2030   cp = strndup(filter,strlen(filter));
2031
2032   /* add filters for file extensions */
2033   gtkfilter     = gtk_file_filter_new();
2034   gtkfilter_all = gtk_file_filter_new();
2035
2036   /* one filter to show everything */
2037   gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2038   gtk_file_filter_set_name   (gtkfilter_all, "All Files");
2039
2040   /* add filter if present */
2041   result = strtok(cp, space);
2042   while( result != NULL  ) {
2043     snprintf(fileext,10,"*%s",result);
2044     result = strtok( NULL, space );
2045     gtk_file_filter_add_pattern(gtkfilter, fileext);
2046   };
2047
2048   /* second filter to only show what's useful */
2049   gtk_file_filter_set_name (gtkfilter,filter);
2050
2051   if (openMode[0] == 'r')
2052     {
2053       dialog = gtk_file_chooser_dialog_new (label,
2054                                             NULL,
2055                                             GTK_FILE_CHOOSER_ACTION_OPEN,
2056                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2057                                             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2058                                             NULL);
2059     }
2060   else
2061     {
2062       dialog = gtk_file_chooser_dialog_new (label,
2063                                             NULL,
2064                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2065                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2066                                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2067                                             NULL);
2068       /* add filename suggestions */
2069       if (strlen(def) > 0 )
2070         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2071
2072       //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2073     }
2074
2075   /* add filters */
2076   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2077   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2078   /* activate filter */
2079   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2080
2081   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2082     {
2083       char *filename;
2084       FILE *f;
2085
2086       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2087
2088       //see loadgamepopup
2089       f = fopen(filename, openMode);
2090       if (f == NULL)
2091         {
2092           DisplayError(_("Failed to open file"), errno);
2093         }
2094       else
2095         {
2096           /* TODO add indec */
2097             *fp = f;
2098             ASSIGN(*name, filename);
2099             ScheduleDelayedEvent(DelayedLoad, 50);
2100         }
2101       g_free (filename);
2102     };
2103
2104   gtk_widget_destroy (dialog);
2105   ModeHighlight();
2106
2107   free(cp);
2108   return;
2109
2110 }