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