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