Fix GTK error auto-raising board
[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, 2013 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(shells[BoardWindow]));
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(appData.useStickyWindows) {
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         }
1501         wpMain = wpNew;
1502         DrawPosition(True, NULL);
1503         if(delayedDragTag) g_source_remove(delayedDragTag);
1504         delayedDragTag = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
1505         busy = 0;
1506 }
1507
1508 void
1509 DelayedDrag ()
1510 {
1511 //printf("old timr = %d\n", delayedDragTag);
1512     if(delayedDragTag) g_source_remove(delayedDragTag);
1513     delayedDragTag = g_timeout_add( 200, (GSourceFunc) DragProc, NULL);
1514 //printf("new timr = %d\n", delayedDragTag);
1515 }
1516
1517 static gboolean
1518 EventProc (GtkWidget *widget, GdkEvent *event, gpointer g)
1519 {
1520 //printf("event proc (%d,%d) %dx%d\n", event->configure.x, event->configure.y, event->configure.width, event->configure.height);
1521     // immediately
1522     wpNew.x = event->configure.x;
1523     wpNew.y = event->configure.y;
1524     wpNew.width  = event->configure.width;
1525     wpNew.height = event->configure.height;
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 FileNamePopUpWrapper(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 }