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