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