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