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