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