Pay attention to NO_CANCEL dialog flag
[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     char *markup;
1821     char bgcolor[10];
1822     char fgcolor[10];
1823
1824     if (highlight) {
1825         strcpy(bgcolor, "black");
1826         strcpy(fgcolor, "white");
1827     } else {
1828         strcpy(bgcolor, "white");
1829         strcpy(fgcolor, "black");
1830     }
1831     if (timer > 0 &&
1832         appData.lowTimeWarning &&
1833         (timer / 1000) < appData.icsAlarmTime) {
1834         strcpy(fgcolor, appData.lowTimeWarningColor);
1835     }
1836
1837     if (appData.clockMode) {
1838         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1839                                          bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1840     } else {
1841         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s  </span>",
1842                                          bgcolor, fgcolor, color);
1843     }
1844     gtk_label_set_markup(GTK_LABEL(w), markup);
1845     g_free(markup);
1846 }
1847
1848 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1849
1850 void
1851 SetClockIcon (int color)
1852 {
1853     GdkPixbuf *pm = *clockIcons[color];
1854     if (mainwindowIcon != pm) {
1855         mainwindowIcon = pm;
1856         gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1857     }
1858 }
1859
1860 #define INPUT_SOURCE_BUF_SIZE 8192
1861
1862 typedef struct {
1863     CPKind kind;
1864     int fd;
1865     int lineByLine;
1866     char *unused;
1867     InputCallback func;
1868     guint sid;
1869     char buf[INPUT_SOURCE_BUF_SIZE];
1870     VOIDSTAR closure;
1871 } InputSource;
1872
1873 gboolean
1874 DoInputCallback(io, cond, data)
1875      GIOChannel  *io;
1876      GIOCondition cond;
1877      gpointer    *data;
1878 {
1879   /* read input from one of the input source (for example a chess program, ICS, etc).
1880    * and call a function that will handle the input
1881    */
1882
1883     int count;
1884     int error;
1885     char *p, *q;
1886
1887     /* All information (callback function, file descriptor, etc) is
1888      * saved in an InputSource structure
1889      */
1890     InputSource *is = (InputSource *) data;
1891
1892     if (is->lineByLine) {
1893         count = read(is->fd, is->unused,
1894                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1895         if (count <= 0) {
1896             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1897             return True;
1898         }
1899         is->unused += count;
1900         p = is->buf;
1901         /* break input into lines and call the callback function on each
1902          * line
1903          */
1904         while (p < is->unused) {
1905             q = memchr(p, '\n', is->unused - p);
1906             if (q == NULL) break;
1907             q++;
1908             (is->func)(is, is->closure, p, q - p, 0);
1909             p = q;
1910         }
1911         /* remember not yet used part of the buffer */
1912         q = is->buf;
1913         while (p < is->unused) {
1914             *q++ = *p++;
1915         }
1916         is->unused = q;
1917     } else {
1918       /* read maximum length of input buffer and send the whole buffer
1919        * to the callback function
1920        */
1921         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1922         if (count == -1)
1923           error = errno;
1924         else
1925           error = 0;
1926         (is->func)(is, is->closure, is->buf, count, error);
1927     }
1928     return True; // Must return true or the watch will be removed
1929 }
1930
1931 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1932      ProcRef pr;
1933      int lineByLine;
1934      InputCallback func;
1935      VOIDSTAR closure;
1936 {
1937     InputSource *is;
1938     GIOChannel *channel;
1939     ChildProc *cp = (ChildProc *) pr;
1940
1941     is = (InputSource *) calloc(1, sizeof(InputSource));
1942     is->lineByLine = lineByLine;
1943     is->func = func;
1944     if (pr == NoProc) {
1945         is->kind = CPReal;
1946         is->fd = fileno(stdin);
1947     } else {
1948         is->kind = cp->kind;
1949         is->fd = cp->fdFrom;
1950     }
1951     if (lineByLine)
1952       is->unused = is->buf;
1953     else
1954       is->unused = NULL;
1955
1956    /* GTK-TODO: will this work on windows?*/
1957
1958     channel = g_io_channel_unix_new(is->fd);
1959     g_io_channel_set_close_on_unref (channel, TRUE);
1960     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1961
1962     is->closure = closure;
1963     return (InputSourceRef) is;
1964 }
1965
1966
1967 void
1968 RemoveInputSource(isr)
1969      InputSourceRef isr;
1970 {
1971     InputSource *is = (InputSource *) isr;
1972
1973     if (is->sid == 0) return;
1974     g_source_remove(is->sid);
1975     is->sid = 0;
1976     return;
1977 }
1978
1979 #ifndef HAVE_USLEEP
1980
1981 static Boolean frameWaiting;
1982
1983 static RETSIGTYPE
1984 FrameAlarm (int sig)
1985 {
1986   frameWaiting = False;
1987   /* In case System-V style signals.  Needed?? */
1988   signal(SIGALRM, FrameAlarm);
1989 }
1990
1991 void
1992 FrameDelay (int time)
1993 {
1994   struct itimerval delay;
1995
1996   if (time > 0) {
1997     frameWaiting = True;
1998     signal(SIGALRM, FrameAlarm);
1999     delay.it_interval.tv_sec =
2000       delay.it_value.tv_sec = time / 1000;
2001     delay.it_interval.tv_usec =
2002       delay.it_value.tv_usec = (time % 1000) * 1000;
2003     setitimer(ITIMER_REAL, &delay, NULL);
2004     while (frameWaiting) pause();
2005     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2006     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2007     setitimer(ITIMER_REAL, &delay, NULL);
2008   }
2009 }
2010
2011 #else
2012
2013 void
2014 FrameDelay (int time)
2015 {
2016 #ifdef TODO_GTK
2017   XSync(xDisplay, False);
2018 #endif
2019 //  gtk_main_iteration_do(False);
2020
2021   if (time > 0)
2022     usleep(time * 1000);
2023 }
2024
2025 #endif
2026
2027 static void
2028 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2029 {
2030     char buf[MSG_SIZ], *logoName = buf;
2031     if(appData.logo[n][0]) {
2032         logoName = appData.logo[n];
2033     } else if(appData.autoLogo) {
2034         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2035             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2036         } else if(appData.directory[n] && appData.directory[n][0]) {
2037             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2038         }
2039     }
2040     if(logoName[0])
2041         { ASSIGN(cps->programLogo, logoName); }
2042 }
2043
2044 void
2045 UpdateLogos (int displ)
2046 {
2047     if(optList[W_WHITE-1].handle == NULL) return;
2048     LoadLogo(&first, 0, 0);
2049     LoadLogo(&second, 1, appData.icsActive);
2050     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2051     return;
2052 }
2053
2054 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2055      char *label;
2056      char *def;
2057      char *filter;
2058      FileProc proc;
2059      char *openMode;
2060      Boolean pathFlag;
2061      char **name;
2062      FILE **fp;
2063 {
2064   GtkWidget     *dialog;
2065   GtkFileFilter *gtkfilter;
2066   GtkFileFilter *gtkfilter_all;
2067   char space[]     = " ";
2068   char fileext[10] = "";
2069   char *result     = NULL;
2070   char *cp;
2071
2072   /* make a copy of the filter string, so that strtok can work with it*/
2073   cp = strndup(filter,strlen(filter));
2074
2075   /* add filters for file extensions */
2076   gtkfilter     = gtk_file_filter_new();
2077   gtkfilter_all = gtk_file_filter_new();
2078
2079   /* one filter to show everything */
2080   gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2081   gtk_file_filter_set_name   (gtkfilter_all, "All Files");
2082
2083   /* add filter if present */
2084   result = strtok(cp, space);
2085   while( result != NULL  ) {
2086     snprintf(fileext,10,"*%s",result);
2087     result = strtok( NULL, space );
2088     gtk_file_filter_add_pattern(gtkfilter, fileext);
2089   };
2090
2091   /* second filter to only show what's useful */
2092   gtk_file_filter_set_name (gtkfilter,filter);
2093
2094   if (openMode[0] == 'r')
2095     {
2096       dialog = gtk_file_chooser_dialog_new (label,
2097                                             NULL,
2098                                             GTK_FILE_CHOOSER_ACTION_OPEN,
2099                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2100                                             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2101                                             NULL);
2102     }
2103   else
2104     {
2105       dialog = gtk_file_chooser_dialog_new (label,
2106                                             NULL,
2107                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2108                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2109                                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2110                                             NULL);
2111       /* add filename suggestions */
2112       if (strlen(def) > 0 )
2113         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2114
2115       //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2116     }
2117
2118   /* add filters */
2119   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2120   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2121   /* activate filter */
2122   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2123
2124   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2125     {
2126       char *filename;
2127       FILE *f;
2128
2129       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2130
2131       //see loadgamepopup
2132       f = fopen(filename, openMode);
2133       if (f == NULL)
2134         {
2135           DisplayError(_("Failed to open file"), errno);
2136         }
2137       else
2138         {
2139           /* TODO add indec */
2140             *fp = f;
2141             ASSIGN(*name, filename);
2142             ScheduleDelayedEvent(DelayedLoad, 50);
2143         }
2144       g_free (filename);
2145     };
2146
2147   gtk_widget_destroy (dialog);
2148   ModeHighlight();
2149
2150   free(cp);
2151   return;
2152
2153 }
2154