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