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