Fix sizing of slave board GTK
[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 void
695 SlaveResize (Option *opt)
696 {
697     static int slaveW, slaveH, w, h;
698     GtkAllocation a;
699     if(!slaveH) {
700         gtk_widget_get_allocation(shells[DummyDlg], &a);
701         w = a.width; h = a.height;
702         gtk_widget_get_allocation(opt->handle, &a);
703         slaveW =  w - opt->max; // [HGM] needed to set new shellWidget size when we resize board
704         slaveH =  h - a.height + 13;
705    }
706   gtk_window_resize(GTK_WINDOW(shells[DummyDlg]), slaveW + opt->max, slaveH + opt->value);
707 }
708
709 int
710 main (int argc, char **argv)
711 {
712     int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
713     int boardWidth, boardHeight, w, h;
714     char *p;
715     int forceMono = False;
716
717     srandom(time(0)); // [HGM] book: make random truly random
718
719     setbuf(stdout, NULL);
720     setbuf(stderr, NULL);
721     debugFP = stderr;
722
723     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
724         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
725         exit(0);
726     }
727
728     if(argc > 1 && !strcmp(argv[1], "--help" )) {
729         PrintOptions();
730         exit(0);
731     }
732
733     /* set up GTK */
734     gtk_init (&argc, &argv);
735
736     /* set up keyboard accelerators group */
737     GtkAccelerators = gtk_accel_group_new();
738
739     programName = strrchr(argv[0], '/');
740     if (programName == NULL)
741       programName = argv[0];
742     else
743       programName++;
744
745 #ifdef ENABLE_NLS
746 //    if (appData.debugMode) {
747 //      fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
748 //    }
749
750     bindtextdomain(PACKAGE, LOCALEDIR);
751     bind_textdomain_codeset(PACKAGE, "UTF-8"); // needed when creating markup for the clocks
752     textdomain(PACKAGE);
753 #endif
754
755     appData.boardSize = "";
756     InitAppData(ConvertToLine(argc, argv));
757     p = getenv("HOME");
758     if (p == NULL) p = "/tmp";
759     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
760     gameCopyFilename = (char*) malloc(i);
761     gamePasteFilename = (char*) malloc(i);
762     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
763     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
764
765     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
766         static char buf[MSG_SIZ];
767         EscapeExpand(buf, appData.firstInitString);
768         appData.firstInitString = strdup(buf);
769         EscapeExpand(buf, appData.secondInitString);
770         appData.secondInitString = strdup(buf);
771         EscapeExpand(buf, appData.firstComputerString);
772         appData.firstComputerString = strdup(buf);
773         EscapeExpand(buf, appData.secondComputerString);
774         appData.secondComputerString = strdup(buf);
775     }
776
777     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
778         chessDir = ".";
779     } else {
780         if (chdir(chessDir) != 0) {
781             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
782             perror(chessDir);
783             exit(1);
784         }
785     }
786
787     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
788         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
789         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
790            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
791            exit(errno);
792         }
793         setbuf(debugFP, NULL);
794     }
795
796 #if ENABLE_NLS
797     if (appData.debugMode) {
798       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
799     }
800 #endif
801
802     /* [HGM,HR] make sure board size is acceptable */
803     if(appData.NrFiles > BOARD_FILES ||
804        appData.NrRanks > BOARD_RANKS   )
805          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
806
807 #if !HIGHDRAG
808     /* This feature does not work; animation needs a rewrite */
809     appData.highlightDragging = FALSE;
810 #endif
811     InitBackEnd1();
812
813         gameInfo.variant = StringToVariant(appData.variant);
814         InitPosition(FALSE);
815
816     /*
817      * determine size, based on supplied or remembered -size, or screen size
818      */
819     if (isdigit(appData.boardSize[0])) {
820         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
821                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
822                    &fontPxlSize, &smallLayout, &tinyLayout);
823         if (i == 0) {
824             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
825                     programName, appData.boardSize);
826             exit(2);
827         }
828         if (i < 7) {
829             /* Find some defaults; use the nearest known size */
830             SizeDefaults *szd, *nearest;
831             int distance = 99999;
832             nearest = szd = sizeDefaults;
833             while (szd->name != NULL) {
834                 if (abs(szd->squareSize - squareSize) < distance) {
835                     nearest = szd;
836                     distance = abs(szd->squareSize - squareSize);
837                     if (distance == 0) break;
838                 }
839                 szd++;
840             }
841             if (i < 2) lineGap = nearest->lineGap;
842             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
843             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
844             if (i < 5) fontPxlSize = nearest->fontPxlSize;
845             if (i < 6) smallLayout = nearest->smallLayout;
846             if (i < 7) tinyLayout = nearest->tinyLayout;
847         }
848     } else {
849         SizeDefaults *szd = sizeDefaults;
850         if (*appData.boardSize == NULLCHAR) {
851             GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
852             guint screenwidth = gdk_screen_get_width(screen);
853             guint screenheight = gdk_screen_get_height(screen);
854             while (screenwidth < szd->minScreenSize ||
855                    screenheight < szd->minScreenSize) {
856               szd++;
857             }
858             if (szd->name == NULL) szd--;
859             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
860         } else {
861             while (szd->name != NULL &&
862                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
863             if (szd->name == NULL) {
864                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
865                         programName, appData.boardSize);
866                 exit(2);
867             }
868         }
869         squareSize = szd->squareSize;
870         lineGap = szd->lineGap;
871         clockFontPxlSize = szd->clockFontPxlSize;
872         coordFontPxlSize = szd->coordFontPxlSize;
873         fontPxlSize = szd->fontPxlSize;
874         smallLayout = szd->smallLayout;
875         tinyLayout = szd->tinyLayout;
876         // [HGM] font: use defaults from settings file if available and not overruled
877     }
878
879     defaultLineGap = lineGap;
880     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
881
882     /* [HR] height treated separately (hacked) */
883     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
884     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
885
886     /*
887      * Determine what fonts to use.
888      */
889 #ifdef TODO_GTK
890     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
891 #endif
892
893     /*
894      * Detect if there are not enough colors available and adapt.
895      */
896 #ifdef TODO_GTK
897     if (DefaultDepth(xDisplay, xScreen) <= 2) {
898       appData.monoMode = True;
899     }
900 #endif
901
902     forceMono = MakeColors();
903
904     if (forceMono) {
905       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
906               programName);
907         appData.monoMode = True;
908     }
909
910     ParseIcsTextColors();
911
912     /*
913      * widget hierarchy
914      */
915     if (tinyLayout) {
916         layoutName = "tinyLayout";
917     } else if (smallLayout) {
918         layoutName = "smallLayout";
919     } else {
920         layoutName = "normalLayout";
921     }
922
923     optList = BoardPopUp(squareSize, lineGap, (void*)
924 #ifdef TODO_GTK
925 #if ENABLE_NLS
926                                                 &clockFontSet);
927 #else
928                                                 clockFontStruct);
929 #endif
930 #else
931 0);
932 #endif
933     InitDrawingHandle(optList + W_BOARD);
934     shellWidget      = shells[BoardWindow];
935     currBoard        = &optList[W_BOARD];
936     boardWidget      = optList[W_BOARD].handle;
937     menuBarWidget    = optList[W_MENU].handle;
938     dropMenu         = optList[W_DROP].handle;
939     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
940 #ifdef TODO_GTK
941     formWidget  = XtParent(boardWidget);
942     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
943     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
944     XtGetValues(optList[W_WHITE].handle, args, 2);
945     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
946       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
947       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
948       XtGetValues(optList[W_PAUSE].handle, args, 2);
949     }
950 #endif
951
952     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
953     //       not need to go into InitDrawingSizes().
954
955     InitMenuMarkers();
956
957     // add accelerators to main shell
958     gtk_window_add_accel_group(GTK_WINDOW(shellWidget), GtkAccelerators);
959
960     /*
961      * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
962      */
963     WhiteIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
964     BlackIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
965     mainwindowIcon = WhiteIcon;
966     gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
967
968
969     /*
970      * Create a cursor for the board widget.
971      */
972 #ifdef TODO_GTK
973     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
974     XChangeWindowAttributes(xDisplay, xBoardWindow,
975                             CWCursor, &window_attributes);
976 #endif
977
978     /*
979      * Inhibit shell resizing.
980      */
981 #ifdef TODO_GTK
982     shellArgs[0].value = (XtArgVal) &w;
983     shellArgs[1].value = (XtArgVal) &h;
984     XtGetValues(shellWidget, shellArgs, 2);
985     shellArgs[4].value = shellArgs[2].value = w;
986     shellArgs[5].value = shellArgs[3].value = h;
987 //    XtSetValues(shellWidget, &shellArgs[2], 4);
988 #endif
989     {
990         GtkAllocation a;
991         gtk_widget_get_allocation(shells[BoardWindow], &a);
992         w = a.width; h = a.height;
993 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
994         gtk_widget_get_allocation(boardWidget, &a);
995         marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
996         marginH =  h - a.height + 13;
997         gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
998 //printf("margins h=%d v=%d\n", marginW, marginH);
999     }
1000
1001     CreateAnyPieces();
1002     CreateGrid();
1003
1004     if(appData.logoSize)
1005     {   // locate and read user logo
1006         char buf[MSG_SIZ];
1007         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1008         ASSIGN(userLogo, buf);
1009     }
1010
1011     if (appData.animate || appData.animateDragging)
1012       CreateAnimVars();
1013
1014     g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1015     g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1016
1017     /* [AS] Restore layout */
1018     if( wpMoveHistory.visible ) {
1019       HistoryPopUp();
1020     }
1021
1022     if( wpEvalGraph.visible )
1023       {
1024         EvalGraphPopUp();
1025       };
1026
1027     if( wpEngineOutput.visible ) {
1028       EngineOutputPopUp();
1029     }
1030
1031     InitBackEnd2();
1032
1033     if (errorExitStatus == -1) {
1034         if (appData.icsActive) {
1035             /* We now wait until we see "login:" from the ICS before
1036                sending the logon script (problems with timestamp otherwise) */
1037             /*ICSInitScript();*/
1038             if (appData.icsInputBox) ICSInputBoxPopUp();
1039         }
1040
1041     #ifdef SIGWINCH
1042     signal(SIGWINCH, TermSizeSigHandler);
1043     #endif
1044         signal(SIGINT, IntSigHandler);
1045         signal(SIGTERM, IntSigHandler);
1046         if (*appData.cmailGameName != NULLCHAR) {
1047             signal(SIGUSR1, CmailSigHandler);
1048         }
1049     }
1050
1051     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1052     InitPosition(TRUE);
1053     UpdateLogos(TRUE);
1054 //    XtSetKeyboardFocus(shellWidget, formWidget);
1055 #ifdef TODO_GTK
1056     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1057 #endif
1058
1059     /* check for GTK events and process them */
1060 //    gtk_main();
1061 while(1) {
1062 gtk_main_iteration();
1063 }
1064
1065     if (appData.debugMode) fclose(debugFP); // [DM] debug
1066     return 0;
1067 }
1068
1069 RETSIGTYPE
1070 TermSizeSigHandler (int sig)
1071 {
1072     update_ics_width();
1073 }
1074
1075 RETSIGTYPE
1076 IntSigHandler (int sig)
1077 {
1078     ExitEvent(sig);
1079 }
1080
1081 RETSIGTYPE
1082 CmailSigHandler (int sig)
1083 {
1084     int dummy = 0;
1085     int error;
1086
1087     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1088
1089     /* Activate call-back function CmailSigHandlerCallBack()             */
1090     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1091
1092     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1093 }
1094
1095 void
1096 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1097 {
1098     BoardToTop();
1099     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1100 }
1101 /**** end signal code ****/
1102
1103
1104 #define Abs(n) ((n)<0 ? -(n) : (n))
1105
1106 #ifdef ENABLE_NLS
1107 char *
1108 InsertPxlSize (char *pattern, int targetPxlSize)
1109 {
1110     char *base_fnt_lst, strInt[12], *p, *q;
1111     int alternatives, i, len, strIntLen;
1112
1113     /*
1114      * Replace the "*" (if present) in the pixel-size slot of each
1115      * alternative with the targetPxlSize.
1116      */
1117     p = pattern;
1118     alternatives = 1;
1119     while ((p = strchr(p, ',')) != NULL) {
1120       alternatives++;
1121       p++;
1122     }
1123     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1124     strIntLen = strlen(strInt);
1125     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1126
1127     p = pattern;
1128     q = base_fnt_lst;
1129     while (alternatives--) {
1130       char *comma = strchr(p, ',');
1131       for (i=0; i<14; i++) {
1132         char *hyphen = strchr(p, '-');
1133         if (!hyphen) break;
1134         if (comma && hyphen > comma) break;
1135         len = hyphen + 1 - p;
1136         if (i == 7 && *p == '*' && len == 2) {
1137           p += len;
1138           memcpy(q, strInt, strIntLen);
1139           q += strIntLen;
1140           *q++ = '-';
1141         } else {
1142           memcpy(q, p, len);
1143           p += len;
1144           q += len;
1145         }
1146       }
1147       if (!comma) break;
1148       len = comma + 1 - p;
1149       memcpy(q, p, len);
1150       p += len;
1151       q += len;
1152     }
1153     strcpy(q, p);
1154
1155     return base_fnt_lst;
1156 }
1157
1158 #ifdef TODO_GTK
1159 XFontSet
1160 CreateFontSet (char *base_fnt_lst)
1161 {
1162     XFontSet fntSet;
1163     char **missing_list;
1164     int missing_count;
1165     char *def_string;
1166
1167     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1168                             &missing_list, &missing_count, &def_string);
1169     if (appData.debugMode) {
1170       int i, count;
1171       XFontStruct **font_struct_list;
1172       char **font_name_list;
1173       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1174       if (fntSet) {
1175         fprintf(debugFP, " got list %s, locale %s\n",
1176                 XBaseFontNameListOfFontSet(fntSet),
1177                 XLocaleOfFontSet(fntSet));
1178         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1179         for (i = 0; i < count; i++) {
1180           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1181         }
1182       }
1183       for (i = 0; i < missing_count; i++) {
1184         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1185       }
1186     }
1187     if (fntSet == NULL) {
1188       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1189       exit(2);
1190     }
1191     return fntSet;
1192 }
1193 #endif
1194 #else // not ENABLE_NLS
1195 /*
1196  * Find a font that matches "pattern" that is as close as
1197  * possible to the targetPxlSize.  Prefer fonts that are k
1198  * pixels smaller to fonts that are k pixels larger.  The
1199  * pattern must be in the X Consortium standard format,
1200  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1201  * The return value should be freed with XtFree when no
1202  * longer needed.
1203  */
1204 char *
1205 FindFont (char *pattern, int targetPxlSize)
1206 {
1207     char **fonts, *p, *best, *scalable, *scalableTail;
1208     int i, j, nfonts, minerr, err, pxlSize;
1209
1210 #ifdef TODO_GTK
1211     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1212     if (nfonts < 1) {
1213         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1214                 programName, pattern);
1215         exit(2);
1216     }
1217
1218     best = fonts[0];
1219     scalable = NULL;
1220     minerr = 999999;
1221     for (i=0; i<nfonts; i++) {
1222         j = 0;
1223         p = fonts[i];
1224         if (*p != '-') continue;
1225         while (j < 7) {
1226             if (*p == NULLCHAR) break;
1227             if (*p++ == '-') j++;
1228         }
1229         if (j < 7) continue;
1230         pxlSize = atoi(p);
1231         if (pxlSize == 0) {
1232             scalable = fonts[i];
1233             scalableTail = p;
1234         } else {
1235             err = pxlSize - targetPxlSize;
1236             if (Abs(err) < Abs(minerr) ||
1237                 (minerr > 0 && err < 0 && -err == minerr)) {
1238                 best = fonts[i];
1239                 minerr = err;
1240             }
1241         }
1242     }
1243     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1244         /* If the error is too big and there is a scalable font,
1245            use the scalable font. */
1246         int headlen = scalableTail - scalable;
1247         p = (char *) XtMalloc(strlen(scalable) + 10);
1248         while (isdigit(*scalableTail)) scalableTail++;
1249         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1250     } else {
1251         p = (char *) XtMalloc(strlen(best) + 2);
1252         safeStrCpy(p, best, strlen(best)+1 );
1253     }
1254     if (appData.debugMode) {
1255         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
1256                 pattern, targetPxlSize, p);
1257     }
1258     XFreeFontNames(fonts);
1259 #endif
1260     return p;
1261 }
1262 #endif
1263
1264 void
1265 EnableNamedMenuItem (char *menuRef, int state)
1266 {
1267     MenuItem *item = MenuNameToItem(menuRef);
1268
1269     if(item) gtk_widget_set_sensitive(item->handle, state);
1270 }
1271
1272 void
1273 EnableButtonBar (int state)
1274 {
1275 #ifdef TODO_GTK
1276     XtSetSensitive(optList[W_BUTTON].handle, state);
1277 #endif
1278 }
1279
1280
1281 void
1282 SetMenuEnables (Enables *enab)
1283 {
1284   while (enab->name != NULL) {
1285     EnableNamedMenuItem(enab->name, enab->value);
1286     enab++;
1287   }
1288 }
1289
1290 gboolean KeyPressProc(window, eventkey, data)
1291      GtkWindow *window;
1292      GdkEventKey  *eventkey;
1293      gpointer data;
1294 {
1295
1296     MoveTypeInProc(eventkey); // pop up for typed in moves
1297
1298 #ifdef TODO_GTK
1299     /* check for other key values */
1300     switch(eventkey->keyval) {
1301         case GDK_question:
1302           AboutGameEvent();
1303           break;
1304         default:
1305           break;
1306     }
1307 #endif
1308     return False;
1309 }
1310 #ifdef TODO_GTK
1311 void
1312 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1313 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1314     MenuItem *item;
1315     if(*nprms == 0) return;
1316     item = MenuNameToItem(prms[0]);
1317     if(item) ((MenuProc *) item->proc) ();
1318 }
1319 #endif
1320
1321 void
1322 SetupDropMenu ()
1323 {
1324 #ifdef TODO_GTK
1325     int i, j, count;
1326     char label[32];
1327     Arg args[16];
1328     Widget entry;
1329     char* p;
1330
1331     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1332         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1333         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1334                    dmEnables[i].piece);
1335         XtSetSensitive(entry, p != NULL || !appData.testLegality
1336                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1337                                        && !appData.icsActive));
1338         count = 0;
1339         while (p && *p++ == dmEnables[i].piece) count++;
1340         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1341         j = 0;
1342         XtSetArg(args[j], XtNlabel, label); j++;
1343         XtSetValues(entry, args, j);
1344     }
1345 #endif
1346 }
1347
1348 static void
1349 do_flash_delay (unsigned long msec)
1350 {
1351     TimeDelay(msec);
1352 }
1353
1354 void
1355 FlashDelay (int flash_delay)
1356 {
1357         if(flash_delay) do_flash_delay(flash_delay);
1358 }
1359
1360 double
1361 Fraction (int x, int start, int stop)
1362 {
1363    double f = ((double) x - start)/(stop - start);
1364    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1365    return f;
1366 }
1367
1368 static WindowPlacement wpNew;
1369
1370 void
1371 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1372 {
1373     int touch=0, fudge = 2, f = 2;
1374     GetActualPlacement(sh, wp);
1375     if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x)         < fudge) touch = 1; else // right touch
1376     if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x)            < fudge) touch = 2; else // left touch
1377     if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1378     if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y)    < fudge) touch = 4;      // top touch
1379 //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);
1380     if(!touch ) return; // only windows that touch co-move
1381     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1382         int heightInc = wpNew.height - wpMain.height;
1383         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1384         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1385         wp->y += fracTop * heightInc;
1386         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1387 #ifdef TODO_GTK
1388         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1389 #endif
1390         wp->height += heightInc;
1391     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1392         int widthInc = wpNew.width - wpMain.width;
1393         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1394         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1395         wp->y += fracLeft * widthInc;
1396         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1397 #ifdef TODO_GTK
1398         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1399 #endif
1400         wp->width += widthInc;
1401     }
1402     wp->x += wpNew.x - wpMain.x;
1403     wp->y += wpNew.y - wpMain.y;
1404     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1405     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1406 #ifdef TODO_GTK
1407     XtSetArg(args[j], XtNx, wp->x); j++;
1408     XtSetArg(args[j], XtNy, wp->y); j++;
1409     XtSetValues(sh, args, j);
1410 #endif
1411         gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1412 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1413         gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1414 }
1415
1416 void
1417 ReSize (WindowPlacement *wp)
1418 {
1419         int sqx, sqy, w, h, lg = lineGap;
1420         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1421         sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1422         sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1423         if(sqy < sqx) sqx = sqy;
1424         if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1425             lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1426             sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1427             sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1428             if(sqy < sqx) sqx = sqy;
1429         }
1430         if(sqx != squareSize) {
1431 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1432             squareSize = sqx; // adopt new square size
1433             CreatePNGPieces(); // make newly scaled pieces
1434             InitDrawingSizes(0, 0); // creates grid etc.
1435         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1436         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1437         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1438         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1439         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1440 }
1441
1442 static guint delayedDragTag = 0;
1443
1444 void
1445 DragProc ()
1446 {
1447         static int busy;
1448         if(busy) return;
1449
1450         busy = 1;
1451 //      GetActualPlacement(shellWidget, &wpNew);
1452 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1453         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1454            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1455             busy = 0; return; // false alarm
1456         }
1457         ReSize(&wpNew);
1458         if(appData.useStickyWindows) {
1459             if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1460             if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1461             if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1462             if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1463         }
1464         wpMain = wpNew;
1465         DrawPosition(True, NULL);
1466         if(delayedDragTag) g_source_remove(delayedDragTag);
1467         delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1468         busy = 0;
1469 }
1470
1471 void
1472 DelayedDrag ()
1473 {
1474 //printf("old timr = %d\n", delayedDragTag);
1475     if(delayedDragTag) g_source_remove(delayedDragTag);
1476     delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1477 //printf("new timr = %d\n", delayedDragTag);
1478 }
1479
1480 static gboolean
1481 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1482 {
1483 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1484     // immediately
1485     wpNew.x = event->configure.x;
1486     wpNew.y = event->configure.y;
1487     wpNew.width  = event->configure.width;
1488     wpNew.height = event->configure.height;
1489     DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1490     return FALSE;
1491 }
1492
1493
1494
1495 /* Disable all user input other than deleting the window */
1496 static int frozen = 0;
1497
1498 void
1499 FreezeUI ()
1500 {
1501   if (frozen) return;
1502   /* Grab by a widget that doesn't accept input */
1503   gtk_grab_add(optList[W_MESSG].handle);
1504   frozen = 1;
1505 }
1506
1507 /* Undo a FreezeUI */
1508 void
1509 ThawUI ()
1510 {
1511   if (!frozen) return;
1512   gtk_grab_remove(optList[W_MESSG].handle);
1513   frozen = 0;
1514 }
1515
1516 void
1517 ModeHighlight ()
1518 {
1519     static int oldPausing = FALSE;
1520     static GameMode oldmode = (GameMode) -1;
1521     char *wname;
1522     if (!boardWidget) return;
1523
1524     if (pausing != oldPausing) {
1525         oldPausing = pausing;
1526         MarkMenuItem("Mode.Pause", pausing);
1527
1528         if (appData.showButtonBar) {
1529           /* Always toggle, don't set.  Previous code messes up when
1530              invoked while the button is pressed, as releasing it
1531              toggles the state again. */
1532             GdkColor color;
1533             gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1534             gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1535         }
1536     }
1537
1538     wname = ModeToWidgetName(oldmode);
1539     if (wname != NULL) {
1540         MarkMenuItem(wname, False);
1541     }
1542     wname = ModeToWidgetName(gameMode);
1543     if (wname != NULL) {
1544         MarkMenuItem(wname, True);
1545     }
1546     oldmode = gameMode;
1547     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1548
1549     /* Maybe all the enables should be handled here, not just this one */
1550     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1551
1552     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1553 }
1554
1555
1556 /*
1557  * Button/menu procedures
1558  */
1559
1560 void CopyFileToClipboard(gchar *filename)
1561 {
1562     gchar *selection_tmp;
1563     GtkClipboard *cb;
1564
1565     // read the file
1566     FILE* f = fopen(filename, "r");
1567     long len;
1568     size_t count;
1569     if (f == NULL) return;
1570     fseek(f, 0, 2);
1571     len = ftell(f);
1572     rewind(f);
1573     selection_tmp = g_try_malloc(len + 1);
1574     if (selection_tmp == NULL) {
1575         printf("Malloc failed in CopyFileToClipboard\n");
1576         return;
1577     }
1578     count = fread(selection_tmp, 1, len, f);
1579     fclose(f);
1580     if (len != count) {
1581       g_free(selection_tmp);
1582       return;
1583     }
1584     selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1585
1586     // copy selection_tmp to clipboard
1587     GdkDisplay *gdisp = gdk_display_get_default();
1588     if (!gdisp) {
1589         g_free(selection_tmp);
1590         return;
1591     }
1592     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1593     gtk_clipboard_set_text(cb, selection_tmp, -1);
1594     g_free(selection_tmp);
1595 }
1596
1597 void
1598 CopySomething (char *src)
1599 {
1600     GdkDisplay *gdisp = gdk_display_get_default();
1601     GtkClipboard *cb;
1602     if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1603     if (gdisp == NULL) return;
1604     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1605     gtk_clipboard_set_text(cb, src, -1);
1606 }
1607
1608 void
1609 PastePositionProc ()
1610 {
1611     GdkDisplay *gdisp = gdk_display_get_default();
1612     GtkClipboard *cb;
1613     gchar *fenstr;
1614
1615     if (gdisp == NULL) return;
1616     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1617     fenstr = gtk_clipboard_wait_for_text(cb);
1618     if (fenstr==NULL) return; // nothing had been selected to copy
1619     EditPositionPasteFEN(fenstr);
1620     return;
1621 }
1622
1623 void
1624 PasteGameProc ()
1625 {
1626     gchar *text=NULL;
1627     GtkClipboard *cb;
1628     guint len=0;
1629     FILE* f;
1630
1631     // get game from clipboard
1632     GdkDisplay *gdisp = gdk_display_get_default();
1633     if (gdisp == NULL) return;
1634     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1635     text = gtk_clipboard_wait_for_text(cb);
1636     if (text == NULL) return; // nothing to paste
1637     len = strlen(text);
1638
1639     // write to temp file
1640     if (text == NULL || len == 0) {
1641       return; //nothing to paste
1642     }
1643     f = fopen(gamePasteFilename, "w");
1644     if (f == NULL) {
1645       DisplayError(_("Can't open temp file"), errno);
1646       return;
1647     }
1648     fwrite(text, 1, len, f);
1649     fclose(f);
1650
1651     // load from file
1652     LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1653     return;
1654 }
1655
1656
1657 #ifdef TODO_GTK
1658 void
1659 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1660 {
1661     QuitProc();
1662 }
1663 #endif
1664
1665 void MoveTypeInProc(eventkey)
1666     GdkEventKey  *eventkey;
1667 {
1668     char buf[10];
1669
1670     // ingnore if ctrl or alt is pressed
1671     if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1672         return;
1673     }
1674
1675     buf[0]=eventkey->keyval;
1676     buf[1]='\0';
1677     if (*buf >= 32)
1678         BoxAutoPopUp (buf);
1679 }
1680
1681 #ifdef TODO_GTK
1682 void
1683 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1684 {
1685         if (!TempBackwardActive) {
1686                 TempBackwardActive = True;
1687                 BackwardEvent();
1688         }
1689 }
1690
1691 void
1692 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1693 {
1694         /* Check to see if triggered by a key release event for a repeating key.
1695          * If so the next queued event will be a key press of the same key at the same time */
1696         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1697                 XEvent next;
1698                 XPeekEvent(xDisplay, &next);
1699                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1700                         next.xkey.keycode == event->xkey.keycode)
1701                                 return;
1702         }
1703     ForwardEvent();
1704         TempBackwardActive = False;
1705 }
1706
1707 void
1708 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1709 {   // called as key binding
1710     char buf[MSG_SIZ];
1711     String name;
1712     if (nprms && *nprms > 0)
1713       name = prms[0];
1714     else
1715       name = "xboard";
1716     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1717     system(buf);
1718 }
1719 #endif
1720
1721 void
1722 ManProc ()
1723 {   // called from menu
1724 #ifdef TODO_GTK
1725     ManInner(NULL, NULL, NULL, NULL);
1726 #endif
1727 }
1728
1729 void
1730 SetWindowTitle (char *text, char *title, char *icon)
1731 {
1732 #ifdef TODO_GTK
1733     Arg args[16];
1734     int i;
1735     if (appData.titleInWindow) {
1736         i = 0;
1737         XtSetArg(args[i], XtNlabel, text);   i++;
1738         XtSetValues(titleWidget, args, i);
1739     }
1740     i = 0;
1741     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
1742     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
1743     XtSetValues(shellWidget, args, i);
1744     XSync(xDisplay, False);
1745 #endif
1746     if (appData.titleInWindow) {
1747         SetWidgetLabel(titleWidget, text);
1748     }
1749     gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1750 }
1751
1752
1753 void
1754 DisplayIcsInteractionTitle (String message)
1755 {
1756 #ifdef TODO_GTK
1757   if (oldICSInteractionTitle == NULL) {
1758     /* Magic to find the old window title, adapted from vim */
1759     char *wina = getenv("WINDOWID");
1760     if (wina != NULL) {
1761       Window win = (Window) atoi(wina);
1762       Window root, parent, *children;
1763       unsigned int nchildren;
1764       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1765       for (;;) {
1766         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1767         if (!XQueryTree(xDisplay, win, &root, &parent,
1768                         &children, &nchildren)) break;
1769         if (children) XFree((void *)children);
1770         if (parent == root || parent == 0) break;
1771         win = parent;
1772       }
1773       XSetErrorHandler(oldHandler);
1774     }
1775     if (oldICSInteractionTitle == NULL) {
1776       oldICSInteractionTitle = "xterm";
1777     }
1778   }
1779   printf("\033]0;%s\007", message);
1780   fflush(stdout);
1781 #endif
1782 }
1783
1784
1785 void
1786 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1787 {
1788     GtkWidget *w = (GtkWidget *) opt->handle;
1789     GdkColor col;
1790     char *markup;
1791     char bgcolor[10];
1792     char fgcolor[10];
1793
1794     if (highlight) {
1795         strcpy(bgcolor, "black");
1796         strcpy(fgcolor, "white");
1797     } else {
1798         strcpy(bgcolor, "white");
1799         strcpy(fgcolor, "black");
1800     }
1801     if (timer > 0 &&
1802         appData.lowTimeWarning &&
1803         (timer / 1000) < appData.icsAlarmTime) {
1804         strcpy(fgcolor, appData.lowTimeWarningColor);
1805     }
1806
1807     gdk_color_parse( bgcolor, &col );
1808     gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1809
1810     if (appData.clockMode) {
1811         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1812                                          bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1813     } else {
1814         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s  </span>",
1815                                          bgcolor, fgcolor, color);
1816     }
1817     gtk_label_set_markup(GTK_LABEL(w), markup);
1818     g_free(markup);
1819 }
1820
1821 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1822
1823 void
1824 SetClockIcon (int color)
1825 {
1826     GdkPixbuf *pm = *clockIcons[color];
1827     if (mainwindowIcon != pm) {
1828         mainwindowIcon = pm;
1829         gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1830     }
1831 }
1832
1833 #define INPUT_SOURCE_BUF_SIZE 8192
1834
1835 typedef struct {
1836     CPKind kind;
1837     int fd;
1838     int lineByLine;
1839     char *unused;
1840     InputCallback func;
1841     guint sid;
1842     char buf[INPUT_SOURCE_BUF_SIZE];
1843     VOIDSTAR closure;
1844 } InputSource;
1845
1846 gboolean
1847 DoInputCallback(io, cond, data)
1848      GIOChannel  *io;
1849      GIOCondition cond;
1850      gpointer    *data;
1851 {
1852   /* read input from one of the input source (for example a chess program, ICS, etc).
1853    * and call a function that will handle the input
1854    */
1855
1856     int count;
1857     int error;
1858     char *p, *q;
1859
1860     /* All information (callback function, file descriptor, etc) is
1861      * saved in an InputSource structure
1862      */
1863     InputSource *is = (InputSource *) data;
1864
1865     if (is->lineByLine) {
1866         count = read(is->fd, is->unused,
1867                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1868         if (count <= 0) {
1869             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1870             return True;
1871         }
1872         is->unused += count;
1873         p = is->buf;
1874         /* break input into lines and call the callback function on each
1875          * line
1876          */
1877         while (p < is->unused) {
1878             q = memchr(p, '\n', is->unused - p);
1879             if (q == NULL) break;
1880             q++;
1881             (is->func)(is, is->closure, p, q - p, 0);
1882             p = q;
1883         }
1884         /* remember not yet used part of the buffer */
1885         q = is->buf;
1886         while (p < is->unused) {
1887             *q++ = *p++;
1888         }
1889         is->unused = q;
1890     } else {
1891       /* read maximum length of input buffer and send the whole buffer
1892        * to the callback function
1893        */
1894         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1895         if (count == -1)
1896           error = errno;
1897         else
1898           error = 0;
1899         (is->func)(is, is->closure, is->buf, count, error);
1900     }
1901     return True; // Must return true or the watch will be removed
1902 }
1903
1904 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1905      ProcRef pr;
1906      int lineByLine;
1907      InputCallback func;
1908      VOIDSTAR closure;
1909 {
1910     InputSource *is;
1911     GIOChannel *channel;
1912     ChildProc *cp = (ChildProc *) pr;
1913
1914     is = (InputSource *) calloc(1, sizeof(InputSource));
1915     is->lineByLine = lineByLine;
1916     is->func = func;
1917     if (pr == NoProc) {
1918         is->kind = CPReal;
1919         is->fd = fileno(stdin);
1920     } else {
1921         is->kind = cp->kind;
1922         is->fd = cp->fdFrom;
1923     }
1924     if (lineByLine)
1925       is->unused = is->buf;
1926     else
1927       is->unused = NULL;
1928
1929    /* GTK-TODO: will this work on windows?*/
1930
1931     channel = g_io_channel_unix_new(is->fd);
1932     g_io_channel_set_close_on_unref (channel, TRUE);
1933     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1934
1935     is->closure = closure;
1936     return (InputSourceRef) is;
1937 }
1938
1939
1940 void
1941 RemoveInputSource(isr)
1942      InputSourceRef isr;
1943 {
1944     InputSource *is = (InputSource *) isr;
1945
1946     if (is->sid == 0) return;
1947     g_source_remove(is->sid);
1948     is->sid = 0;
1949     return;
1950 }
1951
1952 #ifndef HAVE_USLEEP
1953
1954 static Boolean frameWaiting;
1955
1956 static RETSIGTYPE
1957 FrameAlarm (int sig)
1958 {
1959   frameWaiting = False;
1960   /* In case System-V style signals.  Needed?? */
1961   signal(SIGALRM, FrameAlarm);
1962 }
1963
1964 void
1965 FrameDelay (int time)
1966 {
1967   struct itimerval delay;
1968
1969   if (time > 0) {
1970     frameWaiting = True;
1971     signal(SIGALRM, FrameAlarm);
1972     delay.it_interval.tv_sec =
1973       delay.it_value.tv_sec = time / 1000;
1974     delay.it_interval.tv_usec =
1975       delay.it_value.tv_usec = (time % 1000) * 1000;
1976     setitimer(ITIMER_REAL, &delay, NULL);
1977     while (frameWaiting) pause();
1978     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
1979     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
1980     setitimer(ITIMER_REAL, &delay, NULL);
1981   }
1982 }
1983
1984 #else
1985
1986 void
1987 FrameDelay (int time)
1988 {
1989 #ifdef TODO_GTK
1990   XSync(xDisplay, False);
1991 #endif
1992 //  gtk_main_iteration_do(False);
1993
1994   if (time > 0)
1995     usleep(time * 1000);
1996 }
1997
1998 #endif
1999
2000 static void
2001 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2002 {
2003     char buf[MSG_SIZ], *logoName = buf;
2004     if(appData.logo[n][0]) {
2005         logoName = appData.logo[n];
2006     } else if(appData.autoLogo) {
2007         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2008             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2009         } else if(appData.directory[n] && appData.directory[n][0]) {
2010             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2011         }
2012     }
2013     if(logoName[0])
2014         { ASSIGN(cps->programLogo, logoName); }
2015 }
2016
2017 void
2018 UpdateLogos (int displ)
2019 {
2020     if(optList[W_WHITE-1].handle == NULL) return;
2021     LoadLogo(&first, 0, 0);
2022     LoadLogo(&second, 1, appData.icsActive);
2023     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2024     return;
2025 }
2026
2027 void FileNamePopUpWrapper(label, def, filter, proc, pathFlag, openMode, name, fp)
2028      char *label;
2029      char *def;
2030      char *filter;
2031      FileProc proc;
2032      char *openMode;
2033      Boolean pathFlag;
2034      char **name;
2035      FILE **fp;
2036 {
2037   GtkWidget     *dialog;
2038   GtkFileFilter *gtkfilter;
2039   GtkFileFilter *gtkfilter_all;
2040   char space[]     = " ";
2041   char fileext[10] = "";
2042   char *result     = NULL;
2043   char *cp;
2044
2045   /* make a copy of the filter string, so that strtok can work with it*/
2046   cp = strndup(filter,strlen(filter));
2047
2048   /* add filters for file extensions */
2049   gtkfilter     = gtk_file_filter_new();
2050   gtkfilter_all = gtk_file_filter_new();
2051
2052   /* one filter to show everything */
2053   gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2054   gtk_file_filter_set_name   (gtkfilter_all, "All Files");
2055
2056   /* add filter if present */
2057   result = strtok(cp, space);
2058   while( result != NULL  ) {
2059     snprintf(fileext,10,"*%s",result);
2060     result = strtok( NULL, space );
2061     gtk_file_filter_add_pattern(gtkfilter, fileext);
2062   };
2063
2064   /* second filter to only show what's useful */
2065   gtk_file_filter_set_name (gtkfilter,filter);
2066
2067   if (openMode[0] == 'r')
2068     {
2069       dialog = gtk_file_chooser_dialog_new (label,
2070                                             NULL,
2071                                             GTK_FILE_CHOOSER_ACTION_OPEN,
2072                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2073                                             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2074                                             NULL);
2075     }
2076   else
2077     {
2078       dialog = gtk_file_chooser_dialog_new (label,
2079                                             NULL,
2080                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2081                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2082                                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2083                                             NULL);
2084       /* add filename suggestions */
2085       if (strlen(def) > 0 )
2086         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2087
2088       //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2089     }
2090
2091   /* add filters */
2092   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2093   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2094   /* activate filter */
2095   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2096
2097   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2098     {
2099       char *filename;
2100       FILE *f;
2101
2102       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2103
2104       //see loadgamepopup
2105       f = fopen(filename, openMode);
2106       if (f == NULL)
2107         {
2108           DisplayError(_("Failed to open file"), errno);
2109         }
2110       else
2111         {
2112           /* TODO add indec */
2113             *fp = f;
2114             ASSIGN(*name, filename);
2115             ScheduleDelayedEvent(DelayedLoad, 50);
2116         }
2117       g_free (filename);
2118     };
2119
2120   gtk_widget_destroy (dialog);
2121   ModeHighlight();
2122
2123   free(cp);
2124   return;
2125
2126 }