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