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