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