a3add4ada927f97fab10ad8f575c22b56bef0c38
[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, lg = lineGap;
1458         if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
1459         sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1460         sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1461         if(sqy < sqx) sqx = sqy;
1462         if(appData.overrideLineGap < 0) { // do second iteration with adjusted lineGap
1463             lg = lineGap = sqx < 37 ? 1 : sqx < 59 ? 2 : sqx < 116 ? 3 : 4;
1464             sqx = (wp->width  - lg - marginW) / BOARD_WIDTH - lg;
1465             sqy = (wp->height - lg - marginH) / BOARD_HEIGHT - lg;
1466             if(sqy < sqx) sqx = sqy;
1467         }
1468         if(sqx != squareSize) {
1469 //printf("new sq size %d (%dx%d)\n", sqx, wp->width, wp->height);
1470             squareSize = sqx; // adopt new square size
1471             CreatePNGPieces(); // make newly scaled pieces
1472             InitDrawingSizes(0, 0); // creates grid etc.
1473         } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
1474         w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
1475         h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
1476         if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
1477         if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
1478 }
1479
1480 static guint delayedDragTag = 0;
1481
1482 void
1483 DragProc ()
1484 {
1485         static int busy;
1486         if(busy) return;
1487
1488         busy = 1;
1489 //      GetActualPlacement(shellWidget, &wpNew);
1490 //printf("drag proc (%d,%d) %dx%d\n", wpNew.x, wpNew.y, wpNew.width, wpNew.height);
1491         if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
1492            wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
1493             busy = 0; return; // false alarm
1494         }
1495         ReSize(&wpNew);
1496         if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
1497         if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
1498         if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
1499         if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
1500         wpMain = wpNew;
1501         DrawPosition(True, NULL);
1502         if(delayedDragTag) g_source_remove(delayedDragTag);
1503         delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1504         busy = 0;
1505 }
1506
1507 void
1508 DelayedDrag ()
1509 {
1510 //printf("old timr = %d\n", delayedDragTag);
1511     if(delayedDragTag) g_source_remove(delayedDragTag);
1512     delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1513 //printf("new timr = %d\n", delayedDragTag);
1514 }
1515
1516 static gboolean
1517 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1518 {
1519 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1520     // immediately
1521     wpNew.x = event->configure.x;
1522     wpNew.y = event->configure.y;
1523     wpNew.width  = event->configure.width;
1524     wpNew.height = event->configure.height;
1525     if(appData.useStickyWindows)
1526         DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
1527     return FALSE;
1528 }
1529
1530
1531
1532 /* Disable all user input other than deleting the window */
1533 static int frozen = 0;
1534
1535 void
1536 FreezeUI ()
1537 {
1538   if (frozen) return;
1539   /* Grab by a widget that doesn't accept input */
1540   gtk_grab_add(optList[W_MESSG].handle);
1541   frozen = 1;
1542 }
1543
1544 /* Undo a FreezeUI */
1545 void
1546 ThawUI ()
1547 {
1548   if (!frozen) return;
1549   gtk_grab_remove(optList[W_MESSG].handle);
1550   frozen = 0;
1551 }
1552
1553 void
1554 ModeHighlight ()
1555 {
1556     static int oldPausing = FALSE;
1557     static GameMode oldmode = (GameMode) -1;
1558     char *wname;
1559     if (!boardWidget) return;
1560
1561     if (pausing != oldPausing) {
1562         oldPausing = pausing;
1563         MarkMenuItem("Mode.Pause", pausing);
1564
1565         if (appData.showButtonBar) {
1566           /* Always toggle, don't set.  Previous code messes up when
1567              invoked while the button is pressed, as releasing it
1568              toggles the state again. */
1569             GdkColor color;     
1570             gdk_color_parse( pausing ? "#808080" : "#F0F0F0", &color );
1571             gtk_widget_modify_bg ( GTK_WIDGET(optList[W_PAUSE].handle), GTK_STATE_NORMAL, &color );
1572         }
1573     }
1574
1575     wname = ModeToWidgetName(oldmode);
1576     if (wname != NULL) {
1577         MarkMenuItem(wname, False);
1578     }
1579     wname = ModeToWidgetName(gameMode);
1580     if (wname != NULL) {
1581         MarkMenuItem(wname, True);
1582     }
1583     oldmode = gameMode;
1584     MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
1585
1586     /* Maybe all the enables should be handled here, not just this one */
1587     EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
1588
1589     DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
1590 }
1591
1592
1593 /*
1594  * Button/menu procedures
1595  */
1596
1597 void CopyFileToClipboard(gchar *filename)
1598 {
1599     gchar *selection_tmp;
1600     GtkClipboard *cb;
1601
1602     // read the file
1603     FILE* f = fopen(filename, "r");
1604     long len;
1605     size_t count;
1606     if (f == NULL) return;
1607     fseek(f, 0, 2);
1608     len = ftell(f);
1609     rewind(f);
1610     selection_tmp = g_try_malloc(len + 1);
1611     if (selection_tmp == NULL) {
1612         printf("Malloc failed in CopyFileToClipboard\n");
1613         return;
1614     }
1615     count = fread(selection_tmp, 1, len, f);
1616     fclose(f);
1617     if (len != count) {
1618       g_free(selection_tmp);
1619       return;
1620     }
1621     selection_tmp[len] = NULLCHAR; // file is now in selection_tmp
1622     
1623     // copy selection_tmp to clipboard
1624     GdkDisplay *gdisp = gdk_display_get_default();
1625     if (!gdisp) {
1626         g_free(selection_tmp);
1627         return;
1628     }
1629     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1630     gtk_clipboard_set_text(cb, selection_tmp, -1);
1631     g_free(selection_tmp);    
1632 }
1633
1634 void
1635 CopySomething (char *src)
1636 {
1637     GdkDisplay *gdisp = gdk_display_get_default();
1638     GtkClipboard *cb;
1639     if(!src) { CopyFileToClipboard(gameCopyFilename); return; }
1640     if (gdisp == NULL) return;
1641     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);
1642     gtk_clipboard_set_text(cb, src, -1);
1643 }
1644
1645 void
1646 PastePositionProc ()
1647 {
1648     GdkDisplay *gdisp = gdk_display_get_default();
1649     GtkClipboard *cb;
1650     gchar *fenstr;
1651
1652     if (gdisp == NULL) return;
1653     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);    
1654     fenstr = gtk_clipboard_wait_for_text(cb);
1655     if (fenstr==NULL) return; // nothing had been selected to copy  
1656     EditPositionPasteFEN(fenstr);
1657     return;
1658 }
1659
1660 void
1661 PasteGameProc ()
1662 {
1663     gchar *text=NULL;
1664     GtkClipboard *cb;
1665     guint len=0;
1666     FILE* f;
1667
1668     // get game from clipboard
1669     GdkDisplay *gdisp = gdk_display_get_default();
1670     if (gdisp == NULL) return;
1671     cb = gtk_clipboard_get_for_display(gdisp, GDK_SELECTION_CLIPBOARD);    
1672     text = gtk_clipboard_wait_for_text(cb);
1673     if (text == NULL) return; // nothing to paste  
1674     len = strlen(text);
1675
1676     // write to temp file
1677     if (text == NULL || len == 0) {
1678       return; //nothing to paste 
1679     }
1680     f = fopen(gamePasteFilename, "w");
1681     if (f == NULL) {
1682       DisplayError(_("Can't open temp file"), errno);
1683       return;
1684     }
1685     fwrite(text, 1, len, f);
1686     fclose(f);
1687
1688     // load from file 
1689     LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
1690     return;
1691 }
1692
1693
1694 #ifdef TODO_GTK
1695 void
1696 QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1697 {
1698     QuitProc();
1699 }
1700 #endif
1701
1702 void MoveTypeInProc(eventkey)
1703     GdkEventKey  *eventkey;
1704 {
1705     char buf[10];
1706
1707     // ingnore if ctrl or alt is pressed
1708     if (eventkey->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) {
1709         return;
1710     }
1711
1712     buf[0]=eventkey->keyval;
1713     buf[1]='\0';
1714     if (*buf >= 32)        
1715         BoxAutoPopUp (buf);
1716 }
1717
1718 #ifdef TODO_GTK
1719 void
1720 TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1721 {
1722         if (!TempBackwardActive) {
1723                 TempBackwardActive = True;
1724                 BackwardEvent();
1725         }
1726 }
1727
1728 void
1729 TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1730 {
1731         /* Check to see if triggered by a key release event for a repeating key.
1732          * If so the next queued event will be a key press of the same key at the same time */
1733         if (XEventsQueued(xDisplay, QueuedAfterReading)) {
1734                 XEvent next;
1735                 XPeekEvent(xDisplay, &next);
1736                 if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
1737                         next.xkey.keycode == event->xkey.keycode)
1738                                 return;
1739         }
1740     ForwardEvent();
1741         TempBackwardActive = False;
1742 }
1743
1744 void
1745 ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
1746 {   // called as key binding
1747     char buf[MSG_SIZ];
1748     String name;
1749     if (nprms && *nprms > 0)
1750       name = prms[0];
1751     else
1752       name = "xboard";
1753     snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
1754     system(buf);
1755 }
1756 #endif
1757
1758 void
1759 ManProc ()
1760 {   // called from menu
1761 #ifdef TODO_GTK
1762     ManInner(NULL, NULL, NULL, NULL);
1763 #endif
1764 }
1765
1766 void
1767 SetWindowTitle (char *text, char *title, char *icon)
1768 {
1769 #ifdef TODO_GTK
1770     Arg args[16];
1771     int i;
1772     if (appData.titleInWindow) {
1773         i = 0;
1774         XtSetArg(args[i], XtNlabel, text);   i++;
1775         XtSetValues(titleWidget, args, i);
1776     }
1777     i = 0;
1778     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
1779     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
1780     XtSetValues(shellWidget, args, i);
1781     XSync(xDisplay, False);
1782 #endif
1783     if (appData.titleInWindow) {
1784         SetWidgetLabel(titleWidget, text);
1785     }
1786     gtk_window_set_title (GTK_WINDOW(shells[BoardWindow]), title);
1787 }
1788
1789
1790 void
1791 DisplayIcsInteractionTitle (String message)
1792 {
1793 #ifdef TODO_GTK
1794   if (oldICSInteractionTitle == NULL) {
1795     /* Magic to find the old window title, adapted from vim */
1796     char *wina = getenv("WINDOWID");
1797     if (wina != NULL) {
1798       Window win = (Window) atoi(wina);
1799       Window root, parent, *children;
1800       unsigned int nchildren;
1801       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
1802       for (;;) {
1803         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
1804         if (!XQueryTree(xDisplay, win, &root, &parent,
1805                         &children, &nchildren)) break;
1806         if (children) XFree((void *)children);
1807         if (parent == root || parent == 0) break;
1808         win = parent;
1809       }
1810       XSetErrorHandler(oldHandler);
1811     }
1812     if (oldICSInteractionTitle == NULL) {
1813       oldICSInteractionTitle = "xterm";
1814     }
1815   }
1816   printf("\033]0;%s\007", message);
1817   fflush(stdout);
1818 #endif
1819 }
1820
1821
1822 void
1823 DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
1824 {
1825     GtkWidget *w = (GtkWidget *) opt->handle;
1826     GdkColor col;
1827     char *markup;
1828     char bgcolor[10];
1829     char fgcolor[10];
1830
1831     if (highlight) {
1832         strcpy(bgcolor, "black");
1833         strcpy(fgcolor, "white");
1834     } else {
1835         strcpy(bgcolor, "white");
1836         strcpy(fgcolor, "black");
1837     }
1838     if (timer > 0 &&
1839         appData.lowTimeWarning &&
1840         (timer / 1000) < appData.icsAlarmTime) {
1841         strcpy(fgcolor, appData.lowTimeWarningColor);
1842     }
1843
1844     gdk_color_parse( bgcolor, &col );
1845     gtk_widget_modify_bg(gtk_widget_get_parent(opt->handle), GTK_STATE_NORMAL, &col);
1846
1847     if (appData.clockMode) {
1848         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s:%s%s</span>",
1849                                          bgcolor, fgcolor, color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
1850     } else {
1851         markup = g_markup_printf_escaped("<span size=\"xx-large\" weight=\"heavy\" background=\"%s\" foreground=\"%s\">%s  </span>",
1852                                          bgcolor, fgcolor, color);
1853     }
1854     gtk_label_set_markup(GTK_LABEL(w), markup);
1855     g_free(markup);
1856 }
1857
1858 static GdkPixbuf **clockIcons[] = { &WhiteIcon, &BlackIcon };
1859
1860 void
1861 SetClockIcon (int color)
1862 {
1863     GdkPixbuf *pm = *clockIcons[color];
1864     if (mainwindowIcon != pm) {
1865         mainwindowIcon = pm;
1866         gtk_window_set_icon(GTK_WINDOW(shellWidget), mainwindowIcon);
1867     }
1868 }
1869
1870 #define INPUT_SOURCE_BUF_SIZE 8192
1871
1872 typedef struct {
1873     CPKind kind;
1874     int fd;
1875     int lineByLine;
1876     char *unused;
1877     InputCallback func;
1878     guint sid;
1879     char buf[INPUT_SOURCE_BUF_SIZE];
1880     VOIDSTAR closure;
1881 } InputSource;
1882
1883 gboolean
1884 DoInputCallback(io, cond, data)
1885      GIOChannel  *io;
1886      GIOCondition cond;
1887      gpointer    *data;
1888 {
1889   /* read input from one of the input source (for example a chess program, ICS, etc).
1890    * and call a function that will handle the input
1891    */
1892
1893     int count;
1894     int error;
1895     char *p, *q;
1896
1897     /* All information (callback function, file descriptor, etc) is
1898      * saved in an InputSource structure
1899      */
1900     InputSource *is = (InputSource *) data;
1901
1902     if (is->lineByLine) {
1903         count = read(is->fd, is->unused,
1904                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
1905         if (count <= 0) {
1906             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
1907             return True;
1908         }
1909         is->unused += count;
1910         p = is->buf;
1911         /* break input into lines and call the callback function on each
1912          * line
1913          */
1914         while (p < is->unused) {
1915             q = memchr(p, '\n', is->unused - p);
1916             if (q == NULL) break;
1917             q++;
1918             (is->func)(is, is->closure, p, q - p, 0);
1919             p = q;
1920         }
1921         /* remember not yet used part of the buffer */
1922         q = is->buf;
1923         while (p < is->unused) {
1924             *q++ = *p++;
1925         }
1926         is->unused = q;
1927     } else {
1928       /* read maximum length of input buffer and send the whole buffer
1929        * to the callback function
1930        */
1931         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
1932         if (count == -1)
1933           error = errno;
1934         else
1935           error = 0;
1936         (is->func)(is, is->closure, is->buf, count, error);
1937     }
1938     return True; // Must return true or the watch will be removed
1939 }
1940
1941 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
1942      ProcRef pr;
1943      int lineByLine;
1944      InputCallback func;
1945      VOIDSTAR closure;
1946 {
1947     InputSource *is;
1948     GIOChannel *channel;
1949     ChildProc *cp = (ChildProc *) pr;
1950
1951     is = (InputSource *) calloc(1, sizeof(InputSource));
1952     is->lineByLine = lineByLine;
1953     is->func = func;
1954     if (pr == NoProc) {
1955         is->kind = CPReal;
1956         is->fd = fileno(stdin);
1957     } else {
1958         is->kind = cp->kind;
1959         is->fd = cp->fdFrom;
1960     }
1961     if (lineByLine)
1962       is->unused = is->buf;
1963     else
1964       is->unused = NULL;
1965
1966    /* GTK-TODO: will this work on windows?*/
1967
1968     channel = g_io_channel_unix_new(is->fd);
1969     g_io_channel_set_close_on_unref (channel, TRUE);
1970     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
1971
1972     is->closure = closure;
1973     return (InputSourceRef) is;
1974 }
1975
1976
1977 void
1978 RemoveInputSource(isr)
1979      InputSourceRef isr;
1980 {
1981     InputSource *is = (InputSource *) isr;
1982
1983     if (is->sid == 0) return;
1984     g_source_remove(is->sid);
1985     is->sid = 0;
1986     return;
1987 }
1988
1989 #ifndef HAVE_USLEEP
1990
1991 static Boolean frameWaiting;
1992
1993 static RETSIGTYPE
1994 FrameAlarm (int sig)
1995 {
1996   frameWaiting = False;
1997   /* In case System-V style signals.  Needed?? */
1998   signal(SIGALRM, FrameAlarm);
1999 }
2000
2001 void
2002 FrameDelay (int time)
2003 {
2004   struct itimerval delay;
2005
2006   if (time > 0) {
2007     frameWaiting = True;
2008     signal(SIGALRM, FrameAlarm);
2009     delay.it_interval.tv_sec =
2010       delay.it_value.tv_sec = time / 1000;
2011     delay.it_interval.tv_usec =
2012       delay.it_value.tv_usec = (time % 1000) * 1000;
2013     setitimer(ITIMER_REAL, &delay, NULL);
2014     while (frameWaiting) pause();
2015     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
2016     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
2017     setitimer(ITIMER_REAL, &delay, NULL);
2018   }
2019 }
2020
2021 #else
2022
2023 void
2024 FrameDelay (int time)
2025 {
2026 #ifdef TODO_GTK
2027   XSync(xDisplay, False);
2028 #endif
2029 //  gtk_main_iteration_do(False);
2030
2031   if (time > 0)
2032     usleep(time * 1000);
2033 }
2034
2035 #endif
2036
2037 static void
2038 LoadLogo (ChessProgramState *cps, int n, Boolean ics)
2039 {
2040     char buf[MSG_SIZ], *logoName = buf;
2041     if(appData.logo[n][0]) {
2042         logoName = appData.logo[n];
2043     } else if(appData.autoLogo) {
2044         if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
2045             sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
2046         } else if(appData.directory[n] && appData.directory[n][0]) {
2047             sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
2048         }
2049     }
2050     if(logoName[0])
2051         { ASSIGN(cps->programLogo, logoName); }
2052 }
2053
2054 void
2055 UpdateLogos (int displ)
2056 {
2057     if(optList[W_WHITE-1].handle == NULL) return;
2058     LoadLogo(&first, 0, 0);
2059     LoadLogo(&second, 1, appData.icsActive);
2060     if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
2061     return;
2062 }
2063
2064 void FileNamePopUpGTK(label, def, filter, proc, pathFlag, openMode, name, fp)
2065      char *label;
2066      char *def;
2067      char *filter;
2068      FileProc proc;
2069      char *openMode;
2070      Boolean pathFlag;
2071      char **name;
2072      FILE **fp;
2073 {
2074   GtkWidget     *dialog;
2075   GtkFileFilter *gtkfilter;
2076   GtkFileFilter *gtkfilter_all;
2077   char space[]     = " ";
2078   char fileext[10] = "";
2079   char *result     = NULL;
2080   char *cp;
2081
2082   /* make a copy of the filter string, so that strtok can work with it*/
2083   cp = strndup(filter,strlen(filter));
2084
2085   /* add filters for file extensions */
2086   gtkfilter     = gtk_file_filter_new();
2087   gtkfilter_all = gtk_file_filter_new();
2088
2089   /* one filter to show everything */
2090   gtk_file_filter_add_pattern(gtkfilter_all, "*.*");
2091   gtk_file_filter_set_name   (gtkfilter_all, "All Files");
2092
2093   /* add filter if present */
2094   result = strtok(cp, space);
2095   while( result != NULL  ) {
2096     snprintf(fileext,10,"*%s",result);
2097     result = strtok( NULL, space );
2098     gtk_file_filter_add_pattern(gtkfilter, fileext);
2099   };
2100
2101   /* second filter to only show what's useful */
2102   gtk_file_filter_set_name (gtkfilter,filter);
2103
2104   if (openMode[0] == 'r')
2105     {
2106       dialog = gtk_file_chooser_dialog_new (label,
2107                                             NULL,
2108                                             GTK_FILE_CHOOSER_ACTION_OPEN,
2109                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2110                                             GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2111                                             NULL);
2112     }
2113   else
2114     {
2115       dialog = gtk_file_chooser_dialog_new (label,
2116                                             NULL,
2117                                             GTK_FILE_CHOOSER_ACTION_SAVE,
2118                                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2119                                             GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2120                                             NULL);
2121       /* add filename suggestions */
2122       if (strlen(def) > 0 )
2123         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), def);
2124
2125       //gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER (dialog),TRUE);
2126     }
2127
2128   /* add filters */
2129   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter_all);
2130   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2131   /* activate filter */
2132   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog),gtkfilter);
2133
2134   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2135     {
2136       char *filename;
2137       FILE *f;
2138
2139       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2140
2141       //see loadgamepopup
2142       f = fopen(filename, openMode);
2143       if (f == NULL)
2144         {
2145           DisplayError(_("Failed to open file"), errno);
2146         }
2147       else
2148         {
2149           /* TODO add indec */
2150             *fp = f;
2151             ASSIGN(*name, filename);
2152             ScheduleDelayedEvent(DelayedLoad, 50);
2153         }
2154       g_free (filename);
2155     };
2156
2157   gtk_widget_destroy (dialog);
2158   ModeHighlight();
2159
2160   free(cp);
2161   return;
2162
2163 }
2164