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