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