Fix clock highlighting
[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   gtk_window_get_position(GTK_WINDOW(shell), &a.x, &a.y);
560   wp->x = a.x;
561   wp->y = a.y;
562   wp->width = a.width;
563   wp->height = a.height;
564 //printf("placement: (%d,%d) %dx%d\n", a.x, a.y, a.width, a.height);
565   frameX = 3; frameY = 3; // remember to decide if windows touch
566 }
567
568 void
569 GetWindowCoords ()
570 { // wrapper to shield use of window handles from back-end (make addressible by number?)
571   // In XBoard this will have to wait until awareness of window parameters is implemented
572   GetActualPlacement(shellWidget, &wpMain);
573   if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
574   if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
575   if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
576   if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
577   if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
578   if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
579 }
580
581 void
582 PrintCommPortSettings (FILE *f, char *name)
583 { // This option does not exist in XBoard
584 }
585
586 void
587 EnsureOnScreen (int *x, int *y, int minX, int minY)
588 {
589   return;
590 }
591
592 int
593 MainWindowUp ()
594 { // [HGM] args: allows testing if main window is realized from back-end
595   return DialogExists(BoardWindow);
596 }
597
598 void
599 PopUpStartupDialog ()
600 {  // start menu not implemented in XBoard
601 }
602
603 char *
604 ConvertToLine (int argc, char **argv)
605 {
606   static char line[128*1024], buf[1024];
607   int i;
608
609   line[0] = NULLCHAR;
610   for(i=1; i<argc; i++)
611     {
612       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
613           && argv[i][0] != '{' )
614         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
615       else
616         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
617       strncat(line, buf, 128*1024 - strlen(line) - 1 );
618     }
619
620   line[strlen(line)-1] = NULLCHAR;
621   return line;
622 }
623
624 //--------------------------------------------------------------------------------------------
625
626 void
627 ResizeBoardWindow (int w, int h, int inhibit)
628 {
629     w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
630     h += marginH;
631     gtk_window_resize(GTK_WINDOW(shellWidget), w, h);
632 }
633
634 int
635 MakeColors ()
636 {   // dummy, as the GTK code does not make colors in advance
637     return FALSE;
638 }
639
640 void
641 InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
642 {   // determine what fonts to use, and create them
643 #ifdef TODO_GTK
644     XrmValue vTo;
645     XrmDatabase xdb;
646
647     if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
648         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
649     if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
650         appData.font = fontTable[MESSAGE_FONT][squareSize];
651     if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
652         appData.coordFont = fontTable[COORD_FONT][squareSize];
653
654 #if ENABLE_NLS
655     appData.font = InsertPxlSize(appData.font, fontPxlSize);
656     appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
657     appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
658     fontSet = CreateFontSet(appData.font);
659     clockFontSet = CreateFontSet(appData.clockFont);
660     {
661       /* For the coordFont, use the 0th font of the fontset. */
662       XFontSet coordFontSet = CreateFontSet(appData.coordFont);
663       XFontStruct **font_struct_list;
664       XFontSetExtents *fontSize;
665       char **font_name_list;
666       XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
667       coordFontID = XLoadFont(xDisplay, font_name_list[0]);
668       coordFontStruct = XQueryFont(xDisplay, coordFontID);
669       fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
670       textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
671     }
672 #else
673     appData.font = FindFont(appData.font, fontPxlSize);
674     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
675     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
676     clockFontID = XLoadFont(xDisplay, appData.clockFont);
677     clockFontStruct = XQueryFont(xDisplay, clockFontID);
678     coordFontID = XLoadFont(xDisplay, appData.coordFont);
679     coordFontStruct = XQueryFont(xDisplay, coordFontID);
680     // textHeight in !NLS mode!
681 #endif
682     countFontID = coordFontID;  // [HGM] holdings
683     countFontStruct = coordFontStruct;
684
685     xdb = XtDatabase(xDisplay);
686 #if ENABLE_NLS
687     XrmPutLineResource(&xdb, "*international: True");
688     vTo.size = sizeof(XFontSet);
689     vTo.addr = (XtPointer) &fontSet;
690     XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
691 #else
692     XrmPutStringResource(&xdb, "*font", appData.font);
693 #endif
694 #endif
695 }
696
697 char *
698 PrintArg (ArgType t)
699 {
700   char *p="";
701   switch(t) {
702     case ArgZ:
703     case ArgInt:      p = " N"; break;
704     case ArgString:   p = " STR"; break;
705     case ArgBoolean:  p = " TF"; break;
706     case ArgSettingsFilename:
707     case ArgFilename: p = " FILE"; break;
708     case ArgX:        p = " Nx"; break;
709     case ArgY:        p = " Ny"; break;
710     case ArgAttribs:  p = " TEXTCOL"; break;
711     case ArgColor:    p = " COL"; break;
712     case ArgFont:     p = " FONT"; break;
713     case ArgBoardSize: p = " SIZE"; break;
714     case ArgFloat: p = " FLOAT"; break;
715     case ArgTrue:
716     case ArgFalse:
717     case ArgTwo:
718     case ArgNone:
719     case ArgCommSettings:
720       break;
721   }
722   return p;
723 }
724
725 void
726 PrintOptions ()
727 {
728   char buf[MSG_SIZ];
729   int len=0;
730   ArgDescriptor *q, *p = argDescriptors+5;
731   printf("\nXBoard accepts the following options:\n"
732          "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
733          " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
734          " SIZE = board-size spec(s)\n"
735          " Within parentheses are short forms, or options to set to true or false.\n"
736          " Persistent options (saved in the settings file) are marked with *)\n\n");
737   while(p->argName) {
738     if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
739     snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
740     if(p->save) strcat(buf+len, "*");
741     for(q=p+1; q->argLoc == p->argLoc; q++) {
742       if(q->argName[0] == '-') continue;
743       strcat(buf+len, q == p+1 ? " (" : " ");
744       sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
745     }
746     if(q != p+1) strcat(buf+len, ")");
747     len = strlen(buf);
748     if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
749     p = q;
750   }
751   if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
752 }
753
754 int
755 main (int argc, char **argv)
756 {
757     int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
758     int boardWidth, boardHeight, w, h;
759     char *p;
760     int forceMono = False;
761
762     srandom(time(0)); // [HGM] book: make random truly random
763
764     setbuf(stdout, NULL);
765     setbuf(stderr, NULL);
766     debugFP = stderr;
767
768     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
769         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
770         exit(0);
771     }
772
773     if(argc > 1 && !strcmp(argv[1], "--help" )) {
774         PrintOptions();
775         exit(0);
776     }
777
778     /* set up GTK */
779     gtk_init (&argc, &argv);
780
781     programName = strrchr(argv[0], '/');
782     if (programName == NULL)
783       programName = argv[0];
784     else
785       programName++;
786
787 #ifdef ENABLE_NLS
788 //    if (appData.debugMode) {
789 //      fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
790 //    }
791
792     bindtextdomain(PACKAGE, LOCALEDIR);
793     textdomain(PACKAGE);
794 #endif
795
796     appData.boardSize = "";
797     InitAppData(ConvertToLine(argc, argv));
798     p = getenv("HOME");
799     if (p == NULL) p = "/tmp";
800     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
801     gameCopyFilename = (char*) malloc(i);
802     gamePasteFilename = (char*) malloc(i);
803     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
804     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
805
806     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
807         static char buf[MSG_SIZ];
808         EscapeExpand(buf, appData.firstInitString);
809         appData.firstInitString = strdup(buf);
810         EscapeExpand(buf, appData.secondInitString);
811         appData.secondInitString = strdup(buf);
812         EscapeExpand(buf, appData.firstComputerString);
813         appData.firstComputerString = strdup(buf);
814         EscapeExpand(buf, appData.secondComputerString);
815         appData.secondComputerString = strdup(buf);
816     }
817
818     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
819         chessDir = ".";
820     } else {
821         if (chdir(chessDir) != 0) {
822             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
823             perror(chessDir);
824             exit(1);
825         }
826     }
827
828     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
829         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
830         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
831            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
832            exit(errno);
833         }
834         setbuf(debugFP, NULL);
835     }
836
837 #if ENABLE_NLS
838     if (appData.debugMode) {
839       fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
840     }
841 #endif
842
843     /* [HGM,HR] make sure board size is acceptable */
844     if(appData.NrFiles > BOARD_FILES ||
845        appData.NrRanks > BOARD_RANKS   )
846          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
847
848 #if !HIGHDRAG
849     /* This feature does not work; animation needs a rewrite */
850     appData.highlightDragging = FALSE;
851 #endif
852     InitBackEnd1();
853
854         gameInfo.variant = StringToVariant(appData.variant);
855         InitPosition(FALSE);
856
857     /*
858      * determine size, based on supplied or remembered -size, or screen size
859      */
860     if (isdigit(appData.boardSize[0])) {
861         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
862                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
863                    &fontPxlSize, &smallLayout, &tinyLayout);
864         if (i == 0) {
865             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
866                     programName, appData.boardSize);
867             exit(2);
868         }
869         if (i < 7) {
870             /* Find some defaults; use the nearest known size */
871             SizeDefaults *szd, *nearest;
872             int distance = 99999;
873             nearest = szd = sizeDefaults;
874             while (szd->name != NULL) {
875                 if (abs(szd->squareSize - squareSize) < distance) {
876                     nearest = szd;
877                     distance = abs(szd->squareSize - squareSize);
878                     if (distance == 0) break;
879                 }
880                 szd++;
881             }
882             if (i < 2) lineGap = nearest->lineGap;
883             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
884             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
885             if (i < 5) fontPxlSize = nearest->fontPxlSize;
886             if (i < 6) smallLayout = nearest->smallLayout;
887             if (i < 7) tinyLayout = nearest->tinyLayout;
888         }
889     } else {
890         SizeDefaults *szd = sizeDefaults;
891         if (*appData.boardSize == NULLCHAR) {
892             GdkScreen *screen = gtk_window_get_screen(GTK_WINDOW(mainwindow));
893             guint screenwidth = gdk_screen_get_width(screen);
894             guint screenheight = gdk_screen_get_height(screen);
895             while (screenwidth < szd->minScreenSize ||
896                    screenheight < szd->minScreenSize) {
897               szd++;
898             }
899             if (szd->name == NULL) szd--;
900             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
901         } else {
902             while (szd->name != NULL &&
903                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
904             if (szd->name == NULL) {
905                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
906                         programName, appData.boardSize);
907                 exit(2);
908             }
909         }
910         squareSize = szd->squareSize;
911         lineGap = szd->lineGap;
912         clockFontPxlSize = szd->clockFontPxlSize;
913         coordFontPxlSize = szd->coordFontPxlSize;
914         fontPxlSize = szd->fontPxlSize;
915         smallLayout = szd->smallLayout;
916         tinyLayout = szd->tinyLayout;
917         // [HGM] font: use defaults from settings file if available and not overruled
918     }
919
920     defaultLineGap = lineGap;
921     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
922
923     /* [HR] height treated separately (hacked) */
924     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
925     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
926
927     /*
928      * Determine what fonts to use.
929      */
930 #ifdef TODO_GTK
931     InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
932 #endif
933
934     /*
935      * Detect if there are not enough colors available and adapt.
936      */
937 #ifdef TODO_GTK
938     if (DefaultDepth(xDisplay, xScreen) <= 2) {
939       appData.monoMode = True;
940     }
941 #endif
942
943     forceMono = MakeColors();
944
945     if (forceMono) {
946       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
947               programName);
948         appData.monoMode = True;
949     }
950
951     ParseIcsTextColors();
952
953     /*
954      * widget hierarchy
955      */
956     if (tinyLayout) {
957         layoutName = "tinyLayout";
958     } else if (smallLayout) {
959         layoutName = "smallLayout";
960     } else {
961         layoutName = "normalLayout";
962     }
963
964     optList = BoardPopUp(squareSize, lineGap, (void*)
965 #ifdef TODO_GTK
966 #if ENABLE_NLS
967                                                 &clockFontSet);
968 #else
969                                                 clockFontStruct);
970 #endif
971 #else
972 0);
973 #endif
974     InitDrawingHandle(optList + W_BOARD);
975     shellWidget      = shells[BoardWindow];
976     currBoard        = &optList[W_BOARD];
977     boardWidget      = optList[W_BOARD].handle;
978     menuBarWidget    = optList[W_MENU].handle;
979     dropMenu         = optList[W_DROP].handle;
980     titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
981 #ifdef TODO_GTK
982     formWidget  = XtParent(boardWidget);
983     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
984     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
985     XtGetValues(optList[W_WHITE].handle, args, 2);
986     if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
987       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
988       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
989       XtGetValues(optList[W_PAUSE].handle, args, 2);
990     }
991 #endif
992
993     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
994     //       not need to go into InitDrawingSizes().
995
996     InitMenuMarkers();
997
998     /*
999      * Create an icon. (Use two icons, to indicate whther it is white's or black's turn.)
1000      */
1001     WhiteIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_white.svg", NULL);
1002     BlackIcon  = gdk_pixbuf_new_from_file(SVGDIR "/icon_black.svg", NULL);
1003     mainwindowIcon = WhiteIcon;
1004     gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1005
1006
1007     /*
1008      * Create a cursor for the board widget.
1009      */
1010 #ifdef TODO_GTK
1011     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
1012     XChangeWindowAttributes(xDisplay, xBoardWindow,
1013                             CWCursor, &window_attributes);
1014 #endif
1015
1016     /*
1017      * Inhibit shell resizing.
1018      */
1019 #ifdef TODO_GTK
1020     shellArgs[0].value = (XtArgVal) &w;
1021     shellArgs[1].value = (XtArgVal) &h;
1022     XtGetValues(shellWidget, shellArgs, 2);
1023     shellArgs[4].value = shellArgs[2].value = w;
1024     shellArgs[5].value = shellArgs[3].value = h;
1025 //    XtSetValues(shellWidget, &shellArgs[2], 4);
1026 #endif
1027     {
1028         GtkAllocation a;
1029         gtk_widget_get_allocation(shells[BoardWindow], &a);
1030         w = a.width; h = a.height;
1031 //printf("start size (%d,%d), %dx%d\n", a.x, a.y, w, h);
1032         gtk_widget_get_allocation(boardWidget, &a);
1033         marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
1034         marginH =  h - a.height - 25; // subtract 25, because GTK seems to insert this amount of extra empty space
1035         gtk_window_resize(GTK_WINDOW(shellWidget), marginW + boardWidth, marginH + boardHeight);
1036 //printf("margins h=%d v=%d\n", marginW, marginH);
1037     }
1038
1039     CreateAnyPieces();
1040     CreateGrid();
1041
1042     if(appData.logoSize)
1043     {   // locate and read user logo
1044         char buf[MSG_SIZ];
1045         snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
1046         ASSIGN(userLogo, buf);
1047     }
1048
1049     if (appData.animate || appData.animateDragging)
1050       CreateAnimVars();
1051
1052     g_signal_connect(shells[BoardWindow], "key-press-event", G_CALLBACK(KeyPressProc), NULL);
1053     g_signal_connect(shells[BoardWindow], "configure-event", G_CALLBACK(EventProc), NULL);
1054
1055     /* [AS] Restore layout */
1056     if( wpMoveHistory.visible ) {
1057       HistoryPopUp();
1058     }
1059
1060     if( wpEvalGraph.visible )
1061       {
1062         EvalGraphPopUp();
1063       };
1064
1065     if( wpEngineOutput.visible ) {
1066       EngineOutputPopUp();
1067     }
1068
1069     InitBackEnd2();
1070
1071     if (errorExitStatus == -1) {
1072         if (appData.icsActive) {
1073             /* We now wait until we see "login:" from the ICS before
1074                sending the logon script (problems with timestamp otherwise) */
1075             /*ICSInitScript();*/
1076             if (appData.icsInputBox) ICSInputBoxPopUp();
1077         }
1078
1079     #ifdef SIGWINCH
1080     signal(SIGWINCH, TermSizeSigHandler);
1081     #endif
1082         signal(SIGINT, IntSigHandler);
1083         signal(SIGTERM, IntSigHandler);
1084         if (*appData.cmailGameName != NULLCHAR) {
1085             signal(SIGUSR1, CmailSigHandler);
1086         }
1087     }
1088
1089     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1090     InitPosition(TRUE);
1091     UpdateLogos(TRUE);
1092 //    XtSetKeyboardFocus(shellWidget, formWidget);
1093 #ifdef TODO_GTK
1094     XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
1095 #endif
1096
1097     /* check for GTK events and process them */
1098 //    gtk_main();
1099 while(1) {
1100 gtk_main_iteration();
1101 }
1102
1103     if (appData.debugMode) fclose(debugFP); // [DM] debug
1104     return 0;
1105 }
1106
1107 RETSIGTYPE
1108 TermSizeSigHandler (int sig)
1109 {
1110     update_ics_width();
1111 }
1112
1113 RETSIGTYPE
1114 IntSigHandler (int sig)
1115 {
1116     ExitEvent(sig);
1117 }
1118
1119 RETSIGTYPE
1120 CmailSigHandler (int sig)
1121 {
1122     int dummy = 0;
1123     int error;
1124
1125     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
1126
1127     /* Activate call-back function CmailSigHandlerCallBack()             */
1128     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1129
1130     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1131 }
1132
1133 void
1134 CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
1135 {
1136     BoardToTop();
1137     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
1138 }
1139 /**** end signal code ****/
1140
1141
1142 #define Abs(n) ((n)<0 ? -(n) : (n))
1143
1144 #ifdef ENABLE_NLS
1145 char *
1146 InsertPxlSize (char *pattern, int targetPxlSize)
1147 {
1148     char *base_fnt_lst, strInt[12], *p, *q;
1149     int alternatives, i, len, strIntLen;
1150
1151     /*
1152      * Replace the "*" (if present) in the pixel-size slot of each
1153      * alternative with the targetPxlSize.
1154      */
1155     p = pattern;
1156     alternatives = 1;
1157     while ((p = strchr(p, ',')) != NULL) {
1158       alternatives++;
1159       p++;
1160     }
1161     snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
1162     strIntLen = strlen(strInt);
1163     base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
1164
1165     p = pattern;
1166     q = base_fnt_lst;
1167     while (alternatives--) {
1168       char *comma = strchr(p, ',');
1169       for (i=0; i<14; i++) {
1170         char *hyphen = strchr(p, '-');
1171         if (!hyphen) break;
1172         if (comma && hyphen > comma) break;
1173         len = hyphen + 1 - p;
1174         if (i == 7 && *p == '*' && len == 2) {
1175           p += len;
1176           memcpy(q, strInt, strIntLen);
1177           q += strIntLen;
1178           *q++ = '-';
1179         } else {
1180           memcpy(q, p, len);
1181           p += len;
1182           q += len;
1183         }
1184       }
1185       if (!comma) break;
1186       len = comma + 1 - p;
1187       memcpy(q, p, len);
1188       p += len;
1189       q += len;
1190     }
1191     strcpy(q, p);
1192
1193     return base_fnt_lst;
1194 }
1195
1196 #ifdef TODO_GTK
1197 XFontSet
1198 CreateFontSet (char *base_fnt_lst)
1199 {
1200     XFontSet fntSet;
1201     char **missing_list;
1202     int missing_count;
1203     char *def_string;
1204
1205     fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
1206                             &missing_list, &missing_count, &def_string);
1207     if (appData.debugMode) {
1208       int i, count;
1209       XFontStruct **font_struct_list;
1210       char **font_name_list;
1211       fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
1212       if (fntSet) {
1213         fprintf(debugFP, " got list %s, locale %s\n",
1214                 XBaseFontNameListOfFontSet(fntSet),
1215                 XLocaleOfFontSet(fntSet));
1216         count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
1217         for (i = 0; i < count; i++) {
1218           fprintf(debugFP, " got charset %s\n", font_name_list[i]);
1219         }
1220       }
1221       for (i = 0; i < missing_count; i++) {
1222         fprintf(debugFP, " missing charset %s\n", missing_list[i]);
1223       }
1224     }
1225     if (fntSet == NULL) {
1226       fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
1227       exit(2);
1228     }
1229     return fntSet;
1230 }
1231 #endif
1232 #else // not ENABLE_NLS
1233 /*
1234  * Find a font that matches "pattern" that is as close as
1235  * possible to the targetPxlSize.  Prefer fonts that are k
1236  * pixels smaller to fonts that are k pixels larger.  The
1237  * pattern must be in the X Consortium standard format,
1238  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
1239  * The return value should be freed with XtFree when no
1240  * longer needed.
1241  */
1242 char *
1243 FindFont (char *pattern, int targetPxlSize)
1244 {
1245     char **fonts, *p, *best, *scalable, *scalableTail;
1246     int i, j, nfonts, minerr, err, pxlSize;
1247
1248 #ifdef TODO_GTK
1249     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
1250     if (nfonts < 1) {
1251         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
1252                 programName, pattern);
1253         exit(2);
1254     }
1255
1256     best = fonts[0];
1257     scalable = NULL;
1258     minerr = 999999;
1259     for (i=0; i<nfonts; i++) {
1260         j = 0;
1261         p = fonts[i];
1262         if (*p != '-') continue;
1263         while (j < 7) {
1264             if (*p == NULLCHAR) break;
1265             if (*p++ == '-') j++;
1266         }
1267         if (j < 7) continue;
1268         pxlSize = atoi(p);
1269         if (pxlSize == 0) {
1270             scalable = fonts[i];
1271             scalableTail = p;
1272         } else {
1273             err = pxlSize - targetPxlSize;
1274             if (Abs(err) < Abs(minerr) ||
1275                 (minerr > 0 && err < 0 && -err == minerr)) {
1276                 best = fonts[i];
1277                 minerr = err;
1278             }
1279         }
1280     }
1281     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
1282         /* If the error is too big and there is a scalable font,
1283            use the scalable font. */
1284         int headlen = scalableTail - scalable;
1285         p = (char *) XtMalloc(strlen(scalable) + 10);
1286         while (isdigit(*scalableTail)) scalableTail++;
1287         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
1288     } else {
1289         p = (char *) XtMalloc(strlen(best) + 2);
1290         safeStrCpy(p, best, strlen(best)+1 );
1291     }
1292     if (appData.debugMode) {
1293         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
1294                 pattern, targetPxlSize, p);
1295     }
1296     XFreeFontNames(fonts);
1297 #endif
1298     return p;
1299 }
1300 #endif
1301
1302 void
1303 EnableNamedMenuItem (char *menuRef, int state)
1304 {
1305     MenuItem *item = MenuNameToItem(menuRef);
1306
1307     if(item) gtk_widget_set_sensitive(item->handle, state);
1308 }
1309
1310 void
1311 EnableButtonBar (int state)
1312 {
1313 #ifdef TODO_GTK
1314     XtSetSensitive(optList[W_BUTTON].handle, state);
1315 #endif
1316 }
1317
1318
1319 void
1320 SetMenuEnables (Enables *enab)
1321 {
1322   while (enab->name != NULL) {
1323     EnableNamedMenuItem(enab->name, enab->value);
1324     enab++;
1325   }
1326 }
1327
1328 gboolean KeyPressProc(window, eventkey, data)
1329      GtkWindow *window;
1330      GdkEventKey  *eventkey;
1331      gpointer data;
1332 {
1333
1334     MoveTypeInProc(eventkey); // pop up for typed in moves
1335
1336 #ifdef TODO_GTK
1337     /* check for other key values */
1338     switch(eventkey->keyval) {
1339         case GDK_question:
1340           AboutGameEvent();
1341           break;
1342         default:
1343           break;
1344     }
1345 #endif
1346     return False;
1347 }
1348 #ifdef TODO_GTK
1349 void
1350 KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1351 {   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
1352     MenuItem *item;
1353     if(*nprms == 0) return;
1354     item = MenuNameToItem(prms[0]);
1355     if(item) ((MenuProc *) item->proc) ();
1356 }
1357 #endif
1358
1359 void
1360 SetupDropMenu ()
1361 {
1362 #ifdef TODO_GTK
1363     int i, j, count;
1364     char label[32];
1365     Arg args[16];
1366     Widget entry;
1367     char* p;
1368
1369     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
1370         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
1371         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
1372                    dmEnables[i].piece);
1373         XtSetSensitive(entry, p != NULL || !appData.testLegality
1374                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
1375                                        && !appData.icsActive));
1376         count = 0;
1377         while (p && *p++ == dmEnables[i].piece) count++;
1378         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
1379         j = 0;
1380         XtSetArg(args[j], XtNlabel, label); j++;
1381         XtSetValues(entry, args, j);
1382     }
1383 #endif
1384 }
1385
1386 static void
1387 do_flash_delay (unsigned long msec)
1388 {
1389     TimeDelay(msec);
1390 }
1391
1392 void
1393 FlashDelay (int flash_delay)
1394 {
1395         if(flash_delay) do_flash_delay(flash_delay);
1396 }
1397
1398 double
1399 Fraction (int x, int start, int stop)
1400 {
1401    double f = ((double) x - start)/(stop - start);
1402    if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
1403    return f;
1404 }
1405
1406 static WindowPlacement wpNew;
1407
1408 void
1409 CoDrag (GtkWidget *sh, WindowPlacement *wp)
1410 {
1411     int touch=0, fudge = 2, f = 2;
1412     GetActualPlacement(sh, wp);
1413     if(abs(wpMain.x + wpMain.width + 2*frameX - f - wp->x)         < fudge) touch = 1; else // right touch
1414     if(abs(wp->x + wp->width + 2*frameX + f - wpMain.x)            < fudge) touch = 2; else // left touch
1415     if(abs(wpMain.y + wpMain.height + frameX - f + frameY - wp->y) < fudge) touch = 3; else // bottom touch
1416     if(abs(wp->y + wp->height + frameX + frameY + f - wpMain.y)    < fudge) touch = 4;      // top touch
1417 //printf("CoDrag: touch = %d x=%d w=%d x2=%d w2=%d fx=%d\n", touch, wpMain.x, wpMain.width, wp->x, wp->width, frameX);
1418     if(!touch ) return; // only windows that touch co-move
1419     if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
1420         int heightInc = wpNew.height - wpMain.height;
1421         double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1422         double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
1423         wp->y += fracTop * heightInc;
1424         heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
1425 #ifdef TODO_GTK
1426         if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
1427 #endif
1428         wp->height += heightInc;
1429     } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
1430         int widthInc = wpNew.width - wpMain.width;
1431         double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1432         double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
1433         wp->y += fracLeft * widthInc;
1434         widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
1435 #ifdef TODO_GTK
1436         if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
1437 #endif
1438         wp->width += widthInc;
1439     }
1440     wp->x += wpNew.x - wpMain.x;
1441     wp->y += wpNew.y - wpMain.y;
1442     if(touch == 1) wp->x += wpNew.width - wpMain.width; else
1443     if(touch == 3) wp->y += wpNew.height - wpMain.height;
1444 #ifdef TODO_GTK
1445     XtSetArg(args[j], XtNx, wp->x); j++;
1446     XtSetArg(args[j], XtNy, wp->y); j++;
1447     XtSetValues(sh, args, j);
1448 #endif
1449         gtk_window_move(GTK_WINDOW(sh), wp->x, wp->y);
1450 //printf("moved to (%d,%d)\n", wp->x, wp->y);
1451         gtk_window_resize(GTK_WINDOW(sh), wp->width, wp->height);
1452 }
1453
1454 void
1455 ReSize (WindowPlacement *wp)
1456 {
1457         int sqx, sqy, w, h;
1458         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1459         sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
1460         sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
1461         if(sqy < sqx) sqx = sqy;
1462         if(sqx != squareSize) {
1463 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1464             squareSize = sqx; // adopt new square size
1465             CreatePNGPieces(); // make newly scaled pieces
1466             InitDrawingSizes(0, 0); // creates grid etc.
1467         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1468         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1469         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1470         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1471         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1472 }
1473
1474 static guint delayedDragTag = 0;
1475
1476 void
1477 DragProc ()
1478 {
1479         static int busy;
1480         if(busy) return;
1481
1482         busy = 1;
1483 //      GetActualPlacement(shellWidget, &wpNew);
1484 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1485         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1486            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1487             busy = 0; return; // false alarm
1488         }
1489         ReSize(&wpNew);
1490         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1491         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1492         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1493         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1494         wpMain = wpNew;
1495         DrawPosition(True, NULL);
1496         if(delayedDragTag) g_source_remove(delayedDragTag);
1497         delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1498         busy = 0;
1499 }
1500
1501 void
1502 DelayedDrag ()
1503 {
1504 //printf("old timr = %d\n", delayedDragTag);
1505     if(delayedDragTag) g_source_remove(delayedDragTag);
1506     delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1507 //printf("new timr = %d\n", delayedDragTag);
1508 }
1509
1510 static gboolean
1511 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1512 {
1513 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1514     // immediately
1515     wpNew.x = event->configure.x;
1516     wpNew.y = event->configure.y;
1517     wpNew.width  = event->configure.width;
1518     wpNew.height = event->configure.height;
1519     if(appData.useStickyWindows)
1520         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1521     return FALSE;
1522 }
1523
1524
1525
1526 /* Disable all user input other than deleting the window */
1527 static int frozen = 0;
1528
1529 void
1530 FreezeUI ()
1531 {
1532   if (frozen) return;
1533   /* Grab by a widget that doesn't accept input */
1534   gtk_grab_add(optList[W_MESSG].handle);
1535   frozen = 1;
1536 }
1537
1538 /* Undo a FreezeUI */
1539 void
1540 ThawUI ()
1541 {
1542   if (!frozen) return;
1543   gtk_grab_remove(optList[W_MESSG].handle);
1544   frozen = 0;
1545 }
1546
1547 void
1548 ModeHighlight ()
1549 {
1550     static int oldPausing = FALSE;
1551     static GameMode oldmode = (GameMode) -1;
1552     char *wname;
1553     if (!boardWidget) return;
1554
1555     if (pausing != oldPausing) {
1556         oldPausing = pausing;
1557         MarkMenuItem("Mode.Pause", pausing);
1558
1559         if (appData.showButtonBar) {
1560           /* Always toggle, don't set.  Previous code messes up when
1561              invoked while the button is pressed, as releasing it
1562              toggles the state again. */
1563             GdkColor color;     
1564             gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1565             gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1566         }
1567     }
1568
1569     wname = ModeToWidgetName(oldmode);
1570     if (wname != NULL) {
1571         MarkMenuItem(wname, False);
1572     }
1573     wname = ModeToWidgetName(gameMode);
1574     if (wname != NULL) {
1575         MarkMenuItem(wname, True);
1576     }
1577     oldmode = gameMode;
1578     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1579
1580     /* Maybe all the enables should be handled here, not just this one */
1581     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1582
1583     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1584 }
1585
1586
1587 /*
1588  * Button/menu procedures
1589  */
1590
1591 void CopyFileToClipboard(gchar *filename)
1592 {
1593     gchar *selection_tmp;
1594     GtkClipboard *cb;
1595
1596     // read the file
1597     FILE* f = fopen(filename, "r");
1598     long len;
1599     size_t count;
1600     if (f == NULL) return;
1601     fseek(f, 0, 2);
1602     len = ftell(f);
1603     rewind(f);
1604     selection_tmp = g_try_malloc(len + 1);
1605     if (selection_tmp == NULL) {
1606         printf("Malloc failed in CopyFileToClipboard\n");
1607         return;
1608     }
1609     count = fread(selection_tmp, 1, len, f);
1610     fclose(f);
1611     if (len != count) {
1612       g_free(selection_tmp);
1613       return;
1614     }
1615     selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1616     
1617     // copy selection_tmp to clipboard
1618     GdkDisplay *gdisp = gdk_display_get_default();
1619     if (!gdisp) {
1620         g_free(selection_tmp);
1621         return;
1622     }
1623     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1624     gtk_clipboard_set_text(cb, selection_tmp, -1);
1625     g_free(selection_tmp);    
1626 }
1627
1628 void
1629 CopySomething (char *src)
1630 {
1631     GdkDisplay *gdisp = gdk_display_get_default();
1632     GtkClipboard *cb;
1633     if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1634     if (gdisp == NULL) return;
1635     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1636     gtk_clipboard_set_text(cb, src, -1);
1637 }
1638
1639 void
1640 PastePositionProc ()
1641 {
1642     GdkDisplay *gdisp = gdk_display_get_default();
1643     GtkClipboard *cb;
1644     gchar *fenstr;
1645
1646     if (gdisp == NULL) return;
1647     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);    
1648     fenstr = gtk_clipboard_wait_for_text(cb);
1649     if (fenstr==NULL) return; // nothing had been selected to copy  
1650     EditPositionPasteFEN(fenstr);
1651     return;
1652 }
1653
1654 void
1655 PasteGameProc ()
1656 {
1657     gchar *text=NULL;
1658     GtkClipboard *cb;
1659     guint len=0;
1660     FILE* f;
1661
1662     // get game from clipboard
1663     GdkDisplay *gdisp = gdk_display_get_default();
1664     if (gdisp == NULL) return;
1665     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);    
1666     text = gtk_clipboard_wait_for_text(cb);
1667     if (text == NULL) return; // nothing to paste  
1668     len = strlen(text);
1669
1670     // write to temp file
1671     if (text == NULL || len == 0) {
1672       return; //nothing to paste 
1673     }
1674     f = fopen(gamePasteFilename, "w");
1675     if (f == NULL) {
1676       DisplayError(_("Can't open temp file"), errno);
1677       return;
1678     }
1679     fwrite(text, 1, len, f);
1680     fclose(f);
1681
1682     // load from file 
1683     LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1684     return;
1685 }
1686
1687
1688 #ifdef TODO_GTK
1689 void
1690 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1691 {
1692     QuitProc();
1693 }
1694 #endif
1695
1696 void MoveTypeInProc(eventkey)
1697     GdkEventKey  *eventkey;
1698 {
1699     char buf[10];
1700
1701     // ingnore if ctrl or alt is pressed
1702     if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1703         return;
1704     }
1705
1706     buf[0]=eventkey->keyval;
1707     buf[1]='\0';
1708     if (*buf >= 32)        
1709         BoxAutoPopUp (buf);
1710 }
1711
1712 #ifdef TODO_GTK
1713 void
1714 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1715 {
1716         if (!TempBackwardActive) {
1717                 TempBackwardActive = True;
1718                 BackwardEvent();
1719         }
1720 }
1721
1722 void
1723 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1724 {
1725         /* Check to see if triggered by a key release event for a repeating key.
1726          * If so the next queued event will be a key press of the same key at the same time */
1727         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1728                 XEvent next;
1729                 XPeekEvent(xDisplay, &next);
1730                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1731                         next.xkey.keycode == event->xkey.keycode)
1732                                 return;
1733         }
1734     ForwardEvent();
1735         TempBackwardActive = False;
1736 }
1737
1738 void
1739 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1740 {   // called as key binding
1741     char buf[MSG_SIZ];
1742     String name;
1743     if (nprms && *nprms > 0)
1744       name = prms[0];
1745     else
1746       name = "xboard";
1747     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1748     system(buf);
1749 }
1750 #endif
1751
1752 void
1753 ManProc ()
1754 {   // called from menu
1755 #ifdef TODO_GTK
1756     ManInner(NULL, NULL, NULL, NULL);
1757 #endif
1758 }
1759
1760 void
1761 SetWindowTitle (char *text, char *title, char *icon)
1762 {
1763 #ifdef TODO_GTK
1764     Arg args[16];
1765     int i;
1766     if (appData.titleInWindow) {
1767         i = 0;
1768         XtSetArg(args[i], XtNlabel, text);   i++;
1769         XtSetValues(titleWidget, args, i);
1770     }
1771     i = 0;
1772     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
1773     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
1774     XtSetValues(shellWidget, args, i);
1775     XSync(xDisplay, False);
1776 #endif
1777     if (appData.titleInWindow) {
1778         SetWidgetLabel(titleWidget, text);
1779     }
1780     gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1781 }
1782
1783
1784 void
1785 DisplayIcsInteractionTitle (String message)
1786 {
1787 #ifdef TODO_GTK
1788   if (oldICSInteractionTitle == NULL) {
1789     /* Magic to find the old window title, adapted from vim */
1790     char *wina = getenv("WINDOWID");
1791     if (wina != NULL) {
1792       Window win = (Window) atoi(wina);
1793       Window root, parent, *children;
1794       unsigned int nchildren;
1795       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1796       for (;;) {
1797         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1798         if (!XQueryTree(xDisplay, win, &root, &parent,
1799                         &children, &nchildren)) break;
1800         if (children) XFree((void *)children);
1801         if (parent == root || parent == 0) break;
1802         win = parent;
1803       }
1804       XSetErrorHandler(oldHandler);
1805     }
1806     if (oldICSInteractionTitle == NULL) {
1807       oldICSInteractionTitle = "xterm";
1808     }
1809   }
1810   printf("\033]0;%s\007", message);
1811   fflush(stdout);
1812 #endif
1813 }
1814
1815
1816 void
1817 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1818 {
1819     GtkWidget *w = (GtkWidget *) opt->handle;
1820     GdkColor col;
1821     char *markup;
1822     char bgcolor[10];
1823     char fgcolor[10];
1824
1825     if (highlight) {
1826         strcpy(bgcolor, "black");
1827         strcpy(fgcolor, "white");
1828     } else {
1829         strcpy(bgcolor, "white");
1830         strcpy(fgcolor, "black");
1831     }
1832     if (timer > 0 &&
1833         appData.lowTimeWarning &&
1834         (timer / 1000) < appData.icsAlarmTime) {
1835         strcpy(fgcolor, appData.lowTimeWarningColor);
1836     }
1837
1838     gdk_color_parse( bgcolor, &col );
1839     gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1840
1841     if (appData.clockMode) {
1842         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1843                                          bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1844     } else {
1845         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s  </span>",
1846                                          bgcolor, fgcolor, color);
1847     }
1848     gtk_label_set_markup(GTK_LABEL(w), markup);
1849     g_free(markup);
1850 }
1851
1852 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1853
1854 void
1855 SetClockIcon (int color)
1856 {
1857     GdkPixbuf *pm = *clockIcons[color];
1858     if (mainwindowIcon != pm) {
1859         mainwindowIcon = pm;
1860         gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1861     }
1862 }
1863
1864 #define INPUT_SOURCE_BUF_SIZE 8192
1865
1866 typedef struct {
1867     CPKind kind;
1868     int fd;
1869     int lineByLine;
1870     char *unused;
1871     InputCallback func;
1872     guint sid;
1873     char buf[INPUT_SOURCE_BUF_SIZE];
1874     VOIDSTAR closure;
1875 } InputSource;
1876
1877 gboolean
1878 DoInputCallback(io, cond, data)
1879      GIOChannel  *io;
1880      GIOCondition cond;
1881      gpointer    *data;
1882 {
1883   /* read input from one of the input source (for example a chess program, ICS, etc).
1884    * and call a function that will handle the input
1885    */
1886
1887     int count;
1888     int error;
1889     char *p, *q;
1890
1891     /* All information (callback function, file descriptor, etc) is
1892      * saved in an InputSource structure
1893      */
1894     InputSource *is = (InputSource *) data;
1895
1896     if (is->lineByLine) {
1897         count = read(is->fd, is->unused,
1898                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1899         if (count <= 0) {
1900             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1901             return True;
1902         }
1903         is->unused += count;
1904         p = is->buf;
1905         /* break input into lines and call the callback function on each
1906          * line
1907          */
1908         while (p < is->unused) {
1909             q = memchr(p, '\n', is->unused - p);
1910             if (q == NULL) break;
1911             q++;
1912             (is->func)(is, is->closure, p, q - p, 0);
1913             p = q;
1914         }
1915         /* remember not yet used part of the buffer */
1916         q = is->buf;
1917         while (p < is->unused) {
1918             *q++ = *p++;
1919         }
1920         is->unused = q;
1921     } else {
1922       /* read maximum length of input buffer and send the whole buffer
1923        * to the callback function
1924        */
1925         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1926         if (count == -1)
1927           error = errno;
1928         else
1929           error = 0;
1930         (is->func)(is, is->closure, is->buf, count, error);
1931     }
1932     return True; // Must return true or the watch will be removed
1933 }
1934
1935 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1936      ProcRef pr;
1937      int lineByLine;
1938      InputCallback func;
1939      VOIDSTAR closure;
1940 {
1941     InputSource *is;
1942     GIOChannel *channel;
1943     ChildProc *cp = (ChildProc *) pr;
1944
1945     is = (InputSource *) calloc(1, sizeof(InputSource));
1946     is->lineByLine = lineByLine;
1947     is->func = func;
1948     if (pr == NoProc) {
1949         is->kind = CPReal;
1950         is->fd = fileno(stdin);
1951     } else {
1952         is->kind = cp->kind;
1953         is->fd = cp->fdFrom;
1954     }
1955     if (lineByLine)
1956       is->unused = is->buf;
1957     else
1958       is->unused = NULL;
1959
1960    /* GTK-TODO: will this work on windows?*/
1961
1962     channel = g_io_channel_unix_new(is->fd);
1963     g_io_channel_set_close_on_unref (channel, TRUE);
1964     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1965
1966     is->closure = closure;
1967     return (InputSourceRef) is;
1968 }
1969
1970
1971 void
1972 RemoveInputSource(isr)
1973      InputSourceRef isr;
1974 {
1975     InputSource *is = (InputSource *) isr;
1976
1977     if (is->sid == 0) return;
1978     g_source_remove(is->sid);
1979     is->sid = 0;
1980     return;
1981 }
1982
1983 #ifndef HAVE_USLEEP
1984
1985 static Boolean frameWaiting;
1986
1987 static RETSIGTYPE
1988 FrameAlarm (int sig)
1989 {
1990   frameWaiting = False;
1991   /* In case System-V style signals.  Needed?? */
1992   signal(SIGALRM, FrameAlarm);
1993 }
1994
1995 void
1996 FrameDelay (int time)
1997 {
1998   struct itimerval delay;
1999
2000   if (time > 0) {
2001     frameWaiting = True;
2002     signal(SIGALRM, FrameAlarm);
2003     delay.it_interval.tv_sec =
2004       delay.it_value.tv_sec = time / 1000;
2005     delay.it_interval.tv_usec =
2006       delay.it_value.tv_usec = (time % 1000) * 1000;
2007     setitimer(ITIMER_REAL, &delay, NULL);
2008     while (frameWaiting) pause();
2009     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2010     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2011     setitimer(ITIMER_REAL, &delay, NULL);
2012   }
2013 }
2014
2015 #else
2016
2017 void
2018 FrameDelay (int time)
2019 {
2020 #ifdef TODO_GTK
2021   XSync(xDisplay, False);
2022 #endif
2023 //  gtk_main_iteration_do(False);
2024
2025   if (time > 0)
2026     usleep(time * 1000);
2027 }
2028
2029 #endif
2030
2031 static void
2032 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2033 {
2034     char buf[MSG_SIZ], *logoName = buf;
2035     if(appData.logo[n][0]) {
2036         logoName = appData.logo[n];
2037     } else if(appData.autoLogo) {
2038         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2039             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2040         } else if(appData.directory[n] && appData.directory[n][0]) {
2041             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2042         }
2043     }
2044     if(logoName[0])
2045         { ASSIGN(cps->programLogo, logoName); }
2046 }
2047
2048 void
2049 UpdateLogos (int displ)
2050 {
2051     if(optList[W_WHITE-1].handle == NULL) return;
2052     LoadLogo(&first, 0, 0);
2053     LoadLogo(&second, 1, appData.icsActive);
2054     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2055     return;
2056 }
2057
2058 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2059      char *label;
2060      char *def;
2061      char *filter;
2062      FileProc proc;
2063      char *openMode;
2064      Boolean pathFlag;
2065      char **name;
2066      FILE **fp;
2067 {
2068   GtkWidget     *dialog;
2069   GtkFileFilter *gtkfilter;
2070   GtkFileFilter *gtkfilter_all;
2071   char space[]     = " ";
2072   char fileext[10] = "";
2073   char *result     = NULL;
2074   char *cp;
2075
2076   /* make a copy of the filter string, so that strtok can work with it*/
2077   cp = strndup(filter,strlen(filter));
2078
2079   /* add filters for file extensions */
2080   gtkfilter     = gtk_file_filter_new();
2081   gtkfilter_all = gtk_file_filter_new();
2082
2083   /* one filter to show everything */
2084   gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2085   gtk_file_filter_set_name   (gtkfilter_all, "All Files");
2086
2087   /* add filter if present */
2088   result = strtok(cp, space);
2089   while( result != NULL  ) {
2090     snprintf(fileext,10,"*%s",result);
2091     result = strtok( NULL, space );
2092     gtk_file_filter_add_pattern(gtkfilter, fileext);
2093   };
2094
2095   /* second filter to only show what's useful */
2096   gtk_file_filter_set_name (gtkfilter,filter);
2097
2098   if (openMode[0] == 'r')
2099     {
2100       dialog = gtk_file_chooser_dialog_new (label,
2101                                             NULL,
2102                                             GTK_FILE_CHOOSER_ACTION_OPEN,
2103                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2104                                             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2105                                             NULL);
2106     }
2107   else
2108     {
2109       dialog = gtk_file_chooser_dialog_new (label,
2110                                             NULL,
2111                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2112                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2113                                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2114                                             NULL);
2115       /* add filename suggestions */
2116       if (strlen(def) > 0 )
2117         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2118
2119       //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2120     }
2121
2122   /* add filters */
2123   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2124   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2125   /* activate filter */
2126   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2127
2128   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2129     {
2130       char *filename;
2131       FILE *f;
2132
2133       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2134
2135       //see loadgamepopup
2136       f = fopen(filename, openMode);
2137       if (f == NULL)
2138         {
2139           DisplayError(_("Failed to open file"), errno);
2140         }
2141       else
2142         {
2143           /* TODO add indec */
2144             *fp = f;
2145             ASSIGN(*name, filename);
2146             ScheduleDelayedEvent(DelayedLoad, 50);
2147         }
2148       g_free (filename);
2149     };
2150
2151   gtk_widget_destroy (dialog);
2152   ModeHighlight();
2153
2154   free(cp);
2155   return;
2156
2157 }
2158