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