fixing the last merge
[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 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
64 #if !OMIT_SOCKETS
65 # if HAVE_SYS_SOCKET_H
66 #  include <sys/socket.h>
67 #  include <netinet/in.h>
68 #  include <netdb.h>
69 # else /* not HAVE_SYS_SOCKET_H */
70 #  if HAVE_LAN_SOCKET_H
71 #   include <lan/socket.h>
72 #   include <lan/in.h>
73 #   include <lan/netdb.h>
74 #  else /* not HAVE_LAN_SOCKET_H */
75 #   define OMIT_SOCKETS 1
76 #  endif /* not HAVE_LAN_SOCKET_H */
77 # endif /* not HAVE_SYS_SOCKET_H */
78 #endif /* !OMIT_SOCKETS */
79
80 #if STDC_HEADERS
81 # include <stdlib.h>
82 # include <string.h>
83 #else /* not STDC_HEADERS */
84 extern char *getenv();
85 # if HAVE_STRING_H
86 #  include <string.h>
87 # else /* not HAVE_STRING_H */
88 #  include <strings.h>
89 # endif /* not HAVE_STRING_H */
90 #endif /* not STDC_HEADERS */
91
92 #if HAVE_SYS_FCNTL_H
93 # include <sys/fcntl.h>
94 #else /* not HAVE_SYS_FCNTL_H */
95 # if HAVE_FCNTL_H
96 #  include <fcntl.h>
97 # endif /* HAVE_FCNTL_H */
98 #endif /* not HAVE_SYS_FCNTL_H */
99
100 #if HAVE_SYS_SYSTEMINFO_H
101 # include <sys/systeminfo.h>
102 #endif /* HAVE_SYS_SYSTEMINFO_H */
103
104 #if TIME_WITH_SYS_TIME
105 # include <sys/time.h>
106 # include <time.h>
107 #else
108 # if HAVE_SYS_TIME_H
109 #  include <sys/time.h>
110 # else
111 #  include <time.h>
112 # endif
113 #endif
114
115 #if HAVE_UNISTD_H
116 # include <unistd.h>
117 #endif
118
119 #if HAVE_SYS_WAIT_H
120 # include <sys/wait.h>
121 #endif
122
123 #if HAVE_DIRENT_H
124 # include <dirent.h>
125 # define NAMLEN(dirent) strlen((dirent)->d_name)
126 # define HAVE_DIR_STRUCT
127 #else
128 # define dirent direct
129 # define NAMLEN(dirent) (dirent)->d_namlen
130 # if HAVE_SYS_NDIR_H
131 #  include <sys/ndir.h>
132 #  define HAVE_DIR_STRUCT
133 # endif
134 # if HAVE_SYS_DIR_H
135 #  include <sys/dir.h>
136 #  define HAVE_DIR_STRUCT
137 # endif
138 # if HAVE_NDIR_H
139 #  include <ndir.h>
140 #  define HAVE_DIR_STRUCT
141 # endif
142 #endif
143
144 #include <X11/Intrinsic.h>
145 #include <X11/StringDefs.h>
146 #include <X11/Shell.h>
147 #include <X11/cursorfont.h>
148 #include <X11/Xatom.h>
149 #include <X11/Xmu/Atoms.h>
150 #if USE_XAW3D
151 #include <X11/Xaw3d/Dialog.h>
152 #include <X11/Xaw3d/Form.h>
153 #include <X11/Xaw3d/List.h>
154 #include <X11/Xaw3d/Label.h>
155 #include <X11/Xaw3d/SimpleMenu.h>
156 #include <X11/Xaw3d/SmeBSB.h>
157 #include <X11/Xaw3d/SmeLine.h>
158 #include <X11/Xaw3d/Box.h>
159 #include <X11/Xaw3d/MenuButton.h>
160 #include <X11/Xaw3d/Text.h>
161 #include <X11/Xaw3d/AsciiText.h>
162 #else
163 #include <X11/Xaw/Dialog.h>
164 #include <X11/Xaw/Form.h>
165 #include <X11/Xaw/List.h>
166 #include <X11/Xaw/Label.h>
167 #include <X11/Xaw/SimpleMenu.h>
168 #include <X11/Xaw/SmeBSB.h>
169 #include <X11/Xaw/SmeLine.h>
170 #include <X11/Xaw/Box.h>
171 #include <X11/Xaw/MenuButton.h>
172 #include <X11/Xaw/Text.h>
173 #include <X11/Xaw/AsciiText.h>
174 #endif
175
176 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
177 #include "common.h"
178
179 #if HAVE_LIBXPM
180 #include <X11/xpm.h>
181 #include "pixmaps/pixmaps.h"
182 #define IMAGE_EXT "xpm"
183 #else
184 #define IMAGE_EXT "xim"
185 #include "bitmaps/bitmaps.h"
186 #endif
187
188 #include <gtk/gtk.h>
189 #include <gdk/gdk.h>
190 #include <gdk-pixbuf/gdk-pixbuf.h>
191
192 #include "bitmaps/icon_white.bm"
193 #include "bitmaps/icon_black.bm"
194 #include "bitmaps/checkmark.bm"
195
196 #include "frontend.h"
197 #include "backend.h"
198 #include "moves.h"
199 #include "xboard.h"
200 #include "childio.h"
201 #include "xgamelist.h"
202 #include "xhistory.h"
203 #include "xedittags.h"
204 #include "gettext.h"
205 #include "callback.h"
206 #include "interface.h"
207
208 // must be moved to xengineoutput.h
209
210 void EngineOutputProc P((Widget w, XEvent *event,
211                          String *prms, Cardinal *nprms));
212 void EvalGraphProc P((Widget w, XEvent *event,
213                       String *prms, Cardinal *nprms));
214
215
216 #ifdef __EMX__
217 #ifndef HAVE_USLEEP
218 #define HAVE_USLEEP
219 #endif
220 #define usleep(t)   _sleep2(((t)+500)/1000)
221 #endif
222
223 #ifdef ENABLE_NLS
224 # define  _(s) gettext (s)
225 # define N_(s) gettext_noop (s)
226 #else
227 # define  _(s) (s)
228 # define N_(s)  s
229 #endif
230
231 typedef struct {
232   String string;
233   XtActionProc proc;
234 } MenuItem;
235
236 typedef struct {
237   String name;
238   MenuItem *mi;
239 } Menu;
240
241 typedef struct {
242   char *name;
243   gboolean value;
244 } Enables;
245
246
247
248 int main P((int argc, char **argv));
249 RETSIGTYPE CmailSigHandler P((int sig));
250 RETSIGTYPE IntSigHandler P((int sig));
251 RETSIGTYPE TermSizeSigHandler P((int sig));
252 void CreateGCs P((void));
253 void CreateXIMPieces P((void));
254 void CreateXPMPieces P((void));
255 void CreatePieces P((void));
256 void CreatePieceMenus P((void));
257 Widget CreateMenuBar P((Menu *mb));
258 char *FindFont P((char *pattern, int targetPxlSize));
259 void PieceMenuPopup P((Widget w, XEvent *event,
260                        String *params, Cardinal *num_params));
261 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
262 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
263 int EventToSquare P((int x, int limit));
264 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
265 void AnimateUserMove P((Widget w, XEvent * event,
266                         String * params, Cardinal * nParams));
267 void HandlePV P((Widget w, XEvent * event,
268                  String * params, Cardinal * nParams));
269 void CommentPopUp P((char *title, char *label));
270 void CommentPopDown P((void));
271 void CommentCallback P((Widget w, XtPointer client_data,
272                         XtPointer call_data));
273 void ICSInputBoxPopUp P((void));
274 void ICSInputBoxPopDown P((void));
275 void AskQuestionReplyAction P((Widget w, XEvent *event,
276                                String *prms, Cardinal *nprms));
277 void AskQuestionProc P((Widget w, XEvent *event,
278                         String *prms, Cardinal *nprms));
279 void AskQuestionPopDown P((void));
280 void PromotionPopDown P((void));
281 void PromotionCallback P((Widget w, XtPointer client_data,
282                           XtPointer call_data));
283 void EditCommentPopDown P((void));
284 void EditCommentCallback P((Widget w, XtPointer client_data,
285                             XtPointer call_data));
286 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
287 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
288                          Cardinal *nprms));
289 void PastePositionProc P((Widget w, XEvent *event, String *prms,
290                           Cardinal *nprms));
291 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
292 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
293 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
295                            Cardinal *nprms));
296 void EditCommentProc P((Widget w, XEvent *event,
297                         String *prms, Cardinal *nprms));
298 void IcsInputBoxProc P((Widget w, XEvent *event,
299                         String *prms, Cardinal *nprms));
300 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
302 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
303 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
304 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
305 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
306 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
307 void DisplayMove P((int moveNumber));
308 void DisplayTitle P((char *title));
309 void ICSInitScript P((void));
310 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
311 void ErrorPopUp P((char *title, char *text, int modal));
312 void ErrorPopDown P((void));
313 static char *ExpandPathName P((char *path));
314 static void CreateAnimVars P((void));
315 static void DragPieceMove P((int x, int y));
316 static void DrawDragPiece P((void));
317 char *ModeToWidgetName P((GameMode mode));
318 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
319 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
320 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
321 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
322 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
323 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
324 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
325 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
326 void GameListOptionsPopDown P(());
327 void ShufflePopDown P(());
328 void EnginePopDown P(());
329 void UciPopDown P(());
330 void TimeControlPopDown P(());
331 void NewVariantPopDown P(());
332 void SettingsPopDown P(());
333 void SetMenuEnables P((Enables *enab));
334 void update_ics_width P(());
335 int get_term_width P(());
336 int CopyMemoProc P(());
337 /*
338  * XBoard depends on Xt R4 or higher
339  */
340 int xtVersion = XtSpecificationRelease;
341
342 int xScreen;
343
344 Window xBoardWindow;
345 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
346   jailSquareColor, highlightSquareColor, premoveHighlightColor;
347 Pixel lowTimeWarningColor;
348
349 #define LINE_TYPE_NORMAL 0
350 #define LINE_TYPE_HIGHLIGHT 1
351 #define LINE_TYPE_PRE 2
352
353
354 GC lightSquareGC, darkSquareGC, jailSquareGC,  wdPieceGC, wlPieceGC,
355   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC,
356   wjPieceGC, bjPieceGC;
357 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
358 Widget  layoutWidget, formWidget, boardWidget, messageWidget,
359   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
360   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
361   menuBarWidget,  editShell, errorShell, analysisShell,
362   ICSInputShell, fileNameShell, askQuestionShell;
363
364 Widget  evalGraphShell, gameListShell;
365 //XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
366 //XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
367
368 Font clockFontID, coordFontID, countFontID;
369 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
370 XtAppContext appContext;
371 char *layoutName;
372 char *oldICSInteractionTitle;
373
374 FileProc fileProc;
375 char *fileOpenMode;
376 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
377
378 Position commentX = -1, commentY = -1;
379 Dimension commentW, commentH;
380 typedef unsigned int BoardSize;
381 BoardSize boardSize;
382 Boolean chessProgram;
383
384 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
385 int squareSize, smallLayout = 0, tinyLayout = 0,
386   marginW, marginH, // [HGM] for run-time resizing
387   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
388   ICSInputBoxUp = False, askQuestionUp = False,
389   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
390   editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
391 Pixel timerForegroundPixel, timerBackgroundPixel;
392 Pixel buttonForegroundPixel, buttonBackgroundPixel;
393 char *chessDir, *programName, *programVersion,
394   *gameCopyFilename, *gamePasteFilename;
395 Boolean alwaysOnTop = False;
396 Boolean saveSettingsOnExit;
397 char *settingsFileName;
398 char *icsTextMenuString;
399 char *icsNames;
400 char *firstChessProgramNames;
401 char *secondChessProgramNames;
402
403 WindowPlacement wpMain;
404 WindowPlacement wpConsole;
405 WindowPlacement wpComment;
406 WindowPlacement wpMoveHistory;
407 WindowPlacement wpEvalGraph;
408 WindowPlacement wpEngineOutput;
409 WindowPlacement wpGameList;
410 WindowPlacement wpTags;
411
412 #define SOLID 0
413 #define OUTLINE 1
414 Pixmap pieceBitmap[2][(int)BlackPawn];
415 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
416 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
417 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
418 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
419 int useImages=0, useImageSqs;
420 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
421 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
422 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
423 XImage *ximLightSquare, *ximDarkSquare;
424 XImage *xim_Cross;
425
426 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
427 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
428
429 #define White(piece) ((int)(piece) < (int)BlackPawn)
430
431 /* Variables for doing smooth animation. This whole thing
432    would be much easier if the board was double-buffered,
433    but that would require a fairly major rewrite.       */
434
435 typedef struct {
436   Pixmap  saveBuf;
437   Pixmap        newBuf;
438   GC    blitGC, pieceGC, outlineGC;
439   XPoint        startSquare, prevFrame, mouseDelta;
440   int   startColor;
441   int   dragPiece;
442   Boolean       dragActive;
443   int     startBoardX, startBoardY;
444 } AnimState;
445
446 /* There can be two pieces being animated at once: a player
447    can begin dragging a piece before the remote opponent has moved. */
448
449 static AnimState game, player;
450
451 /* Bitmaps for use as masks when drawing XPM pieces.
452    Need one for each black and white piece.             */
453 static Pixmap xpmMask[BlackKing + 1];
454
455 /* This magic number is the number of intermediate frames used
456    in each half of the animation. For short moves it's reduced
457    by 1. The total number of frames will be factor * 2 + 1.  */
458 #define kFactor    4
459
460 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
461
462 Enables icsEnables[] = {
463   { "menuFile.Mail Move", False },
464   { "menuFile.Reload CMail Message", False },
465   { "menuMode.Machine Black", False },
466   { "menuMode.Machine White", False },
467   { "menuMode.Analysis Mode", False },
468   { "menuMode.Analyze File", False },
469   { "menuMode.Two Machines", False },
470 #ifndef ZIPPY
471   { "menuHelp.Hint", False },
472   { "menuHelp.Book", False },
473   { "menuStep.Move Now", False },
474   { "menuOptions.Periodic Updates", False },
475   { "menuOptions.Hide Thinking", False },
476   { "menuOptions.Ponder Next Move", False },
477 #endif
478   { NULL, False }
479 };
480
481 Enables ncpEnables[] = {
482   { "menuFile.Mail Move", False },
483   { "menuFile.Reload CMail Message", False },
484   { "menuMode.Machine White", False },
485   { "menuMode.Machine Black", False },
486   { "menuMode.Analysis Mode", False },
487   { "menuMode.Analyze File", False },
488   { "menuMode.Two Machines", False },
489   { "menuMode.ICS Client", False },
490   { "menuMode.ICS Input Box", False },
491   { "Action", False },
492   { "menuStep.Revert", False },
493   { "menuStep.Move Now", False },
494   { "menuStep.Retract Move", False },
495   { "menuOptions.Auto Comment", False },
496   { "menuOptions.Auto Flag", False },
497   { "menuOptions.Auto Flip View", False },
498   { "menuOptions.Auto Observe", False },
499   { "menuOptions.Auto Raise Board", False },
500   { "menuOptions.Get Move List", False },
501   { "menuOptions.ICS Alarm", False },
502   { "menuOptions.Move Sound", False },
503   { "menuOptions.Quiet Play", False },
504   { "menuOptions.Hide Thinking", False },
505   { "menuOptions.Periodic Updates", False },
506   { "menuOptions.Ponder Next Move", False },
507   { "menuHelp.Hint", False },
508   { "menuHelp.Book", False },
509   { NULL, False }
510 };
511
512 Enables gnuEnables[] = {
513   { "menuMode.ICS Client", False },
514   { "menuMode.ICS Input Box", False },
515   { "menuAction.Accept", False },
516   { "menuAction.Decline", False },
517   { "menuAction.Rematch", False },
518   { "menuAction.Adjourn", False },
519   { "menuAction.Stop Examining", False },
520   { "menuAction.Stop Observing", False },
521   { "menuStep.Revert", False },
522   { "menuOptions.Auto Comment", False },
523   { "menuOptions.Auto Observe", False },
524   { "menuOptions.Auto Raise Board", False },
525   { "menuOptions.Get Move List", False },
526   { "menuOptions.Premove", False },
527   { "menuOptions.Quiet Play", False },
528   
529   /* The next two options rely on SetCmailMode being called *after*    */
530   /* SetGNUMode so that when GNU is being used to give hints these     */
531   /* menu options are still available                                  */
532   
533   { "menuFile.Mail Move", False },
534   { "menuFile.Reload CMail Message", False },
535   { NULL, False }
536 };
537
538 Enables cmailEnables[] = {
539   { "Action", True },
540   { "menuAction.Call Flag", False },
541   { "menuAction.Draw", True },
542   { "menuAction.Adjourn", False },
543   { "menuAction.Abort", False },
544   { "menuAction.Stop Observing", False },
545   { "menuAction.Stop Examining", False },
546   { "menuFile.Mail Move", True },
547   { "menuFile.Reload CMail Message", True },
548   { NULL, False }
549 };
550
551 Enables trainingOnEnables[] = {
552   { "menuMode.Edit Comment", False },
553   { "menuMode.Pause", False },
554   { "menuStep.Forward", False },
555   { "menuStep.Backward", False },
556   { "menuStep.Forward to End", False },
557   { "menuStep.Back to Start", False },
558   { "menuStep.Move Now", False },
559   { "menuStep.Truncate Game", False },
560   { NULL, False }
561 };
562
563 Enables trainingOffEnables[] = {
564   { "menuMode.Edit Comment", True },
565   { "menuMode.Pause", True },
566   { "menuStep.Forward", True },
567   { "menuStep.Backward", True },
568   { "menuStep.Forward to End", True },
569   { "menuStep.Back to Start", True },
570   { "menuStep.Move Now", True },
571   { "menuStep.Truncate Game", True },
572   { NULL, False }
573 };
574
575 Enables machineThinkingEnables[] = {
576   { "menuFile.Load Game", False },
577   { "menuFile.Load Next Game", False },
578   { "menuFile.Load Previous Game", False },
579   { "menuFile.Reload Same Game", False },
580   { "menuFile.Paste Game", False },
581   { "menuFile.Load Position", False },
582   { "menuFile.Load Next Position", False },
583   { "menuFile.Load Previous Position", False },
584   { "menuFile.Reload Same Position", False },
585   { "menuFile.Paste Position", False },
586   { "menuMode.Machine White", False },
587   { "menuMode.Machine Black", False },
588   { "menuMode.Two Machines", False },
589   { "menuStep.Retract Move", False },
590   { NULL, False }
591 };
592
593 Enables userThinkingEnables[] = {
594   { "menuFile.Load Game", True },
595   { "menuFile.Load Next Game", True },
596   { "menuFile.Load Previous Game", True },
597   { "menuFile.Reload Same Game", True },
598   { "menuFile.Paste Game", True },
599   { "menuFile.Load Position", True },
600   { "menuFile.Load Next Position", True },
601   { "menuFile.Load Previous Position", True },
602   { "menuFile.Reload Same Position", True },
603   { "menuFile.Paste Position", True },
604   { "menuMode.Machine White", True },
605   { "menuMode.Machine Black", True },
606   { "menuMode.Two Machines", True },
607   { "menuStep.Retract Move", True },
608   { NULL, False }
609 };
610
611
612
613 MenuItem fileMenu[] = {
614   {N_("New Shuffle Game ..."), ShuffleMenuProc},
615   {N_("New Variant ..."), NewVariantProc},      // [HGM] variant: not functional yet
616   //    {"----", NothingProc},
617   //    {N_("Save Game"), SaveGameProc},
618   //    {"----", NothingProc},
619   {N_("Copy Game"), CopyGameProc},
620   {N_("Paste Game"), PasteGameProc},
621   //    {"----", NothingProc},
622   //    {N_("Load Position"), LoadPositionProc},
623   //    {N_("Load Next Position"), LoadNextPositionProc},
624   //    {N_("Load Previous Position"), LoadPrevPositionProc},
625   //    {N_("Reload Same Position"), ReloadPositionProc},
626   //    {N_("Save Position"), SavePositionProc},
627   //    {"----", NothingProc},
628   {N_("Copy Position"), CopyPositionProc},
629   {N_("Paste Position"), PastePositionProc},
630   //    {"----", NothingProc},
631   {N_("Mail Move"), MailMoveProc},
632   {N_("Reload CMail Message"), ReloadCmailMsgProc},
633   //    {"----", NothingProc},
634   {NULL, NULL}
635 };
636
637 MenuItem modeMenu[] = {
638   //    {N_("Machine White"), MachineWhiteProc},
639   //    {N_("Machine Black"), MachineBlackProc},
640   //    {N_("Two Machines"), TwoMachinesProc},
641   //    {N_("Analysis Mode"), AnalyzeModeProc},
642   //    {N_("Analyze File"), AnalyzeFileProc },
643   //    {N_("ICS Client"), IcsClientProc},
644   //    {N_("Edit Game"), EditGameProc},
645   //    {N_("Edit Position"), EditPositionProc},
646   //    {N_("Training"), TrainingProc},
647   //    {"----", NothingProc},
648   {N_("Show Engine Output"), EngineOutputProc},
649   {N_("Show Evaluation Graph"), EvalGraphProc},
650   {N_("Show Game List"), ShowGameListProc},
651   //    {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
652   //    {"----", NothingProc},
653   //    {N_("Edit Tags"), EditTagsProc},
654   {N_("Edit Comment"), EditCommentProc},
655   {N_("ICS Input Box"), IcsInputBoxProc},
656   {NULL, NULL}
657 };
658
659 MenuItem optionsMenu[] = {
660   //    {N_("Flip View"), FlipViewProc},
661   //    {"----", NothingProc},
662   {N_("Adjudications ..."), EngineMenuProc},
663   {N_("General Settings ..."), UciMenuProc},
664   {N_("Engine #1 Settings ..."), FirstSettingsProc},
665   {N_("Engine #2 Settings ..."), SecondSettingsProc},
666   {N_("Time Control ..."), TimeControlProc},
667   {N_("Game List ..."), GameListOptionsPopUp},
668   {"----", NothingProc},
669   //    {N_("Always Queen"), AlwaysQueenProc},
670   //    {N_("Animate Dragging"), AnimateDraggingProc},
671   //    {N_("Animate Moving"), AnimateMovingProc},
672   //    {N_("Auto Comment"), AutocommProc},
673   //    {N_("Auto Flag"), AutoflagProc},
674   //    {N_("Auto Flip View"), AutoflipProc},
675   //    {N_("Auto Observe"), AutobsProc},
676   //    {N_("Auto Raise Board"), AutoraiseProc},
677   //    {N_("Auto Save"), AutosaveProc},
678   //    {N_("Blindfold"), BlindfoldProc},
679   //    {N_("Flash Moves"), FlashMovesProc},
680   //    {N_("Get Move List"), GetMoveListProc},
681   //#if HIGHDRAG
682   //    {N_("Highlight Dragging"), HighlightDraggingProc},
683   //#endif
684   //    {N_("Highlight Last Move"), HighlightLastMoveProc},
685   //    {N_("Move Sound"), MoveSoundProc},
686   //    {N_("ICS Alarm"), IcsAlarmProc},
687   //    {N_("Old Save Style"), OldSaveStyleProc},
688   //    {N_("Periodic Updates"), PeriodicUpdatesProc},
689   //    {N_("Ponder Next Move"), PonderNextMoveProc},
690   //    {N_("Popup Exit Message"), PopupExitMessageProc},
691   //    {N_("Popup Move Errors"), PopupMoveErrorsProc},
692   //    {N_("Premove"), PremoveProc},
693   //    {N_("Quiet Play"), QuietPlayProc},
694   //    {N_("Hide Thinking"), HideThinkingProc},
695   //    {N_("Test Legality"), TestLegalityProc},
696   //    {N_("Show Coords"), ShowCoordsProc},
697   {"----", NothingProc},
698   {N_("Save Settings Now"), SaveSettingsProc},
699   {N_("Save Settings on Exit"), SaveOnExitProc},
700   {NULL, NULL}
701 };
702
703 Menu menuBar[] = {
704   {N_("File"), fileMenu},
705   {N_("Mode"), modeMenu},
706   {N_("Options"), optionsMenu},
707   {NULL, NULL}
708 };
709
710 #define PIECE_MENU_SIZE 18
711 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
712   { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
713     N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
714     N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
715     N_("Empty square"), N_("Clear board") },
716   { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
717     N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
718     N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
719     N_("Empty square"), N_("Clear board") }
720 };
721 /* must be in same order as PieceMenuStrings! */
722 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
723   { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
724     WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
725     WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
726     PromotePiece, DemotePiece, EmptySquare, ClearBoard },
727   { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
728     BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
729     BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
730     PromotePiece, DemotePiece, EmptySquare, ClearBoard },
731 };
732
733 #define DROP_MENU_SIZE 6
734 String dropMenuStrings[DROP_MENU_SIZE] = {
735   "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"), N_("Queen")
736 };
737 /* must be in same order as PieceMenuStrings! */
738 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
739   (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
740   WhiteRook, WhiteQueen
741 };
742
743 typedef struct {
744   char piece;
745   char* widget;
746 } DropMenuEnables;
747
748 DropMenuEnables dmEnables[] = {
749   { 'P', "Pawn" },
750   { 'N', "Knight" },
751   { 'B', "Bishop" },
752   { 'R', "Rook" },
753   { 'Q', "Queen" }
754 };
755
756 Arg layoutArgs[] = {
757   { XtNborderWidth, 0 },
758   { XtNdefaultDistance, 0 },
759 };
760
761 Arg formArgs[] = {
762   { XtNborderWidth, 0 },
763   { XtNresizable, (XtArgVal) True },
764 };
765
766 Arg boardArgs[] = {
767   { XtNborderWidth, 0 },
768   { XtNwidth, 0 },
769   { XtNheight, 0 }
770 };
771
772 XtResource clientResources[] = {
773   { "flashCount", "flashCount", XtRInt, sizeof(int),
774     XtOffset(AppDataPtr, flashCount), XtRImmediate,
775     (XtPointer) FLASH_COUNT  },
776 };
777
778 XtActionsRec boardActions[] = {
779   //    { "HandleUserMove", HandleUserMove },
780   { "AnimateUserMove", AnimateUserMove },
781   //    { "FileNameAction", FileNameAction },
782   { "HandlePV", HandlePV },
783   { "UnLoadPV", UnLoadPV },
784   { "AskQuestionProc", AskQuestionProc },
785   { "AskQuestionReplyAction", AskQuestionReplyAction },
786   { "PieceMenuPopup", PieceMenuPopup },
787   //    { "WhiteClock", WhiteClock },
788   //    { "BlackClock", BlackClock },
789   { "Iconify", Iconify },
790   { "LoadSelectedProc", LoadSelectedProc },
791   //    { "LoadPositionProc", LoadPositionProc },
792   //    { "LoadNextPositionProc", LoadNextPositionProc },
793   //    { "LoadPrevPositionProc", LoadPrevPositionProc },
794   //    { "ReloadPositionProc", ReloadPositionProc },
795   { "SetFilterProc", SetFilterProc },
796   { "CopyPositionProc", CopyPositionProc },
797   { "PastePositionProc", PastePositionProc },
798   { "CopyGameProc", CopyGameProc },
799   { "PasteGameProc", PasteGameProc },
800   //    { "SaveGameProc", SaveGameProc },
801   //    { "SavePositionProc", SavePositionProc },
802   { "MailMoveProc", MailMoveProc },
803   { "ReloadCmailMsgProc", ReloadCmailMsgProc },
804   //    { "MachineWhiteProc", MachineWhiteProc },
805   //    { "MachineBlackProc", MachineBlackProc },
806   //    { "AnalysisModeProc", AnalyzeModeProc },
807   //    { "AnalyzeFileProc", AnalyzeFileProc },
808   //    { "TwoMachinesProc", TwoMachinesProc },
809   //    { "IcsClientProc", IcsClientProc },
810   //    { "EditGameProc", EditGameProc },
811   //    { "EditPositionProc", EditPositionProc },
812   //    { "TrainingProc", EditPositionProc },
813   { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
814   { "EvalGraphProc", EvalGraphProc},       // [HGM] Winboard_x avaluation graph window
815   { "ShowGameListProc", ShowGameListProc },
816   //    { "ShowMoveListProc", HistoryShowProc},
817   //    { "EditTagsProc", EditCommentProc },
818   { "EditCommentProc", EditCommentProc },
819   //    { "IcsAlarmProc", IcsAlarmProc },
820   { "IcsInputBoxProc", IcsInputBoxProc },
821   //    { "AcceptProc", AcceptProc },
822   //    { "DeclineProc", DeclineProc },
823   //    { "RematchProc", RematchProc },
824   //    { "CallFlagProc", CallFlagProc },
825   //    { "DrawProc", DrawProc },
826   //    { "AdjournProc", AdjournProc },
827   //    { "AbortProc", AbortProc },
828   //    { "ResignProc", ResignProc },
829   //    { "AdjuWhiteProc", AdjuWhiteProc },
830   //    { "AdjuBlackProc", AdjuBlackProc },
831   //    { "AdjuDrawProc", AdjuDrawProc },
832   { "EnterKeyProc", EnterKeyProc },
833   //    { "StopObservingProc", StopObservingProc },
834   //    { "StopExaminingProc", StopExaminingProc },
835   //    { "BackwardProc", BackwardProc },
836   //    { "ForwardProc", ForwardProc },
837   //    { "ToStartProc", ToStartProc },
838   //    { "ToEndProc", ToEndProc },
839   //    { "RevertProc", RevertProc },
840   //    { "TruncateGameProc", TruncateGameProc },
841   //    { "MoveNowProc", MoveNowProc },
842   //    { "RetractMoveProc", RetractMoveProc },
843   //    { "AlwaysQueenProc", AlwaysQueenProc },
844   //    { "AnimateDraggingProc", AnimateDraggingProc },
845   //    { "AnimateMovingProc", AnimateMovingProc },
846   //    { "AutoflagProc", AutoflagProc },
847   //    { "AutoflipProc", AutoflipProc },
848   //    { "AutobsProc", AutobsProc },
849   //    { "AutoraiseProc", AutoraiseProc },
850   //    { "AutosaveProc", AutosaveProc },
851   //    { "BlindfoldProc", BlindfoldProc },
852   //    { "FlashMovesProc", FlashMovesProc },
853   //    { "FlipViewProc", FlipViewProc },
854   //    { "GetMoveListProc", GetMoveListProc },
855 #if HIGHDRAG
856   //    { "HighlightDraggingProc", HighlightDraggingProc },
857 #endif
858   //    { "HighlightLastMoveProc", HighlightLastMoveProc },
859   //    { "IcsAlarmProc", IcsAlarmProc },
860   //    { "MoveSoundProc", MoveSoundProc },
861   //    { "OldSaveStyleProc", OldSaveStyleProc },
862   //    { "PeriodicUpdatesProc", PeriodicUpdatesProc },
863   //    { "PonderNextMoveProc", PonderNextMoveProc },
864   //    { "PopupExitMessageProc", PopupExitMessageProc },
865   //    { "PopupMoveErrorsProc", PopupMoveErrorsProc },
866   //    { "PremoveProc", PremoveProc },
867   //    { "QuietPlayProc", QuietPlayProc },
868   //    { "ShowThinkingProc", ShowThinkingProc },
869   //    { "HideThinkingProc", HideThinkingProc },
870   //    { "TestLegalityProc", TestLegalityProc },
871   { "SaveSettingsProc", SaveSettingsProc },
872   { "SaveOnExitProc", SaveOnExitProc },
873   //    { "InfoProc", InfoProc },
874   //    { "ManProc", ManProc },
875   //    { "HintProc", HintProc },
876   //    { "BookProc", BookProc },
877   { "AboutGameProc", AboutGameProc },
878   { "DebugProc", DebugProc },
879   { "NothingProc", NothingProc },
880   { "CommentPopDown", (XtActionProc) CommentPopDown },
881   { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
882   { "TagsPopDown", (XtActionProc) TagsPopDown },
883   { "ErrorPopDown", (XtActionProc) ErrorPopDown },
884   { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
885   //    { "FileNamePopDown", (XtActionProc) FileNamePopDown },
886   { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
887   { "GameListPopDown", (XtActionProc) GameListPopDown },
888   { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
889   { "PromotionPopDown", (XtActionProc) PromotionPopDown },
890   //    { "HistoryPopDown", (XtActionProc) HistoryPopDown },
891   { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
892   { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
893   { "ShufflePopDown", (XtActionProc) ShufflePopDown },
894   { "EnginePopDown", (XtActionProc) EnginePopDown },
895   { "UciPopDown", (XtActionProc) UciPopDown },
896   { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
897   { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
898   { "SettingsPopDown", (XtActionProc) SettingsPopDown },
899   { "CopyMemoProc", (XtActionProc) CopyMemoProc },
900 };
901
902 //char globalTranslations[] =
903 //  ":<Key>R: ResignProc() \n                   \
904 //   :<Key>r: ResetProc() \n                    \
905 //   :<Key>g: LoadGameProc() \n                 \
906 //   :<Key>N: LoadNextGameProc() \n             \
907 //   :<Key>P: LoadPrevGameProc() \n             \
908 //   :<Key>Q: QuitProc() \n                     \
909 //   :<Key>F: ToEndProc() \n                    \
910 //   :<Key>f: ForwardProc() \n                  \
911 //   :<Key>B: ToStartProc() \n                  \
912 //   :<Key>b: BackwardProc() \n                 \
913 //   :<Key>p: PauseProc() \n                    \
914 //   :<Key>d: DrawProc() \n                     \
915 //   :<Key>t: CallFlagProc() \n                 \
916 //   :<Key>i: Iconify() \n                      \
917 //   :<Key>c: Iconify() \n                      \
918 //   :<Key>v: FlipViewProc() \n                 \
919 //   <KeyDown>Control_L: BackwardProc() \n      \
920 //   <KeyUp>Control_L: ForwardProc() \n         \
921 //   <KeyDown>Control_R: BackwardProc() \n      \
922 //   <KeyUp>Control_R: ForwardProc() \n                 \
923 //   Shift<Key>1: AskQuestionProc(\"Direct command\",               \
924 //                                \"Send to chess program:\",,1) \n \
925 //   Shift<Key>2: AskQuestionProc(\"Direct command\",                   \
926 //                                \"Send to second chess program:\",,2) \n";
927 //
928 //char boardTranslations[] =
929 //   "<Btn1Down>: HandleUserMove() \n           \
930 //   <Btn1Up>: HandleUserMove() \n              \
931 //   <Btn1Motion>: AnimateUserMove() \n         \
932 //   <Btn3Motion>: HandlePV() \n                \
933 //   <Btn3Up>: UnLoadPV() \n                                            \
934 //   Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
935 //                 PieceMenuPopup(menuB) \n                             \
936 //   Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
937 //                 PieceMenuPopup(menuW) \n                             \
938 //   Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
939 //                 PieceMenuPopup(menuW) \n                             \
940 //   Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
941 //                 PieceMenuPopup(menuB) \n";
942 //
943 //char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
944 //char blackTranslations[] = "<BtnDown>: BlackClock()\n";
945
946 char ICSInputTranslations[] =
947   "<Key>Return: EnterKeyProc() \n";
948
949 String xboardResources[] = {
950   //    "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
951   "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
952   "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
953   NULL
954 };
955
956 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
957                            "magenta", "cyan", "white" };
958 typedef struct {
959   int attr, bg, fg;
960 } TextColors;
961 TextColors textColors[(int)NColorClasses];
962
963 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
964 static int
965 parse_color(str, which)
966      char *str;
967      int which;
968 {
969   char *p, buf[100], *d;
970   int i;
971   
972   if (strlen(str) > 99) /* watch bounds on buf */
973     return -1;
974   
975   p = str;
976   d = buf;
977   for (i=0; i<which; ++i) {
978     p = strchr(p, ',');
979     if (!p)
980       return -1;
981     ++p;
982   }
983   
984   /* Could be looking at something like:
985      black, , 1
986      .. in which case we want to stop on a comma also */
987   while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
988     ++p;
989   
990   if (*p == ',') {
991     return -1;          /* Use default for empty field */
992   }
993   
994   if (which == 2 || isdigit(*p))
995     return atoi(p);
996   
997   while (*p && isalpha(*p))
998     *(d++) = *(p++);
999   
1000   *d = 0;
1001   
1002   for (i=0; i<8; ++i) {
1003     if (!StrCaseCmp(buf, cnames[i]))
1004       return which? (i+40) : (i+30);
1005   }
1006   if (!StrCaseCmp(buf, "default")) return -1;
1007   
1008   fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1009   return -2;
1010 }
1011
1012 static int
1013 parse_cpair(cc, str)
1014      ColorClass cc;
1015      char *str;
1016 {
1017   if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1018     fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1019             programName, str);
1020     return -1;
1021   }
1022   
1023   /* bg and attr are optional */
1024   textColors[(int)cc].bg = parse_color(str, 1);
1025   if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1026     textColors[(int)cc].attr = 0;
1027   }
1028   return 0;
1029 }
1030
1031
1032 void
1033 BoardToTop()
1034 {
1035   /* this should raise the board to the top */
1036   gtk_window_present(GTK_WINDOW(GUI_Window));
1037   return;
1038 }
1039
1040 //---------------------------------------------------------------------------------------------------------
1041 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1042 #define XBOARD True
1043 #define JAWS_ARGS
1044 #define CW_USEDEFAULT (1<<31)
1045 #define ICS_TEXT_MENU_SIZE 90
1046 #define DEBUG_FILE "xboard.debug"
1047 #define SetCurrentDirectory chdir
1048 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1049 #define OPTCHAR "-"
1050 #define SEPCHAR " "
1051
1052 // these two must some day move to frontend.h, when they are implemented
1053 Boolean GameListIsUp();
1054
1055 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1056 #include "args.h"
1057
1058 // front-end part of option handling
1059
1060 // [HGM] This platform-dependent table provides the location for storing the color info
1061 extern char *crWhite, * crBlack;
1062
1063 void *
1064 colorVariable[] = {
1065   &appData.whitePieceColor, 
1066   &appData.blackPieceColor, 
1067   &appData.lightSquareColor,
1068   &appData.darkSquareColor, 
1069   &appData.highlightSquareColor,
1070   &appData.premoveHighlightColor,
1071   &appData.lowTimeWarningColor,
1072   NULL,
1073   NULL,
1074   NULL,
1075   NULL,
1076   NULL,
1077   &crWhite,
1078   &crBlack,
1079   NULL
1080 };
1081
1082 // [HGM] font: keep a font for each square size, even non-stndard ones
1083 #define NUM_SIZES 18
1084 #define MAX_SIZE 130
1085 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1086 char *fontTable[NUM_FONTS][MAX_SIZE];
1087
1088 void
1089 ParseFont(char *name, int number)
1090 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1091   int size;
1092   if(sscanf(name, "size%d:", &size)) {
1093     // [HGM] font: font is meant for specific boardSize (likely from settings file);
1094     //       defer processing it until we know if it matches our board size
1095     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1096       fontTable[number][size] = strdup(strchr(name, ':')+1);
1097       fontValid[number][size] = True;
1098     }
1099     return;
1100   }
1101   switch(number) {
1102   case 0: // CLOCK_FONT
1103     appData.clockFont = strdup(name);
1104     break;
1105   case 1: // MESSAGE_FONT
1106     appData.font = strdup(name);
1107     break;
1108   case 2: // COORD_FONT
1109     appData.coordFont = strdup(name);
1110     break;
1111   default:
1112     return;
1113   }
1114   fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1115 }
1116
1117 void
1118 SetFontDefaults()
1119 { // only 2 fonts currently
1120   appData.clockFont = CLOCK_FONT_NAME;
1121   appData.coordFont = COORD_FONT_NAME;
1122   appData.font  =   DEFAULT_FONT_NAME;
1123 }
1124
1125 void
1126 CreateFonts()
1127 { // no-op, until we identify the code for this already in XBoard and move it here
1128 }
1129
1130 void
1131 ParseColor(int n, char *name)
1132 { // in XBoard, just copy the color-name string
1133   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1134 }
1135
1136 void
1137 ParseTextAttribs(ColorClass cc, char *s)
1138 {   
1139   (&appData.colorShout)[cc] = strdup(s);
1140 }
1141
1142 void
1143 ParseBoardSize(void *addr, char *name)
1144 {
1145   appData.boardSize = strdup(name);
1146 }
1147
1148 void
1149 LoadAllSounds()
1150 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1151 }
1152
1153 void
1154 SetCommPortDefaults()
1155 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1156 }
1157
1158 // [HGM] args: these three cases taken out to stay in front-end
1159 void
1160 SaveFontArg(FILE *f, ArgDescriptor *ad)
1161 {
1162   char *name, buf[MSG_SIZ];
1163   int i, n = (int)ad->argLoc;
1164   switch(n) {
1165   case 0: // CLOCK_FONT
1166     name = appData.clockFont;
1167     break;
1168   case 1: // MESSAGE_FONT
1169     name = appData.font;
1170     break;
1171   case 2: // COORD_FONT
1172     name = appData.coordFont;
1173     break;
1174   default:
1175     return;
1176   }
1177   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1178     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1179       fontTable[n][squareSize] = strdup(name);
1180       fontValid[n][squareSize] = True;
1181       break;
1182     }
1183   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1184                               fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]); 
1185 }
1186
1187 void
1188 ExportSounds()
1189 { // nothing to do, as the sounds are at all times represented by their text-string names already
1190 }
1191
1192 void
1193 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1194 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1195   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1196 }
1197
1198 void
1199 SaveColor(FILE *f, ArgDescriptor *ad)
1200 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1201   if(colorVariable[(int)ad->argLoc])
1202     fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1203 }
1204
1205 void
1206 SaveBoardSize(FILE *f, char *name, void *addr)
1207 { // wrapper to shield back-end from BoardSize & sizeInfo
1208   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1209 }
1210
1211 void
1212 ParseCommPortSettings(char *s)
1213 { // no such option in XBoard (yet)
1214 }
1215
1216 extern Widget engineOutputShell;
1217 extern Widget tagsShell, editTagsShell;
1218
1219 void
1220 GetActualPlacement(Widget wg, WindowPlacement *wp)
1221 {
1222   Arg args[16];
1223   Dimension w, h;
1224   Position x, y;
1225   int i;
1226   
1227   if(!wg) return;
1228   
1229   i = 0;
1230   XtSetArg(args[i], XtNx, &x); i++;
1231   XtSetArg(args[i], XtNy, &y); i++;
1232   XtSetArg(args[i], XtNwidth, &w); i++;
1233   XtSetArg(args[i], XtNheight, &h); i++;
1234   XtGetValues(wg, args, i);
1235   wp->x = x - 4;
1236   wp->y = y - 23;
1237   wp->height = h;
1238   wp->width = w;
1239 }
1240
1241 void
1242 GetWindowCoords()
1243 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1244   // In XBoard this will have to wait until awareness of window parameters is implemented
1245   
1246   //  GetActualPlacement(shellWidget, &wpMain);
1247   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1248     //  if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1249     if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1250   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1251   if(commentShell) GetActualPlacement(commentShell, &wpComment);
1252   else             GetActualPlacement(editShell,    &wpComment);
1253   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1254   else      GetActualPlacement(editTagsShell, &wpTags);
1255 }
1256
1257 void
1258 PrintCommPortSettings(FILE *f, char *name)
1259 { // This option does not exist in XBoard
1260 }
1261
1262 int
1263 MySearchPath(char *installDir, char *name, char *fullname)
1264 { // just append installDir and name. Perhaps ExpandPath should be used here?
1265   name = ExpandPathName(name);
1266   if(name && name[0] == '/') strcpy(fullname, name); else {
1267     sprintf(fullname, "%s%c%s", installDir, '/', name);
1268   }
1269   return 1;
1270 }
1271
1272 int
1273 MyGetFullPathName(char *name, char *fullname)
1274 { // should use ExpandPath?
1275   name = ExpandPathName(name);
1276   strcpy(fullname, name);
1277   return 1;
1278 }
1279
1280 void
1281 EnsureOnScreen(int *x, int *y, int minX, int minY)
1282 {
1283   return;
1284 }
1285
1286 int
1287 MainWindowUp()
1288 { // [HGM] args: allows testing if main window is realized from back-end
1289   return xBoardWindow != 0;
1290 }
1291
1292 void
1293 PopUpStartupDialog()
1294 {  // start menu not implemented in XBoard
1295 }
1296 char *
1297 ConvertToLine(int argc, char **argv)
1298 {
1299   static char line[128*1024], buf[1024];
1300   int i;
1301   
1302   line[0] = NULLCHAR;
1303   for(i=1; i<argc; i++) {
1304     if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1305         && argv[i][0] != '{' )
1306       sprintf(buf, "{%s} ", argv[i]);
1307     else sprintf(buf, "%s ", argv[i]);
1308     strcat(line, buf);
1309   }
1310   line[strlen(line)-1] = NULLCHAR;
1311   return line;
1312 }
1313
1314 //--------------------------------------------------------------------------------------------
1315
1316 #ifdef IDSIZES
1317 // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1318 #else
1319
1320 #define BoardSize int
1321 void InitDrawingSizes(BoardSize boardSize, int flags)
1322 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1323   Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1324   Arg args[16];
1325   XtGeometryResult gres;
1326   int i;
1327   
1328   boardWidth  = lineGap + BOARD_WIDTH  * (squareSize + lineGap);
1329   boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1330   
1331   timerWidth = (boardWidth - sep) / 2;
1332   
1333   if (appData.titleInWindow)
1334     {
1335       i = 0;
1336       if (smallLayout)
1337         {
1338           w = boardWidth - 2*bor;
1339         }
1340       else
1341         {
1342           w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1343         }
1344     }
1345   
1346   if(!formWidget) return;
1347   
1348   /*
1349    * Inhibit shell resizing.
1350    */
1351   
1352   // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1353   // (only for xpm)
1354   if(useImages) {
1355     for(i=0; i<4; i++) {
1356       int p;
1357       for(p=0; p<=(int)WhiteKing; p++)
1358         xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1359       if(gameInfo.variant == VariantShogi) {
1360         xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1361         xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1362         xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1363         xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1364         xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1365       }
1366 #ifdef GOTHIC
1367       if(gameInfo.variant == VariantGothic) {
1368         xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1369       }
1370 #endif
1371 #if !HAVE_LIBXPM
1372       // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1373       for(p=0; p<=(int)WhiteKing; p++)
1374         ximMaskPm[p] = ximMaskPm2[p]; // defaults
1375       if(gameInfo.variant == VariantShogi) {
1376         ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1377         ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1378         ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1379         ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1380         ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1381       }
1382 #ifdef GOTHIC
1383       if(gameInfo.variant == VariantGothic) {
1384         ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1385       }
1386 #endif
1387 #endif
1388     }
1389   } else {
1390     for(i=0; i<2; i++) {
1391       int p;
1392       for(p=0; p<=(int)WhiteKing; p++)
1393         pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1394       if(gameInfo.variant == VariantShogi) {
1395         pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1396         pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1397         pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1398         pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1399         pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1400       }
1401 #ifdef GOTHIC
1402       if(gameInfo.variant == VariantGothic) {
1403         pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1404       }
1405 #endif
1406     }
1407   }
1408 #if HAVE_LIBXPM
1409   CreateAnimVars();
1410 #endif
1411 }
1412 #endif
1413
1414 void EscapeExpand(char *p, char *q)
1415 {       // [HGM] initstring: routine to shape up string arguments
1416   while(*p++ = *q++) 
1417     if(p[-1] == '\\')
1418       switch(*q++) {
1419       case 'n': p[-1] = '\n'; break;
1420       case 'r': p[-1] = '\r'; break;
1421       case 't': p[-1] = '\t'; break;
1422       case '\\': p[-1] = '\\'; break;
1423       case 0: *p = 0; return;
1424       default: p[-1] = q[-1]; break;
1425       }
1426 }
1427
1428 int
1429 main(argc, argv)
1430      int argc;
1431      char **argv;
1432 {
1433   int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1434   XSetWindowAttributes window_attributes;
1435   Arg args[16];
1436   Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1437   XrmValue vFrom, vTo;
1438   XtGeometryResult gres;
1439   char *p;
1440   XrmDatabase xdb;
1441   int forceMono = False;
1442   
1443   srandom(time(0)); // [HGM] book: make random truly random
1444   
1445   setbuf(stdout, NULL);
1446   setbuf(stderr, NULL);
1447   debugFP = stderr;
1448   
1449   if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1450     printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1451     exit(0);
1452   }
1453   
1454   programName = strrchr(argv[0], '/');
1455   if (programName == NULL)
1456     programName = argv[0];
1457   else
1458     programName++;
1459   
1460 #ifdef ENABLE_NLS
1461   XtSetLanguageProc(NULL, NULL, NULL);
1462   bindtextdomain(PACKAGE, LOCALEDIR);
1463   textdomain(PACKAGE);
1464 #endif
1465   
1466   /* set up GTK */
1467   gtk_init (&argc, &argv);
1468   
1469   /* parse glade file to build widgets */
1470   
1471   builder = gtk_builder_new ();
1472   GError *gtkerror=NULL;
1473   if(!gtk_builder_add_from_file (builder, "gtk-interface.xml", &gtkerror))
1474     {
1475       if(gtkerror)
1476         printf ("Error: %d %s\n",gtkerror->code,gtkerror->message);
1477     }
1478   
1479   /* test if everything worked ok */
1480   
1481   GUI_Window = GTK_WIDGET (gtk_builder_get_object (builder, "MainWindow"));
1482   if(!GUI_Window) printf("Error: gtk_builder didn't work (MainWindow)!\n");
1483   
1484   GUI_Aspect = GTK_WIDGET (gtk_builder_get_object (builder, "Aspectframe"));
1485   if(!GUI_Aspect) printf("Error: gtk_builder didn't work (Aspectframe)!\n");
1486   
1487   GUI_Menubar  = GTK_WIDGET (gtk_builder_get_object (builder, "MenuBar"));
1488   if(!GUI_Menubar) printf("Error: gtk_builder didn't work (MenuBar)!\n");
1489   GUI_Timer  = GTK_WIDGET (gtk_builder_get_object (builder, "Timer"));
1490   if(!GUI_Timer) printf("Error: gtk_builder didn't work (Timer)!\n");
1491   GUI_Buttonbar  = GTK_WIDGET (gtk_builder_get_object (builder, "ButtonBar"));
1492   if(!GUI_Buttonbar) printf("Error: gtk_builder didn't work (ButtonBar)!\n");
1493   GUI_Board  = GTK_WIDGET (gtk_builder_get_object (builder, "Board"));
1494   if(!GUI_Board) printf("Error: gtk_builder didn't work (Board)!\n");
1495   
1496   GUI_Whiteclock  = GTK_WIDGET (gtk_builder_get_object (builder, "WhiteClock"));
1497   if(!GUI_Whiteclock) printf("Error: gtk_builder didn't work (WhiteClock)!\n");
1498   
1499   GUI_Blackclock  = GTK_WIDGET (gtk_builder_get_object (builder, "BlackClock"));
1500   if(!GUI_Blackclock) printf("Error: gtk_builder didn't work (BlackClock)!\n");
1501   
1502   /* GTK lists stores*/
1503   LIST_MoveHistory = GTK_LIST_STORE (gtk_builder_get_object (builder, "MoveHistoryStore"));
1504   if(!LIST_MoveHistory) printf("Error: gtk_builder didn't work (MoveHistoryStore)!\n");
1505   
1506   LIST_GameList = GTK_LIST_STORE (gtk_builder_get_object (builder, "GameListStore"));
1507   if(!LIST_GameList) printf("Error: gtk_builder didn't work (GameListStore)!\n");
1508   
1509   /* EditTags window */
1510   GUI_EditTags = GTK_WIDGET (gtk_builder_get_object (builder, "EditTags"));
1511   if(!GUI_EditTags) printf("Error: gtk_builder didn't work (EditTags)!\n");
1512   
1513   GUI_EditTagsTextArea = GTK_WIDGET (gtk_builder_get_object (builder, "EditTagsTextArea"));
1514   if(!GUI_EditTagsTextArea) printf("Error: gtk_builder didn't work(EditTagsTextArea)!\n");
1515   
1516   /* move history and game list windows */
1517   GUI_History = GTK_WIDGET (gtk_builder_get_object (builder, "MoveHistory"));
1518   if(!GUI_History) printf("Error: gtk_builder didn't work (MoveHistory)!\n");
1519   
1520   TREE_History = GTK_TREE_VIEW (gtk_builder_get_object (builder, "MoveHistoryView"));
1521   if(!TREE_History) printf("Error: gtk_builder didn't work (MoveHistoryView)!\n");
1522   
1523   GUI_GameList = GTK_WIDGET (gtk_builder_get_object (builder, "GameList"));
1524   if(!GUI_GameList) printf("Error: gtk_builder didn't work (GameList)!\n");
1525   
1526   TREE_Game = GTK_TREE_VIEW (gtk_builder_get_object (builder, "GameListView"));
1527   if(!TREE_Game) printf("Error: gtk_builder didn't work (GameListView)!\n");
1528   
1529   
1530   /* connect lists to views */
1531   gtk_tree_view_set_model(TREE_History, GTK_TREE_MODEL(LIST_MoveHistory));
1532   gtk_tree_view_set_model(TREE_Game,    GTK_TREE_MODEL(LIST_GameList));
1533   
1534   gtk_builder_connect_signals (builder, NULL);
1535   
1536   // don't unref the builder, since we use it to get references to widgets
1537   //    g_object_unref (G_OBJECT (builder));
1538   
1539   /* end parse glade file */
1540   
1541   appData.boardSize = "";
1542   InitAppData(ConvertToLine(argc, argv));
1543   
1544   p = getenv("HOME");
1545   if (p == NULL) p = "/tmp";
1546   i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1547   gameCopyFilename = (char*) malloc(i);
1548   gamePasteFilename = (char*) malloc(i);
1549   snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1550   snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1551   
1552   //    XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1553   //                          clientResources, XtNumber(clientResources),
1554   //                          NULL, 0);
1555   
1556   { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1557     static char buf[MSG_SIZ];
1558     EscapeExpand(buf, appData.initString);
1559     appData.initString = strdup(buf);
1560     EscapeExpand(buf, appData.secondInitString);
1561     appData.secondInitString = strdup(buf);
1562     EscapeExpand(buf, appData.firstComputerString);
1563     appData.firstComputerString = strdup(buf);
1564     EscapeExpand(buf, appData.secondComputerString);
1565     appData.secondComputerString = strdup(buf);
1566   }
1567   
1568   if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1569     chessDir = ".";
1570   } else {
1571     if (chdir(chessDir) != 0) {
1572       fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1573       perror(chessDir);
1574       exit(1);
1575     }
1576   }
1577   
1578   if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1579     /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1580     if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1581       printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1582       exit(errno);
1583     }
1584     setbuf(debugFP, NULL);
1585   }
1586   
1587   
1588 #if !HIGHDRAG
1589   /* This feature does not work; animation needs a rewrite */
1590   appData.highlightDragging = FALSE;
1591 #endif
1592   InitBackEnd1();
1593   
1594   gameInfo.variant = StringToVariant(appData.variant);
1595   InitPosition(FALSE);
1596   
1597   
1598   squareSize            = 40;
1599   lineGap               = 1;
1600   clockFontPxlSize      = 20;
1601   coordFontPxlSize      = 20;
1602   fontPxlSize           = 20;
1603   smallLayout           = 16;
1604   tinyLayout            = 10;
1605   
1606   
1607   boardWidth  = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1608   boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1609   
1610   /*
1611    * Determine what fonts to use.
1612    */
1613   //    appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1614   //    clockFontID = XLoadFont(xDisplay, appData.clockFont);
1615   //    clockFontStruct = XQueryFont(xDisplay, clockFontID);
1616   //    appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1617   //    coordFontID = XLoadFont(xDisplay, appData.coordFont);
1618   //    coordFontStruct = XQueryFont(xDisplay, coordFontID);
1619   //    appData.font = FindFont(appData.font, fontPxlSize);
1620   //    countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
1621   //    countFontStruct = XQueryFont(xDisplay, countFontID);
1622   //    appData.font = FindFont(appData.font, fontPxlSize);
1623   
1624   //    xdb = XtDatabase(xDisplay);
1625   //    XrmPutStringResource(&xdb, "*font", appData.font);
1626   
1627   /*
1628    * Detect if there are not enough colors available and adapt.
1629    */
1630   //    if (DefaultDepth(xDisplay, xScreen) <= 2) {
1631   //      appData.monoMode = True;
1632   //    }
1633   
1634   if (!appData.monoMode) {
1635     vFrom.addr = (caddr_t) appData.lightSquareColor;
1636     vFrom.size = strlen(appData.lightSquareColor);
1637     //  XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1638     if (vTo.addr == NULL) {
1639       appData.monoMode = True;
1640       forceMono = True;
1641     } else {
1642       lightSquareColor = *(Pixel *) vTo.addr;
1643     }
1644   }
1645   if (!appData.monoMode) {
1646     vFrom.addr = (caddr_t) appData.darkSquareColor;
1647     vFrom.size = strlen(appData.darkSquareColor);
1648     //  XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1649     if (vTo.addr == NULL) {
1650       appData.monoMode = True;
1651       forceMono = True;
1652     } else {
1653       darkSquareColor = *(Pixel *) vTo.addr;
1654     }
1655   }
1656   if (!appData.monoMode) {
1657     vFrom.addr = (caddr_t) appData.whitePieceColor;
1658     vFrom.size = strlen(appData.whitePieceColor);
1659     //  XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1660     if (vTo.addr == NULL) {
1661       appData.monoMode = True;
1662       forceMono = True;
1663     } else {
1664       whitePieceColor = *(Pixel *) vTo.addr;
1665     }
1666   }
1667   if (!appData.monoMode) {
1668     vFrom.addr = (caddr_t) appData.blackPieceColor;
1669     vFrom.size = strlen(appData.blackPieceColor);
1670     //  XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1671     if (vTo.addr == NULL) {
1672       appData.monoMode = True;
1673       forceMono = True;
1674     } else {
1675       blackPieceColor = *(Pixel *) vTo.addr;
1676     }
1677   }
1678   
1679   if (!appData.monoMode) {
1680     vFrom.addr = (caddr_t) appData.highlightSquareColor;
1681     vFrom.size = strlen(appData.highlightSquareColor);
1682     //  XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1683     if (vTo.addr == NULL) {
1684       appData.monoMode = True;
1685       forceMono = True;
1686     } else {
1687       highlightSquareColor = *(Pixel *) vTo.addr;
1688     }
1689   }
1690   
1691   if (!appData.monoMode) {
1692     vFrom.addr = (caddr_t) appData.premoveHighlightColor;
1693     vFrom.size = strlen(appData.premoveHighlightColor);
1694     //  XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1695     if (vTo.addr == NULL) {
1696       appData.monoMode = True;
1697       forceMono = True;
1698     } else {
1699       premoveHighlightColor = *(Pixel *) vTo.addr;
1700     }
1701   }
1702   
1703   if (forceMono) {
1704     fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
1705             programName);
1706     
1707     if (appData.bitmapDirectory == NULL ||
1708         appData.bitmapDirectory[0] == NULLCHAR)
1709       appData.bitmapDirectory = DEF_BITMAP_DIR;
1710   }
1711   
1712   if (appData.lowTimeWarning && !appData.monoMode) {
1713     vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
1714     vFrom.size = strlen(appData.lowTimeWarningColor);
1715     //      XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
1716     if (vTo.addr == NULL)
1717       appData.monoMode = True;
1718     else
1719       lowTimeWarningColor = *(Pixel *) vTo.addr;
1720   }
1721   
1722   if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
1723       parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
1724       parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
1725       parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
1726       parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
1727       parse_cpair(ColorTell, appData.colorTell) < 0 ||
1728       parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
1729       parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
1730       parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
1731       parse_cpair(ColorNormal, appData.colorNormal) < 0)
1732     {
1733       if (appData.colorize) {
1734         fprintf(stderr,
1735                 _("%s: can't parse color names; disabling colorization\n"),
1736                 programName);
1737       }
1738       appData.colorize = FALSE;
1739     }
1740   textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
1741   textColors[ColorNone].attr = 0;
1742   
1743   //    XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
1744   
1745   /*
1746    * widget hierarchy
1747    */
1748   if (tinyLayout) {
1749     layoutName = "tinyLayout";
1750   } else if (smallLayout) {
1751     layoutName = "smallLayout";
1752   } else {
1753     layoutName = "normalLayout";
1754   }
1755   
1756   if (appData.titleInWindow) {
1757     /* todo check what this appdata does */
1758   }
1759   
1760   if (appData.showButtonBar) {
1761     /* TODO hide button bar if requested */
1762   }
1763
1764   
1765   if (appData.titleInWindow)
1766     {
1767       if (smallLayout)
1768         {
1769           /* make it small */
1770           if (appData.showButtonBar)
1771             {
1772               
1773             }
1774         }
1775       else
1776         {
1777           if (appData.showButtonBar)
1778             {
1779             }
1780         }
1781     }
1782   else
1783     {
1784     }
1785   
1786   
1787   /* set some checkboxes in the menu according to appData */
1788   
1789   if (appData.alwaysPromoteToQueen)
1790     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Always Queen")),TRUE);
1791   
1792   if (appData.animateDragging)
1793     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Dragging")),TRUE);
1794   
1795   if (appData.animate)
1796     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Animate Moving")),TRUE);
1797   
1798   if (appData.autoComment)
1799     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Comment")),TRUE);
1800   
1801   if (appData.autoCallFlag)
1802     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flag")),TRUE);
1803   
1804   if (appData.autoFlipView)
1805     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Flip View")),TRUE);
1806   
1807   if (appData.autoObserve)
1808     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Observe")),TRUE);
1809   
1810   if (appData.autoRaiseBoard)
1811     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Raise Board")),TRUE);
1812   
1813   if (appData.autoSaveGames)
1814     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1815   
1816   if (appData.saveGameFile[0] != NULLCHAR)
1817     {
1818       /* Can't turn this off from menu */
1819       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Auto Save")),TRUE);
1820       gtk_action_set_sensitive(GTK_ACTION (gtk_builder_get_object (builder, "menuOptions.Auto Save")),FALSE);
1821     }
1822   
1823   if (appData.blindfold)
1824       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Blindfold")),TRUE);
1825   
1826   if (appData.flashCount > 0)
1827     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Flash Moves")),TRUE);
1828   
1829   if (appData.getMoveList)
1830     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Get Move List")),TRUE);
1831   
1832 #if HIGHDRAG
1833   if (appData.highlightDragging)
1834     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Dragging")),TRUE);
1835 #endif
1836   
1837   if (appData.highlightLastMove)
1838     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Highlight Last Move")),TRUE);
1839   
1840   if (appData.icsAlarm)
1841     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.ICS Alarm")),TRUE);
1842   
1843   if (appData.ringBellAfterMoves)
1844     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Move Sound")),TRUE);
1845   
1846   if (appData.oldSaveStyle)
1847     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Old Save Style")),TRUE);
1848   
1849   if (appData.periodicUpdates)
1850     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Periodic Updates")),TRUE);
1851   
1852   if (appData.ponderNextMove)
1853     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Ponder Next Move")),TRUE);
1854   
1855   if (appData.popupExitMessage)
1856     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Exit Message")),TRUE);
1857   
1858   if (appData.popupMoveErrors)
1859     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Popup Move Errors")),TRUE);
1860   
1861   if (appData.premove)
1862     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Premove")),TRUE);
1863   
1864   if (appData.quietPlay)
1865     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Quit Play")),TRUE);
1866   
1867   if (appData.showCoords)
1868     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Show Coords")),TRUE);
1869   
1870   if (appData.showThinking)
1871     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Hide Thinking")),TRUE);
1872   
1873   if (appData.testLegality)
1874     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuOptions.Test Legality")),TRUE);
1875   
1876   // TODO: add
1877   //    if (saveSettingsOnExit) {
1878   //    XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
1879   //                args, 1);
1880   //   }
1881   
1882   
1883   /* end setting check boxes */
1884   
1885   /* load square colors */
1886   SVGLightSquare   = load_pixbuf("svg/LightSquare.svg",squareSize);
1887   SVGDarkSquare    = load_pixbuf("svg/DarkSquare.svg",squareSize);
1888   SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
1889   
1890   /* use two icons to indicate if it is white's or black's turn */
1891   WhiteIcon  = load_pixbuf("svg/icon_white.svg",0);
1892   BlackIcon  = load_pixbuf("svg/icon_black.svg",0);
1893   WindowIcon = WhiteIcon;
1894   gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
1895   
1896   
1897   /* realize window */
1898   gtk_widget_show (GUI_Window);
1899   
1900   /* recalc boardsize */
1901   CreateGCs();
1902   CreatePieces();
1903   CreatePieceMenus();
1904   
1905   if (appData.animate || appData.animateDragging)
1906     CreateAnimVars();
1907   
1908   /* [AS] Restore layout */
1909   if( wpMoveHistory.visible ) {
1910     HistoryPopUp();
1911   }
1912   
1913   if( wpEvalGraph.visible ) 
1914     {
1915       EvalGraphPopUp();
1916     };
1917   
1918   if( wpEngineOutput.visible ) {
1919     EngineOutputPopUp();
1920   }
1921   
1922   InitBackEnd2();
1923   
1924   if (errorExitStatus == -1) {
1925     if (appData.icsActive) {
1926       /* We now wait until we see "login:" from the ICS before
1927          sending the logon script (problems with timestamp otherwise) */
1928       /*ICSInitScript();*/
1929       if (appData.icsInputBox) ICSInputBoxPopUp();
1930     }
1931     
1932 #ifdef SIGWINCH
1933     signal(SIGWINCH, TermSizeSigHandler);
1934 #endif
1935     signal(SIGINT, IntSigHandler);
1936     signal(SIGTERM, IntSigHandler);
1937     if (*appData.cmailGameName != NULLCHAR) {
1938       signal(SIGUSR1, CmailSigHandler);
1939     }
1940   }
1941   gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
1942   InitPosition(TRUE);
1943   
1944   /*
1945    * Create a cursor for the board widget.
1946    * (This needs to be called after the window has been created to have access to board-window)
1947    */
1948   
1949   BoardCursor = gdk_cursor_new(GDK_HAND2);
1950   gdk_window_set_cursor(GUI_Board->window, BoardCursor);
1951   gdk_cursor_destroy(BoardCursor);
1952   
1953   /* end cursor */
1954   gtk_main ();
1955   
1956   if (appData.debugMode) fclose(debugFP); // [DM] debug
1957   return 0;
1958 }
1959
1960 void
1961 ShutDownFrontEnd()
1962 {
1963   if (appData.icsActive && oldICSInteractionTitle != NULL) {
1964     DisplayIcsInteractionTitle(oldICSInteractionTitle);
1965   }
1966   if (saveSettingsOnExit) SaveSettings(settingsFileName);
1967   unlink(gameCopyFilename);
1968   unlink(gamePasteFilename);
1969 }
1970
1971 RETSIGTYPE TermSizeSigHandler(int sig)
1972 {
1973   update_ics_width();
1974 }
1975
1976 RETSIGTYPE
1977 IntSigHandler(sig)
1978      int sig;
1979 {
1980   ExitEvent(sig);
1981 }
1982
1983 RETSIGTYPE
1984 CmailSigHandler(sig)
1985      int sig;
1986 {
1987   int dummy = 0;
1988   int error;
1989   
1990   signal(SIGUSR1, SIG_IGN);     /* suspend handler     */
1991   
1992   /* Activate call-back function CmailSigHandlerCallBack()             */
1993   OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
1994   
1995   signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
1996 }
1997
1998 void
1999 CmailSigHandlerCallBack(isr, closure, message, count, error)
2000      InputSourceRef isr;
2001      VOIDSTAR closure;
2002      char *message;
2003      int count;
2004      int error;
2005 {
2006   BoardToTop();
2007   ReloadCmailMsgEvent(TRUE);    /* Reload cmail msg  */
2008 }
2009 /**** end signal code ****/
2010
2011
2012 void
2013 ICSInitScript()
2014 {
2015   FILE *f;
2016   char buf[MSG_SIZ];
2017   char *p;
2018   
2019   f = fopen(appData.icsLogon, "r");
2020   if (f == NULL) {
2021     p = getenv("HOME");
2022     if (p != NULL) {
2023       strcpy(buf, p);
2024       strcat(buf, "/");
2025       strcat(buf, appData.icsLogon);
2026       f = fopen(buf, "r");
2027     }
2028   }
2029   if (f != NULL)
2030     ProcessICSInitScript(f);
2031 }
2032
2033 void
2034 ResetFrontEnd()
2035 {
2036   CommentPopDown();
2037   EditCommentPopDown();
2038   TagsPopDown();
2039   return;
2040 }
2041
2042 void
2043 GreyRevert(grey)
2044      Boolean grey;
2045 {
2046   Widget w;
2047   if (!menuBarWidget) return;
2048   w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2049   if (w == NULL) {
2050     DisplayError("menuStep.Revert", 0);
2051   } else {
2052     XtSetSensitive(w, !grey);
2053   }
2054 }
2055
2056 void
2057 SetMenuEnables(enab)
2058      Enables *enab;
2059 {
2060   GObject *o;
2061   
2062   if (!builder) return;
2063   while (enab->name != NULL) {
2064     o = gtk_builder_get_object(builder, enab->name);
2065     if(GTK_IS_WIDGET(o))
2066       gtk_widget_set_sensitive(GTK_WIDGET (o),enab->value);
2067     else
2068       {
2069         if(GTK_IS_ACTION(o))
2070           gtk_action_set_sensitive(GTK_ACTION (o),enab->value);
2071         else
2072           DisplayError(enab->name, 0);
2073       }
2074     enab++;
2075   }
2076 }
2077
2078 void SetICSMode()
2079 {
2080   SetMenuEnables(icsEnables);
2081   
2082 #ifdef ZIPPY
2083   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2084     {}; //     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2085 #endif
2086 }
2087
2088 void
2089 SetNCPMode()
2090 {
2091   SetMenuEnables(ncpEnables);
2092 }
2093
2094 void
2095 SetGNUMode()
2096 {
2097   SetMenuEnables(gnuEnables);
2098 }
2099
2100 void
2101 SetCmailMode()
2102 {
2103   SetMenuEnables(cmailEnables);
2104 }
2105
2106 void
2107 SetTrainingModeOn()
2108 {
2109   SetMenuEnables(trainingOnEnables);
2110   if (appData.showButtonBar) {
2111     //    XtSetSensitive(buttonBarWidget, False);
2112   }
2113   CommentPopDown();
2114 }
2115
2116 void
2117 SetTrainingModeOff()
2118 {
2119   SetMenuEnables(trainingOffEnables);
2120   if (appData.showButtonBar) {
2121     //    XtSetSensitive(buttonBarWidget, True);
2122   }
2123 }
2124
2125 void
2126 SetUserThinkingEnables()
2127 {
2128   if (appData.noChessProgram) return;
2129   SetMenuEnables(userThinkingEnables);
2130 }
2131
2132 void
2133 SetMachineThinkingEnables()
2134 {
2135   if (appData.noChessProgram) return;
2136   SetMenuEnables(machineThinkingEnables);
2137   switch (gameMode) {
2138   case MachinePlaysBlack:
2139   case MachinePlaysWhite:
2140   case TwoMachinesPlay:
2141     //    XtSetSensitive(XtNameToWidget(menuBarWidget,
2142     //                            ModeToWidgetName(gameMode)), True);
2143     break;
2144   default:
2145     break;
2146   }
2147 }
2148
2149 #define Abs(n) ((n)<0 ? -(n) : (n))
2150
2151 /*
2152  * Find a font that matches "pattern" that is as close as
2153  * possible to the targetPxlSize.  Prefer fonts that are k
2154  * pixels smaller to fonts that are k pixels larger.  The
2155  * pattern must be in the X Consortium standard format,
2156  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2157  * The return value should be freed with XtFree when no
2158  * longer needed.
2159  */
2160 char *FindFont(pattern, targetPxlSize)
2161      char *pattern;
2162      int targetPxlSize;
2163 {
2164   char **fonts, *p, *best, *scalable, *scalableTail;
2165   int i, j, nfonts, minerr, err, pxlSize;
2166   
2167 #ifdef ENABLE_NLS
2168   char **missing_list;
2169   int missing_count;
2170   char *def_string, *base_fnt_lst, strInt[3];
2171   XFontSet fntSet;
2172   XFontStruct **fnt_list;
2173   
2174   base_fnt_lst = calloc(1, strlen(pattern) + 3);
2175   sprintf(strInt, "%d", targetPxlSize);
2176   p = strstr(pattern, "--");
2177   strncpy(base_fnt_lst, pattern, p - pattern + 2);
2178   strcat(base_fnt_lst, strInt);
2179   strcat(base_fnt_lst, strchr(p + 2, '-'));
2180   
2181   if ((fntSet = XCreateFontSet(xDisplay,
2182                                base_fnt_lst,
2183                                &missing_list,
2184                                &missing_count,
2185                                &def_string)) == NULL) {
2186     
2187     fprintf(stderr, _("Unable to create font set.\n"));
2188     exit (2);
2189   }
2190   
2191   nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
2192 #else
2193   //    fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2194   //    if (nfonts < 1) {
2195   //    fprintf(stderr, _("%s: no fonts match pattern %s\n"),
2196   //            programName, pattern);
2197   //    exit(2);
2198   //    }
2199 #endif
2200   
2201   best = fonts[0];
2202   scalable = NULL;
2203   minerr = 999999;
2204   for (i=0; i<nfonts; i++) {
2205     j = 0;
2206     p = fonts[i];
2207     if (*p != '-') continue;
2208     while (j < 7) {
2209       if (*p == NULLCHAR) break;
2210       if (*p++ == '-') j++;
2211     }
2212     if (j < 7) continue;
2213     pxlSize = atoi(p);
2214     if (pxlSize == 0) {
2215       scalable = fonts[i];
2216       scalableTail = p;
2217     } else {
2218       err = pxlSize - targetPxlSize;
2219       if (Abs(err) < Abs(minerr) ||
2220           (minerr > 0 && err < 0 && -err == minerr)) {
2221         best = fonts[i];
2222         minerr = err;
2223       }
2224     }
2225   }
2226   if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2227     /* If the error is too big and there is a scalable font,
2228        use the scalable font. */
2229     int headlen = scalableTail - scalable;
2230     p = (char *) XtMalloc(strlen(scalable) + 10);
2231     while (isdigit(*scalableTail)) scalableTail++;
2232     sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2233   } else {
2234     p = (char *) XtMalloc(strlen(best) + 1);
2235     strcpy(p, best);
2236   }
2237   if (appData.debugMode) {
2238     fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
2239             pattern, targetPxlSize, p);
2240   }
2241 #ifdef ENABLE_NLS
2242   if (missing_count > 0)
2243     XFreeStringList(missing_list);
2244   //    XFreeFontSet(xDisplay, fntSet);
2245 #else
2246   XFreeFontNames(fonts);
2247 #endif
2248   return p;
2249 }
2250
2251 void CreateGCs()
2252 {
2253   /* GCs are not needed anymore for GTK  just left them in here for the moment, since there is a lot of X-code still around that's wants them*/
2254   return;
2255 }
2256
2257 void CreatePieces()
2258 {
2259   int i;
2260   
2261   /* free if used 
2262      for(i=0;i<MAXPIECES;i++)
2263      {
2264      if(SVGpieces[i])
2265      {  
2266      g_free(SVGpieces[i]);
2267      SVGpieces[i]=NULL;
2268      }
2269      }
2270   */
2271   
2272   /* reload these */
2273   SVGLightSquare   = load_pixbuf("svg/LightSquare.svg",squareSize);
2274   SVGDarkSquare    = load_pixbuf("svg/DarkSquare.svg",squareSize);
2275   SVGNeutralSquare = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2276   
2277   
2278   /* get some defaults going */
2279   for(i=WhitePawn; i<DemotePiece+1; i++)
2280     SVGpieces[i]   = load_pixbuf("svg/NeutralSquare.svg",squareSize);
2281   
2282   SVGpieces[WhitePawn]   = load_pixbuf("svg/WhitePawn.svg",squareSize);
2283   SVGpieces[WhiteKnight] = load_pixbuf("svg/WhiteKnight.svg",squareSize);
2284   SVGpieces[WhiteBishop] = load_pixbuf("svg/WhiteBishop.svg",squareSize);
2285   SVGpieces[WhiteRook]   = load_pixbuf("svg/WhiteRook.svg",squareSize);
2286   SVGpieces[WhiteQueen]  = load_pixbuf("svg/WhiteQueen.svg",squareSize);
2287   SVGpieces[WhiteKing]   = load_pixbuf("svg/WhiteKing.svg",squareSize);
2288   
2289   SVGpieces[BlackPawn]   = load_pixbuf("svg/BlackPawn.svg",squareSize);
2290   SVGpieces[BlackKnight] = load_pixbuf("svg/BlackKnight.svg",squareSize);
2291   SVGpieces[BlackBishop] = load_pixbuf("svg/BlackBishop.svg",squareSize);
2292   SVGpieces[BlackRook]   = load_pixbuf("svg/BlackRook.svg",squareSize);
2293   SVGpieces[BlackQueen]  = load_pixbuf("svg/BlackQueen.svg",squareSize);
2294   SVGpieces[BlackKing]   = load_pixbuf("svg/BlackKing.svg",squareSize);
2295   
2296   return;
2297 }
2298
2299
2300 static void MenuBarSelect(w, addr, index)
2301      Widget w;
2302      caddr_t addr;
2303      caddr_t index;
2304 {
2305   XtActionProc proc = (XtActionProc) addr;
2306   
2307   (proc)(NULL, NULL, NULL, NULL);
2308 }
2309
2310 void CreateMenuBarPopup(parent, name, mb)
2311      Widget parent;
2312      String name;
2313      Menu *mb;
2314 {
2315   int j;
2316   Widget menu, entry;
2317   MenuItem *mi;
2318   Arg args[16];
2319   
2320   menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2321                             parent, NULL, 0);
2322   j = 0;
2323   XtSetArg(args[j], XtNleftMargin, 20);   j++;
2324   XtSetArg(args[j], XtNrightMargin, 20);  j++;
2325   mi = mb->mi;
2326   while (mi->string != NULL) {
2327     if (strcmp(mi->string, "----") == 0) {
2328       entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
2329                                     menu, args, j);
2330     } else {
2331       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
2332       entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
2333                                     menu, args, j+1);
2334       XtAddCallback(entry, XtNcallback,
2335                     (XtCallbackProc) MenuBarSelect,
2336                     (caddr_t) mi->proc);
2337         }
2338     mi++;
2339   }
2340 }
2341
2342 Widget 
2343 CreateMenuBar(mb)
2344      Menu *mb;
2345 {
2346   int j;
2347   Widget anchor, menuBar;
2348   Arg args[16];
2349   char menuName[MSG_SIZ];
2350   
2351   j = 0;
2352   XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
2353   XtSetArg(args[j], XtNvSpace, 0);                        j++;
2354   XtSetArg(args[j], XtNborderWidth, 0);                   j++;
2355   menuBar = XtCreateWidget("menuBar", boxWidgetClass,
2356                            formWidget, args, j);
2357   
2358   while (mb->name != NULL) {
2359     strcpy(menuName, "menu");
2360     strcat(menuName, mb->name);
2361     j = 0;
2362     XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
2363     if (tinyLayout) {
2364       char shortName[2];
2365       shortName[0] = _(mb->name)[0];
2366       shortName[1] = NULLCHAR;
2367       XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
2368     }
2369     else {
2370       XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
2371     }
2372     
2373     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
2374     anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
2375                                    menuBar, args, j);
2376     CreateMenuBarPopup(menuBar, menuName, mb);
2377     mb++;
2378   }
2379   return menuBar;
2380 }
2381
2382
2383 Widget
2384 CreatePieceMenu(name, color)
2385      char *name;
2386      int color;
2387 {
2388   int i;
2389   Widget entry, menu;
2390   Arg args[16];
2391   ChessSquare selection;
2392   
2393   menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
2394                             boardWidget, args, 0);
2395   
2396   for (i = 0; i < PIECE_MENU_SIZE; i++) {
2397     String item = pieceMenuStrings[color][i];
2398     
2399     if (strcmp(item, "----") == 0) {
2400       entry = XtCreateManagedWidget(item, smeLineObjectClass,
2401                                     menu, NULL, 0);
2402     } else {
2403       XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2404       entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2405                                     menu, args, 1);
2406       selection = pieceMenuTranslation[color][i];
2407       XtAddCallback(entry, XtNcallback,
2408                     (XtCallbackProc) PieceMenuSelect,
2409                     (caddr_t) selection);
2410       if (selection == WhitePawn || selection == BlackPawn) {
2411         XtSetArg(args[0], XtNpopupOnEntry, entry);
2412         XtSetValues(menu, args, 1);
2413       }
2414     }
2415   }
2416   return menu;
2417 }
2418
2419 void
2420 CreatePieceMenus()
2421 {
2422   int i;
2423   Widget entry;
2424   Arg args[16];
2425   ChessSquare selection;
2426   
2427   //    whitePieceMenu = CreatePieceMenu("menuW", 0);
2428   //    blackPieceMenu = CreatePieceMenu("menuB", 1);
2429   //
2430   //    XtRegisterGrabAction(PieceMenuPopup, True,
2431   //                     (unsigned)(ButtonPressMask|ButtonReleaseMask),
2432   //                     GrabModeAsync, GrabModeAsync);
2433   //
2434   //    XtSetArg(args[0], XtNlabel, _("Drop"));
2435   //    dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
2436   //                              boardWidget, args, 1);
2437   //    for (i = 0; i < DROP_MENU_SIZE; i++) {
2438   //    String item = dropMenuStrings[i];
2439   //
2440   //    if (strcmp(item, "----") == 0) {
2441   //        entry = XtCreateManagedWidget(item, smeLineObjectClass,
2442   //                                      dropMenu, NULL, 0);
2443   //    } else {
2444   //          XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
2445   //        entry = XtCreateManagedWidget(item, smeBSBObjectClass,
2446   //                                dropMenu, args, 1);
2447   //        selection = dropMenuTranslation[i];
2448   //        XtAddCallback(entry, XtNcallback,
2449   //                      (XtCallbackProc) DropMenuSelect,
2450   //                      (caddr_t) selection);
2451   //    }
2452   //    }
2453 }
2454
2455 void 
2456 SetupDropMenu()
2457 {
2458   int i, j, count;
2459   char label[32];
2460   Arg args[16];
2461   Widget entry;
2462   char* p;
2463   
2464   for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
2465     entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
2466     p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
2467                dmEnables[i].piece);
2468     XtSetSensitive(entry, p != NULL || !appData.testLegality
2469                    /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
2470                                    && !appData.icsActive));
2471     count = 0;
2472     while (p && *p++ == dmEnables[i].piece) count++;
2473     snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
2474     j = 0;
2475     XtSetArg(args[j], XtNlabel, label); j++;
2476     XtSetValues(entry, args, j);
2477   }
2478 }
2479
2480 void 
2481 PieceMenuPopup(w, event, params, num_params)
2482      Widget w;
2483      XEvent *event;
2484      String *params;
2485      Cardinal *num_params;
2486 {
2487   String whichMenu; int menuNr;
2488   if (event->type == ButtonRelease)
2489     menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY); 
2490   else if (event->type == ButtonPress)
2491     menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
2492   switch(menuNr) {
2493   case 0: whichMenu = params[0]; break;
2494   case 1: SetupDropMenu(); whichMenu = "menuD"; break;
2495   case 2:
2496   case -1: if (errorUp) ErrorPopDown();
2497   default: return;
2498   }
2499   XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
2500 }
2501
2502 static void 
2503 PieceMenuSelect(w, piece, junk)
2504      Widget w;
2505      ChessSquare piece;
2506      caddr_t junk;
2507 {
2508   if (pmFromX < 0 || pmFromY < 0) return;
2509   EditPositionMenuEvent(piece, pmFromX, pmFromY);
2510 }
2511
2512 static void 
2513 DropMenuSelect(w, piece, junk)
2514      Widget w;
2515      ChessSquare piece;
2516      caddr_t junk;
2517 {
2518   if (pmFromX < 0 || pmFromY < 0) return;
2519   DropMenuEvent(piece, pmFromX, pmFromY);
2520 }
2521
2522 /*
2523  * If the user selects on a border boundary, return -1; if off the board,
2524  *   return -2.  Otherwise map the event coordinate to the square.
2525  */
2526 int 
2527 EventToSquare(x, limit)
2528      int x;
2529 {
2530   if (x <= 0)
2531     return -2;
2532   if (x < lineGap)
2533     return -1;
2534   x -= lineGap;
2535   if ((x % (squareSize + lineGap)) >= squareSize)
2536     return -1;
2537   x /= (squareSize + lineGap);
2538   if (x >= limit)
2539     return -2;
2540   return x;
2541 }
2542
2543 static void 
2544 do_flash_delay(msec)
2545      unsigned long msec;
2546 {
2547   TimeDelay(msec);
2548 }
2549
2550 static void 
2551 drawHighlight(file, rank, line_type)
2552      int file, rank, line_type;
2553 {
2554   int x, y;
2555   cairo_t *cr;
2556   
2557   if (lineGap == 0 || appData.blindfold) return;
2558   
2559   if (flipView)
2560     {
2561       x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
2562         (squareSize + lineGap);
2563       y = lineGap/2 + rank * (squareSize + lineGap);
2564     }
2565   else
2566     {
2567       x = lineGap/2 + file * (squareSize + lineGap);
2568       y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
2569         (squareSize + lineGap);
2570     }
2571   
2572   /* get a cairo_t */
2573   cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2574   
2575   /* draw the highlight */
2576   cairo_move_to (cr, x, y);
2577   cairo_rel_line_to (cr, 0,squareSize+lineGap);
2578   cairo_rel_line_to (cr, squareSize+lineGap,0);
2579   cairo_rel_line_to (cr, 0,-squareSize-lineGap);
2580   cairo_close_path (cr);
2581   
2582   cairo_set_line_width (cr, lineGap);
2583   switch(line_type)
2584     {
2585       /* TODO: use appdata colors */
2586     case LINE_TYPE_HIGHLIGHT:
2587       cairo_set_source_rgba (cr, 1, 1, 0, 1.0);
2588       break;
2589     case LINE_TYPE_PRE:
2590       cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
2591       break;
2592     case LINE_TYPE_NORMAL:
2593     default:
2594       cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
2595     }
2596   
2597   cairo_stroke (cr);
2598   
2599   /* free memory */
2600   cairo_destroy (cr);
2601   
2602   return;
2603 }
2604
2605 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
2606 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
2607
2608 void
2609 SetHighlights(fromX, fromY, toX, toY)
2610      int fromX, fromY, toX, toY;
2611 {
2612   if (hi1X != fromX || hi1Y != fromY)
2613     {
2614       if (hi1X >= 0 && hi1Y >= 0)
2615         {
2616           drawHighlight(hi1X, hi1Y, LINE_TYPE_NORMAL);
2617         }
2618     }
2619   if (hi2X != toX || hi2Y != toY)
2620     {
2621       if (hi2X >= 0 && hi2Y >= 0)
2622         {
2623           drawHighlight(hi2X, hi2Y, LINE_TYPE_NORMAL);
2624         }
2625     }
2626   if (hi1X != fromX || hi1Y != fromY)
2627     {
2628       if (fromX >= 0 && fromY >= 0)
2629         {
2630           drawHighlight(fromX, fromY, LINE_TYPE_HIGHLIGHT);
2631         }
2632     }     
2633   if (hi2X != toX || hi2Y != toY)
2634     {    
2635       if (toX >= 0 && toY >= 0)
2636         {
2637           drawHighlight(toX, toY, LINE_TYPE_HIGHLIGHT);
2638         }
2639     }
2640   hi1X = fromX;
2641   hi1Y = fromY;
2642   hi2X = toX;
2643   hi2Y = toY;
2644   
2645   return;
2646 }
2647
2648 void
2649 ClearHighlights()
2650 {
2651     SetHighlights(-1, -1, -1, -1);
2652 }
2653
2654
2655 void
2656 SetPremoveHighlights(fromX, fromY, toX, toY)
2657      int fromX, fromY, toX, toY;
2658 {
2659     if (pm1X != fromX || pm1Y != fromY)
2660       {
2661         if (pm1X >= 0 && pm1Y >= 0)
2662           {
2663             drawHighlight(pm1X, pm1Y, LINE_TYPE_NORMAL);
2664           }
2665         if (fromX >= 0 && fromY >= 0)
2666           {
2667             drawHighlight(fromX, fromY, LINE_TYPE_PRE);
2668           }
2669       }
2670     if (pm2X != toX || pm2Y != toY)
2671       {
2672         if (pm2X >= 0 && pm2Y >= 0)
2673           {
2674             drawHighlight(pm2X, pm2Y, LINE_TYPE_NORMAL);
2675           }
2676         if (toX >= 0 && toY >= 0)
2677           {
2678             drawHighlight(toX, toY, LINE_TYPE_PRE);
2679           }
2680       }
2681
2682     pm1X = fromX;
2683     pm1Y = fromY;
2684     pm2X = toX;
2685     pm2Y = toY;
2686
2687     return;
2688 }
2689
2690 void
2691 ClearPremoveHighlights()
2692 {
2693   SetPremoveHighlights(-1, -1, -1, -1);
2694 }
2695
2696 void BlankSquare(x, y, color, piece, dest)
2697      int x, y, color;
2698      ChessSquare piece;
2699      Drawable dest;
2700 {
2701   GdkPixbuf *pb;
2702   
2703   switch (color) 
2704     {
2705     case 0: /* dark */
2706       pb = SVGDarkSquare;
2707       break;
2708     case 1: /* light */
2709       pb = SVGLightSquare;
2710       break;
2711     case 2: /* neutral */
2712     default:
2713       pb = SVGNeutralSquare;
2714       break;
2715     }
2716   gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,pb,0,0,x,y,-1,-1, GDK_RGB_DITHER_NORMAL, 0, 0);
2717   return;
2718 }
2719  
2720 void 
2721 DrawPiece(piece, square_color, x, y, dest)
2722   ChessSquare piece;
2723  int square_color, x, y;
2724  Drawable dest;
2725 {
2726   /* redraw background, since piece might be transparent in some areas */
2727   BlankSquare(x,y,square_color,piece,dest);
2728
2729   /* draw piece */
2730   gdk_draw_pixbuf(GDK_WINDOW(GUI_Board->window),NULL,
2731                   GDK_PIXBUF(SVGpieces[piece]),0,0,x,y,-1,-1,
2732                   GDK_RGB_DITHER_NORMAL, 0, 0);
2733   return ;
2734 }
2735
2736 /* [HR] determine square color depending on chess variant. */
2737 static int SquareColor(row, column)
2738      int row, column;
2739 {
2740     int square_color;
2741
2742     if (gameInfo.variant == VariantXiangqi) {
2743         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
2744             square_color = 1;
2745         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
2746             square_color = 0;
2747         } else if (row <= 4) {
2748             square_color = 0;
2749         } else {
2750             square_color = 1;
2751         }
2752     } else {
2753         square_color = ((column + row) % 2) == 1;
2754     }
2755
2756     /* [hgm] holdings: next line makes all holdings squares light */
2757     if(column < BOARD_LEFT || column >= BOARD_RGHT) square_color = 1;
2758
2759     return square_color;
2760 }
2761
2762 void DrawSquare(row, column, piece, do_flash)
2763      int row, column, do_flash;
2764      ChessSquare piece;
2765 {
2766     int square_color, x, y;
2767     int i;
2768     char string[2];
2769     int flash_delay;
2770
2771     /* Calculate delay in milliseconds (2-delays per complete flash) */
2772     flash_delay = 500 / appData.flashRate;
2773
2774     /* calculate x and y coordinates from row and column */
2775     if (flipView)
2776       {
2777         x = lineGap + ((BOARD_WIDTH-1)-column) *
2778           (squareSize + lineGap);
2779         y = lineGap + row * (squareSize + lineGap);
2780       }
2781     else
2782       {
2783         x = lineGap + column * (squareSize + lineGap);
2784         y = lineGap + ((BOARD_HEIGHT-1)-row) *
2785           (squareSize + lineGap);
2786       }
2787
2788     square_color = SquareColor(row, column);
2789
2790     // [HGM] holdings: blank out area between board and holdings
2791     if ( column == BOARD_LEFT-1 ||  column == BOARD_RGHT
2792          || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
2793          || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) )
2794       {
2795         BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
2796
2797         // [HGM] print piece counts next to holdings
2798         string[1] = NULLCHAR;
2799         if(piece > 1)
2800           {
2801             cairo_text_extents_t extents;
2802             cairo_t *cr;
2803             int  xpos, ypos;
2804
2805             /* get a cairo_t */
2806             cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2807
2808             string[0] = '0' + piece;
2809
2810             /* TODO this has to go into the font-selection */
2811             cairo_select_font_face (cr, "Sans",
2812                                     CAIRO_FONT_SLANT_NORMAL,
2813                                     CAIRO_FONT_WEIGHT_NORMAL);
2814             //TODO
2815 //    switch (event->type) {
2816 //      case Expose:
2817 //      if (event->xexpose.count > 0) return;  /* no clipping is done */
2818 //      XDrawPosition(widget, True, NULL);
2819 //      break;
2820 //      case MotionNotify:
2821 //        if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
2822 //      default:
2823 //      return;
2824 //    }
2825 //}
2826 /* end why */
2827
2828             cairo_set_font_size (cr, 12.0);
2829             cairo_text_extents (cr, string, &extents);
2830
2831             if (column == (flipView ? BOARD_LEFT-1 : BOARD_RGHT) )
2832               {
2833                 xpos= x + squareSize - extents.width - 2;
2834                 ypos= y + extents.y_bearing + 1;
2835               }
2836             if (column == (flipView ? BOARD_RGHT : BOARD_LEFT-1) && piece > 1)
2837               {
2838                 xpos= x + 2;
2839                 ypos = y + extents.y_bearing + 1;
2840               }
2841
2842             /* TODO mono mode? */
2843             cairo_move_to (cr, xpos, ypos);
2844             cairo_text_path (cr, string);
2845             cairo_set_source_rgb (cr, 1.0, 1.0, 1);
2846             cairo_fill_preserve (cr);
2847             cairo_set_source_rgb (cr, 0, 0, 0);
2848             cairo_set_line_width (cr, 0.1);
2849             cairo_stroke (cr);
2850
2851             /* free memory */
2852             cairo_destroy (cr);
2853           }
2854       }
2855     else
2856       {
2857         /* square on the board */
2858         if (piece == EmptySquare || appData.blindfold)
2859           {
2860             BlankSquare(x, y, square_color, piece, xBoardWindow);
2861           }
2862         else
2863           {
2864             if (do_flash && appData.flashCount > 0)
2865               {
2866                 for (i=0; i<appData.flashCount; ++i)
2867                   {
2868
2869                     DrawPiece(piece, square_color, x, y, xBoardWindow);
2870                     do_flash_delay(flash_delay);
2871
2872                     BlankSquare(x, y, square_color, piece, xBoardWindow);
2873                     do_flash_delay(flash_delay);
2874                   }
2875               }
2876             DrawPiece(piece, square_color, x, y, xBoardWindow);
2877           }
2878       }
2879
2880     /* show coordinates if necessary */
2881     if(appData.showCoords)
2882       {
2883         cairo_text_extents_t extents;
2884         cairo_t *cr;
2885         int  xpos, ypos;
2886
2887         /* TODO this has to go into the font-selection */
2888         cairo_select_font_face (cr, "Sans",
2889                                 CAIRO_FONT_SLANT_NORMAL,
2890                                 CAIRO_FONT_WEIGHT_NORMAL);
2891         cairo_set_font_size (cr, 12.0);
2892
2893         string[1] = NULLCHAR;
2894
2895         /* get a cairo_t */
2896         cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
2897
2898         if (row == (flipView ? BOARD_HEIGHT-1 : 0) &&
2899             column >= BOARD_LEFT && column < BOARD_RGHT)
2900           {
2901             string[0] = 'a' + column - BOARD_LEFT;
2902             cairo_text_extents (cr, string, &extents);
2903
2904             xpos = x + squareSize - extents.width - 2;
2905             ypos = y + squareSize - extents.height - extents.y_bearing - 1;
2906
2907             if (appData.monoMode)
2908               { /*TODO*/
2909               }
2910             else
2911               {
2912               }
2913
2914             cairo_move_to (cr, xpos, ypos);
2915             cairo_text_path (cr, string);
2916             cairo_set_source_rgb (cr, 0.0, 0.0, 0);
2917             cairo_fill_preserve (cr);
2918             cairo_set_source_rgb (cr, 0, 1.0, 0);
2919             cairo_set_line_width (cr, 0.1);
2920             cairo_stroke (cr);
2921           }
2922         if ( column == (flipView ? BOARD_RGHT-1 : BOARD_LEFT))
2923           {
2924
2925             string[0] = ONE + row;
2926             cairo_text_extents (cr, string, &extents);
2927
2928             xpos = x + 2;
2929             ypos = y + extents.height + 1;
2930
2931             if (appData.monoMode)
2932               { /*TODO*/
2933               }
2934             else
2935               {
2936               }
2937
2938             cairo_move_to (cr, xpos, ypos);
2939             cairo_text_path (cr, string);
2940             cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
2941             cairo_fill_preserve (cr);
2942             cairo_set_source_rgb (cr, 0, 0, 1.0);
2943             cairo_set_line_width (cr, 0.1);
2944             cairo_stroke (cr);
2945
2946           }
2947         /* free memory */
2948         cairo_destroy (cr);
2949       }
2950
2951     return;
2952 }
2953
2954
2955 /* Returns 1 if there are "too many" differences between b1 and b2
2956    (i.e. more than 1 move was made) */
2957 static int too_many_diffs(b1, b2)
2958      Board b1, b2;
2959 {
2960     int i, j;
2961     int c = 0;
2962
2963     for (i=0; i<BOARD_HEIGHT; ++i) {
2964         for (j=0; j<BOARD_WIDTH; ++j) {
2965             if (b1[i][j] != b2[i][j]) {
2966                 if (++c > 4)    /* Castling causes 4 diffs */
2967                   return 1;
2968             }
2969         }
2970     }
2971
2972     return 0;
2973 }
2974
2975 /* Matrix describing castling maneuvers */
2976 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
2977 static int castling_matrix[4][5] = {
2978     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
2979     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
2980     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
2981     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
2982 };
2983
2984 /* Checks whether castling occurred. If it did, *rrow and *rcol
2985    are set to the destination (row,col) of the rook that moved.
2986
2987    Returns 1 if castling occurred, 0 if not.
2988
2989    Note: Only handles a max of 1 castling move, so be sure
2990    to call too_many_diffs() first.
2991    */
2992 static int check_castle_draw(newb, oldb, rrow, rcol)
2993      Board newb, oldb;
2994      int *rrow, *rcol;
2995 {
2996     int i, *r, j;
2997     int match;
2998
2999     /* For each type of castling... */
3000     for (i=0; i<4; ++i) {
3001         r = castling_matrix[i];
3002
3003         /* Check the 4 squares involved in the castling move */
3004         match = 0;
3005         for (j=1; j<=4; ++j) {
3006             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
3007                 match = 1;
3008                 break;
3009             }
3010         }
3011
3012         if (!match) {
3013             /* All 4 changed, so it must be a castling move */
3014             *rrow = r[0];
3015             *rcol = r[3];
3016             return 1;
3017         }
3018     }
3019     return 0;
3020 }
3021
3022 // [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph 
3023 void 
3024 DrawSeekAxis( int x, int y, int xTo, int yTo )
3025 {
3026   //  XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
3027 }
3028
3029 void 
3030 DrawSeekBackground( int left, int top, int right, int bottom )
3031 {
3032   //  XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
3033 }
3034
3035 void 
3036 DrawSeekText(char *buf, int x, int y)
3037 {
3038   //    XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
3039 }
3040
3041 void 
3042 DrawSeekDot(int x, int y, int colorNr)
3043 {
3044   int square = colorNr & 0x80;
3045   GC color;
3046   colorNr &= 0x7F;
3047   //  color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
3048   //  if(square)
3049     //    XFillRectangle(xDisplay, xBoardWindow, color,
3050     //             x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
3051   //  else
3052     //    XFillArc(xDisplay, xBoardWindow, color, 
3053     //       x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
3054 }
3055
3056 static int damage[BOARD_RANKS][BOARD_FILES];
3057
3058 /*
3059  * event handler for redrawing the board
3060  */
3061 void DrawPosition( repaint, board)
3062      /*Boolean*/int repaint;
3063                 Board board;
3064 {
3065   int i, j, do_flash;
3066   static int lastFlipView = 0;
3067   static int lastBoardValid = 0;
3068   static Board lastBoard;
3069   int rrow, rcol;
3070
3071     if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
3072
3073   if (board == NULL) {
3074     if (!lastBoardValid) return;
3075     board = lastBoard;
3076   }
3077   if (!lastBoardValid || lastFlipView != flipView) {
3078     //    XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
3079     // XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
3080     //  args, 1);
3081   }
3082
3083   /*
3084    * It would be simpler to clear the window with XClearWindow()
3085    * but this causes a very distracting flicker.
3086    */
3087
3088   if (!repaint && lastBoardValid && lastFlipView == flipView)
3089     {
3090       /* If too much changes (begin observing new game, etc.), don't
3091          do flashing */
3092       do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
3093
3094       /* Special check for castling so we don't flash both the king
3095          and the rook (just flash the king). */
3096       if (do_flash)
3097         {
3098           if (check_castle_draw(board, lastBoard, &rrow, &rcol))
3099             {
3100               /* Draw rook with NO flashing. King will be drawn flashing later */
3101               DrawSquare(rrow, rcol, board[rrow][rcol], 0);
3102               lastBoard[rrow][rcol] = board[rrow][rcol];
3103             }
3104         }
3105
3106       /* First pass -- Draw (newly) empty squares and repair damage.
3107          This prevents you from having a piece show up twice while it
3108          is flashing on its new square */
3109       for (i = 0; i < BOARD_HEIGHT; i++)
3110         for (j = 0; j < BOARD_WIDTH; j++)
3111           if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
3112               || damage[i][j])
3113             {
3114               DrawSquare(i, j, board[i][j], 0);
3115               damage[i][j] = False;
3116             }
3117
3118       /* Second pass -- Draw piece(s) in new position and flash them */
3119       for (i = 0; i < BOARD_HEIGHT; i++)
3120         for (j = 0; j < BOARD_WIDTH; j++)
3121           if (board[i][j] != lastBoard[i][j])
3122             {
3123               DrawSquare(i, j, board[i][j], do_flash);
3124             }
3125     }
3126   else
3127     {
3128       /* redraw Grid */
3129       if (lineGap > 0)
3130         {
3131           int x1,x2,y1,y2;
3132           cairo_t *cr;
3133
3134           /* get a cairo_t */
3135           cr = gdk_cairo_create (GDK_WINDOW(GUI_Board->window));
3136
3137           cairo_set_line_width (cr, lineGap);
3138
3139           /* TODO: use appdata colors */
3140           cairo_set_source_rgba (cr, 0, 1, 0, 1.0);
3141
3142           cairo_stroke (cr);
3143
3144           for (i = 0; i < BOARD_HEIGHT + 1; i++)
3145             {
3146               x1 = 0;
3147               x2 = lineGap + BOARD_WIDTH * (squareSize + lineGap);
3148               y1 = y2 = lineGap / 2 + (i * (squareSize + lineGap));
3149
3150               cairo_move_to (cr, x1, y1);
3151               cairo_rel_line_to (cr, x2,0);
3152               cairo_stroke (cr);
3153             }
3154
3155           for (j = 0; j < BOARD_WIDTH + 1; j++)
3156             {
3157               y1 = 0;
3158               y2 = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3159               x1 = x2  = lineGap / 2 + (j * (squareSize + lineGap));
3160
3161               cairo_move_to (cr, x1, y1);
3162               cairo_rel_line_to (cr, 0, y2);
3163               cairo_stroke (cr);
3164             }
3165
3166           /* free memory */
3167           cairo_destroy (cr);
3168         }
3169
3170       /* draw pieces */
3171       for (i = 0; i < BOARD_HEIGHT; i++)
3172         for (j = 0; j < BOARD_WIDTH; j++)
3173           {
3174             DrawSquare(i, j, board[i][j], 0);
3175             damage[i][j] = False;
3176           }
3177     }
3178
3179   CopyBoard(lastBoard, board);
3180   lastBoardValid = 1;
3181   lastFlipView = flipView;
3182
3183   /* Draw highlights */
3184   if (pm1X >= 0 && pm1Y >= 0)
3185     {
3186       drawHighlight(pm1X, pm1Y, LINE_TYPE_PRE);
3187     }
3188   if (pm2X >= 0 && pm2Y >= 0)
3189     {
3190       drawHighlight(pm2X, pm2Y, LINE_TYPE_PRE);
3191     }
3192   if (hi1X >= 0 && hi1Y >= 0)
3193     {
3194       drawHighlight(hi1X, hi1Y, LINE_TYPE_HIGHLIGHT);
3195     }
3196   if (hi2X >= 0 && hi2Y >= 0)
3197     {
3198       drawHighlight(hi2X, hi2Y, LINE_TYPE_HIGHLIGHT);
3199     }
3200
3201   /* If piece being dragged around board, must redraw that too */
3202   DrawDragPiece();
3203
3204   return;
3205 }
3206
3207 void AnimateUserMove (Widget w, XEvent * event,
3208                       String * params, Cardinal * nParams)
3209 {
3210     DragPieceMove(event->xmotion.x, event->xmotion.y);
3211 }
3212
3213 void HandlePV (Widget w, XEvent * event,
3214                       String * params, Cardinal * nParams)
3215 {   // [HGM] pv: walk PV
3216     MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
3217 }
3218
3219 Widget CommentCreate(name, text, mutable, callback, lines)
3220      char *name, *text;
3221      int /*Boolean*/ mutable;
3222      XtCallbackProc callback;
3223      int lines;
3224 {
3225     Arg args[16];
3226     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
3227     Dimension bw_width;
3228     int j;
3229
3230     j = 0;
3231     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
3232     XtGetValues(boardWidget, args, j);
3233
3234     j = 0;
3235     XtSetArg(args[j], XtNresizable, True);  j++;
3236 #if TOPLEVEL
3237 //    shell =
3238 //      XtCreatePopupShell(name, topLevelShellWidgetClass,
3239 //                       shellWidget, args, j);
3240 #else
3241 //    shell =
3242 //      XtCreatePopupShell(name, transientShellWidgetClass,
3243 //                       shellWidget, args, j);
3244 #endif
3245     layout =
3246       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3247                             layoutArgs, XtNumber(layoutArgs));
3248     form =
3249       XtCreateManagedWidget("form", formWidgetClass, layout,
3250                             formArgs, XtNumber(formArgs));
3251
3252     j = 0;
3253     if (mutable) {
3254         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
3255         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
3256     }
3257     XtSetArg(args[j], XtNstring, text);  j++;
3258     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
3259     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
3260     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
3261     XtSetArg(args[j], XtNright, XtChainRight);  j++;
3262     XtSetArg(args[j], XtNresizable, True);  j++;
3263     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
3264     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3265     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
3266     XtSetArg(args[j], XtNautoFill, True);  j++;
3267     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3268     edit =
3269       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3270
3271     if (mutable) {
3272         j = 0;
3273         XtSetArg(args[j], XtNfromVert, edit);  j++;
3274         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3275         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3276         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3277         XtSetArg(args[j], XtNright, XtChainLeft); j++;
3278         b_ok =
3279           XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
3280         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
3281
3282         j = 0;
3283         XtSetArg(args[j], XtNfromVert, edit);  j++;
3284         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
3285         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3286         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3287         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3288         XtSetArg(args[j], XtNright, XtChainLeft); j++;
3289         b_cancel =
3290           XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
3291         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
3292
3293         j = 0;
3294         XtSetArg(args[j], XtNfromVert, edit);  j++;
3295         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
3296         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3297         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3298         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3299         XtSetArg(args[j], XtNright, XtChainLeft); j++;
3300         b_clear =
3301           XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
3302         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
3303     } else {
3304         j = 0;
3305         XtSetArg(args[j], XtNfromVert, edit);  j++;
3306         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3307         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3308         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3309         XtSetArg(args[j], XtNright, XtChainLeft); j++;
3310         b_close =
3311           XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
3312         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
3313
3314         j = 0;
3315         XtSetArg(args[j], XtNfromVert, edit);  j++;
3316         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
3317         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
3318         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
3319         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
3320         XtSetArg(args[j], XtNright, XtChainLeft); j++;
3321         b_edit =
3322           XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
3323         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
3324     }
3325
3326     XtRealizeWidget(shell);
3327
3328     if (commentX == -1) {
3329         int xx, yy;
3330         Window junk;
3331         Dimension pw_height;
3332         Dimension ew_height;
3333
3334         j = 0;
3335         XtSetArg(args[j], XtNheight, &ew_height);  j++;
3336         XtGetValues(edit, args, j);
3337
3338         j = 0;
3339         XtSetArg(args[j], XtNheight, &pw_height);  j++;
3340         XtGetValues(shell, args, j);
3341         commentH = pw_height + (lines - 1) * ew_height;
3342         commentW = bw_width - 16;
3343
3344         //      XSync(xDisplay, False);
3345 #ifdef NOTDEF
3346         /* This code seems to tickle an X bug if it is executed too soon
3347            after xboard starts up.  The coordinates get transformed as if
3348            the main window was positioned at (0, 0).
3349            */
3350 //      XtTranslateCoords(shellWidget,
3351 //                        (bw_width - commentW) / 2, 0 - commentH / 2,
3352 //                        &commentX, &commentY);
3353 #else  /*!NOTDEF*/
3354 //        XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3355 //                            RootWindowOfScreen(XtScreen(shellWidget)),
3356 //                            (bw_width - commentW) / 2, 0 - commentH / 2,
3357 //                            &xx, &yy, &junk);
3358         commentX = xx;
3359         commentY = yy;
3360 #endif /*!NOTDEF*/
3361         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
3362     }
3363
3364     if(wpComment.width > 0) {
3365       commentX = wpComment.x;
3366       commentY = wpComment.y;
3367       commentW = wpComment.width;
3368       commentH = wpComment.height;
3369     }
3370
3371     j = 0;
3372     XtSetArg(args[j], XtNheight, commentH);  j++;
3373     XtSetArg(args[j], XtNwidth, commentW);  j++;
3374     XtSetArg(args[j], XtNx, commentX);  j++;
3375     XtSetArg(args[j], XtNy, commentY);  j++;
3376     XtSetValues(shell, args, j);
3377     XtSetKeyboardFocus(shell, edit);
3378
3379     return shell;
3380 }
3381
3382 /* Used for analysis window and ICS input window */
3383 Widget MiscCreate(name, text, mutable, callback, lines)
3384      char *name, *text;
3385      int /*Boolean*/ mutable;
3386      XtCallbackProc callback;
3387      int lines;
3388 {
3389     Arg args[16];
3390     Widget shell, layout, form, edit;
3391     Position x, y;
3392     Dimension bw_width, pw_height, ew_height, w, h;
3393     int j;
3394     int xx, yy;
3395     Window junk;
3396
3397     j = 0;
3398     XtSetArg(args[j], XtNresizable, True);  j++;
3399 #if TOPLEVEL
3400 //    shell =
3401 //      XtCreatePopupShell(name, topLevelShellWidgetClass,
3402 //                       shellWidget, args, j);
3403 #else
3404 //    shell =
3405 //      XtCreatePopupShell(name, transientShellWidgetClass,
3406 //                       shellWidget, args, j);
3407 #endif
3408     layout =
3409       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
3410                             layoutArgs, XtNumber(layoutArgs));
3411     form =
3412       XtCreateManagedWidget("form", formWidgetClass, layout,
3413                             formArgs, XtNumber(formArgs));
3414
3415     j = 0;
3416     if (mutable) {
3417         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
3418         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
3419     }
3420     XtSetArg(args[j], XtNstring, text);  j++;
3421     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
3422     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
3423     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
3424     XtSetArg(args[j], XtNright, XtChainRight);  j++;
3425     XtSetArg(args[j], XtNresizable, True);  j++;
3426     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
3427     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
3428     XtSetArg(args[j], XtNautoFill, True);  j++;
3429     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
3430     edit =
3431       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
3432
3433     XtRealizeWidget(shell);
3434
3435     j = 0;
3436     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
3437     XtGetValues(boardWidget, args, j);
3438
3439     j = 0;
3440     XtSetArg(args[j], XtNheight, &ew_height);  j++;
3441     XtGetValues(edit, args, j);
3442
3443     j = 0;
3444     XtSetArg(args[j], XtNheight, &pw_height);  j++;
3445     XtGetValues(shell, args, j);
3446     h = pw_height + (lines - 1) * ew_height;
3447     w = bw_width - 16;
3448
3449     //    XSync(xDisplay, False);
3450 #ifdef NOTDEF
3451     /* This code seems to tickle an X bug if it is executed too soon
3452        after xboard starts up.  The coordinates get transformed as if
3453        the main window was positioned at (0, 0).
3454     */
3455 //    XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
3456 #else  /*!NOTDEF*/
3457 //    XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
3458 //                        RootWindowOfScreen(XtScreen(shellWidget)),
3459 //                        (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
3460 #endif /*!NOTDEF*/
3461     x = xx;
3462     y = yy;
3463     if (y < 0) y = 0; /*avoid positioning top offscreen*/
3464
3465     j = 0;
3466     XtSetArg(args[j], XtNheight, h);  j++;
3467     XtSetArg(args[j], XtNwidth, w);  j++;
3468     XtSetArg(args[j], XtNx, x);  j++;
3469     XtSetArg(args[j], XtNy, y);  j++;
3470     XtSetValues(shell, args, j);
3471
3472     return shell;
3473 }
3474
3475
3476 static int savedIndex;  /* gross that this is global */
3477
3478 void EditCommentPopUp(index, title, text)
3479      int index;
3480      char *title, *text;
3481 {
3482     Widget edit;
3483     Arg args[16];
3484     int j;
3485
3486     savedIndex = index;
3487     if (text == NULL) text = "";
3488
3489     if (editShell == NULL) {
3490         editShell =
3491           CommentCreate(title, text, True, EditCommentCallback, 4);
3492         XtRealizeWidget(editShell);
3493         //      CatchDeleteWindow(editShell, "EditCommentPopDown");
3494     } else {
3495         edit = XtNameToWidget(editShell, "*form.text");
3496         j = 0;
3497         XtSetArg(args[j], XtNstring, text); j++;
3498         XtSetValues(edit, args, j);
3499         j = 0;
3500         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
3501         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
3502         XtSetValues(editShell, args, j);
3503     }
3504
3505     XtPopup(editShell, XtGrabNone);
3506
3507     editUp = True;
3508     j = 0;
3509     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3510     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3511                 args, j);
3512 }
3513
3514 void EditCommentCallback(w, client_data, call_data)
3515      Widget w;
3516      XtPointer client_data, call_data;
3517 {
3518     String name, val;
3519     Arg args[16];
3520     int j;
3521     Widget edit;
3522
3523     j = 0;
3524     XtSetArg(args[j], XtNlabel, &name);  j++;
3525     XtGetValues(w, args, j);
3526
3527     if (strcmp(name, _("ok")) == 0) {
3528         edit = XtNameToWidget(editShell, "*form.text");
3529         j = 0;
3530         XtSetArg(args[j], XtNstring, &val); j++;
3531         XtGetValues(edit, args, j);
3532         ReplaceComment(savedIndex, val);
3533         EditCommentPopDown();
3534     } else if (strcmp(name, _("cancel")) == 0) {
3535         EditCommentPopDown();
3536     } else if (strcmp(name, _("clear")) == 0) {
3537         edit = XtNameToWidget(editShell, "*form.text");
3538         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3539         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3540     }
3541 }
3542
3543 void EditCommentPopDown()
3544 {
3545     Arg args[16];
3546     int j;
3547
3548     if (!editUp) return;
3549     j = 0;
3550     XtSetArg(args[j], XtNx, &commentX); j++;
3551     XtSetArg(args[j], XtNy, &commentY); j++;
3552     XtSetArg(args[j], XtNheight, &commentH); j++;
3553     XtSetArg(args[j], XtNwidth, &commentW); j++;
3554     XtGetValues(editShell, args, j);
3555     XtPopdown(editShell);
3556     editUp = False;
3557     j = 0;
3558     XtSetArg(args[j], XtNleftBitmap, None); j++;
3559     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
3560                 args, j);
3561 }
3562
3563 void ICSInputBoxPopUp()
3564 {
3565     Widget edit;
3566     Arg args[16];
3567     int j;
3568     char *title = _("ICS Input");
3569     XtTranslations tr;
3570
3571     if (ICSInputShell == NULL) {
3572         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
3573         tr = XtParseTranslationTable(ICSInputTranslations);
3574         edit = XtNameToWidget(ICSInputShell, "*form.text");
3575         XtOverrideTranslations(edit, tr);
3576         XtRealizeWidget(ICSInputShell);
3577         //      CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
3578
3579     } else {
3580         edit = XtNameToWidget(ICSInputShell, "*form.text");
3581         j = 0;
3582         XtSetArg(args[j], XtNstring, ""); j++;
3583         XtSetValues(edit, args, j);
3584         j = 0;
3585         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
3586         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
3587         XtSetValues(ICSInputShell, args, j);
3588     }
3589
3590     XtPopup(ICSInputShell, XtGrabNone);
3591     XtSetKeyboardFocus(ICSInputShell, edit);
3592
3593     ICSInputBoxUp = True;
3594     j = 0;
3595     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
3596     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3597                 args, j);
3598 }
3599
3600 void ICSInputSendText()
3601 {
3602     Widget edit;
3603     int j;
3604     Arg args[16];
3605     String val;
3606
3607     edit = XtNameToWidget(ICSInputShell, "*form.text");
3608     j = 0;
3609     XtSetArg(args[j], XtNstring, &val); j++;
3610     XtGetValues(edit, args, j);
3611     SendMultiLineToICS(val);
3612     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
3613     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
3614 }
3615
3616 void ICSInputBoxPopDown()
3617 {
3618     Arg args[16];
3619     int j;
3620
3621     if (!ICSInputBoxUp) return;
3622     j = 0;
3623     XtPopdown(ICSInputShell);
3624     ICSInputBoxUp = False;
3625     j = 0;
3626     XtSetArg(args[j], XtNleftBitmap, None); j++;
3627     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
3628                 args, j);
3629 }
3630
3631 void CommentPopUp(title, text)
3632      char *title, *text;
3633 {
3634     Arg args[16];
3635     int j;
3636     Widget edit;
3637
3638     if (commentShell == NULL) {
3639         commentShell =
3640           CommentCreate(title, text, False, CommentCallback, 4);
3641         XtRealizeWidget(commentShell);
3642         //      CatchDeleteWindow(commentShell, "CommentPopDown");
3643     } else {
3644         edit = XtNameToWidget(commentShell, "*form.text");
3645         j = 0;
3646         XtSetArg(args[j], XtNstring, text); j++;
3647         XtSetValues(edit, args, j);
3648         j = 0;
3649         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
3650         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
3651         XtSetValues(commentShell, args, j);
3652     }
3653
3654     XtPopup(commentShell, XtGrabNone);
3655     //    XSync(xDisplay, False);
3656
3657     commentUp = True;
3658 }
3659
3660 void CommentCallback(w, client_data, call_data)
3661      Widget w;
3662      XtPointer client_data, call_data;
3663 {
3664     String name;
3665     Arg args[16];
3666     int j;
3667
3668     j = 0;
3669     XtSetArg(args[j], XtNlabel, &name);  j++;
3670     XtGetValues(w, args, j);
3671
3672     if (strcmp(name, _("close")) == 0) {
3673         CommentPopDown();
3674     } else if (strcmp(name, _("edit")) == 0) {
3675         CommentPopDown();
3676         EditCommentEvent();
3677     }
3678 }
3679
3680
3681 void CommentPopDown()
3682 {
3683     Arg args[16];
3684     int j;
3685
3686     if (!commentUp) return;
3687     j = 0;
3688     XtSetArg(args[j], XtNx, &commentX); j++;
3689     XtSetArg(args[j], XtNy, &commentY); j++;
3690     XtSetArg(args[j], XtNwidth, &commentW); j++;
3691     XtSetArg(args[j], XtNheight, &commentH); j++;
3692     XtGetValues(commentShell, args, j);
3693     XtPopdown(commentShell);
3694     //    XSync(xDisplay, False);
3695     commentUp = False;
3696 }
3697
3698 void PromotionPopUp()
3699 {
3700     Arg args[16];
3701     Widget dialog, layout;
3702     Position x, y;
3703     Dimension bw_width, pw_width;
3704     int j;
3705
3706     j = 0;
3707     XtSetArg(args[j], XtNwidth, &bw_width); j++;
3708     XtGetValues(boardWidget, args, j);
3709
3710     j = 0;
3711     XtSetArg(args[j], XtNresizable, True); j++;
3712     XtSetArg(args[j], XtNtitle, XtNewString(_("Promotion"))); j++;
3713 //    promotionShell =
3714 //      XtCreatePopupShell("Promotion", transientShellWidgetClass,
3715 //                       shellWidget, args, j);
3716 //    layout =
3717 //      XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
3718 //                          layoutArgs, XtNumber(layoutArgs));
3719 //
3720     j = 0;
3721     XtSetArg(args[j], XtNlabel, _("Promote to what?")); j++;
3722     XtSetArg(args[j], XtNborderWidth, 0); j++;
3723     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
3724                                    layout, args, j);
3725
3726   if(gameInfo.variant != VariantShogi) {
3727     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
3728                        (XtPointer) dialog);
3729     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
3730                        (XtPointer) dialog);
3731     XawDialogAddButton(dialog, _("Bishop"), PromotionCallback,
3732                        (XtPointer) dialog);
3733     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
3734                        (XtPointer) dialog);
3735     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
3736         gameInfo.variant == VariantGiveaway) {
3737       XawDialogAddButton(dialog, _("King"), PromotionCallback,
3738                          (XtPointer) dialog);
3739     }
3740     if(gameInfo.variant == VariantCapablanca ||
3741        gameInfo.variant == VariantGothic ||
3742        gameInfo.variant == VariantCapaRandom) {
3743       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
3744                          (XtPointer) dialog);
3745       XawDialogAddButton(dialog, _("Chancellor"), PromotionCallback,
3746                          (XtPointer) dialog);
3747     }
3748   } else // [HGM] shogi
3749   {
3750       XawDialogAddButton(dialog, _("Promote"), PromotionCallback,
3751                          (XtPointer) dialog);
3752       XawDialogAddButton(dialog, _("Defer"), PromotionCallback,
3753                          (XtPointer) dialog);
3754   }
3755     XawDialogAddButton(dialog, _("cancel"), PromotionCallback,
3756                        (XtPointer) dialog);
3757
3758     XtRealizeWidget(promotionShell);
3759     //    CatchDeleteWindow(promotionShell, "PromotionPopDown");
3760
3761     j = 0;
3762     XtSetArg(args[j], XtNwidth, &pw_width); j++;
3763     XtGetValues(promotionShell, args, j);
3764
3765     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
3766                       lineGap + squareSize/3 +
3767                       ((toY == BOARD_HEIGHT-1) ^ (flipView) ?
3768                        0 : 6*(squareSize + lineGap)), &x, &y);
3769
3770     j = 0;
3771     XtSetArg(args[j], XtNx, x); j++;
3772     XtSetArg(args[j], XtNy, y); j++;
3773     XtSetValues(promotionShell, args, j);
3774
3775     XtPopup(promotionShell, XtGrabNone);
3776
3777     promotionUp = True;
3778 }
3779
3780 void PromotionPopDown()
3781 {
3782     if (!promotionUp) return;
3783     XtPopdown(promotionShell);
3784     XtDestroyWidget(promotionShell);
3785     promotionUp = False;
3786 }
3787
3788 void PromotionCallback(w, client_data, call_data)
3789      Widget w;
3790      XtPointer client_data, call_data;
3791 {
3792     String name;
3793     Arg args[16];
3794     int promoChar;
3795
3796     XtSetArg(args[0], XtNlabel, &name);
3797     XtGetValues(w, args, 1);
3798
3799     PromotionPopDown();
3800
3801     if (fromX == -1) return;
3802
3803     if (strcmp(name, _("cancel")) == 0) {
3804         fromX = fromY = -1;
3805         ClearHighlights();
3806         return;
3807     } else if (strcmp(name, _("Knight")) == 0) {
3808         promoChar = 'n';
3809     } else if (strcmp(name, _("Promote")) == 0) {
3810         promoChar = '+';
3811     } else if (strcmp(name, _("Defer")) == 0) {
3812         promoChar = '=';
3813     } else {
3814         promoChar = ToLower(name[0]);
3815     }
3816
3817     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
3818
3819     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
3820     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
3821     fromX = fromY = -1;
3822 }
3823
3824
3825 void ErrorCallback(w, client_data, call_data)
3826      Widget w;
3827      XtPointer client_data, call_data;
3828 {
3829     errorUp = False;
3830     XtPopdown(w = XtParent(XtParent(XtParent(w))));
3831     XtDestroyWidget(w);
3832     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3833 }
3834
3835
3836 void ErrorPopDown()
3837 {
3838     if (!errorUp) return;
3839     errorUp = False;
3840
3841     if(GUI_Error)
3842       gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3843
3844     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
3845
3846     return;
3847 }
3848
3849 void ErrorPopUp(title, label, modal)
3850      char *title, *label;
3851      int modal;
3852 {
3853   GUI_Error = gtk_message_dialog_new(GTK_WINDOW(GUI_Window),
3854                                   GTK_DIALOG_DESTROY_WITH_PARENT,
3855                                   GTK_MESSAGE_ERROR,
3856                                   GTK_BUTTONS_CLOSE,
3857                                   (gchar *)label);
3858
3859   gtk_window_set_title(GTK_WINDOW(GUI_Error),(gchar *) title);
3860   if(modal)
3861     {
3862       gtk_dialog_run(GTK_DIALOG(GUI_Error));
3863       gtk_widget_destroy(GTK_WIDGET(GUI_Error));
3864     }
3865   else
3866     {
3867       g_signal_connect_swapped (GUI_Error, "response",
3868                                 G_CALLBACK (ErrorPopDownProc),
3869                                 GUI_Error);
3870       errorUp = True;
3871       gtk_widget_show(GTK_WIDGET(GUI_Error));
3872     }
3873
3874   return;
3875 }
3876
3877 /* Disable all user input other than deleting the window */
3878 static int frozen = 0;
3879 void FreezeUI()
3880 {
3881   if (frozen) return;
3882   /* Grab by a widget that doesn't accept input */
3883   //  XtAddGrab(messageWidget, TRUE, FALSE);
3884   frozen = 1;
3885 }
3886
3887 /* Undo a FreezeUI */
3888 void ThawUI()
3889 {
3890   if (!frozen) return;
3891   //  XtRemoveGrab(messageWidget);
3892   frozen = 0;
3893 }
3894
3895 char *ModeToWidgetName(mode)
3896      GameMode mode;
3897 {
3898     switch (mode) {
3899       case BeginningOfGame:
3900         if (appData.icsActive)
3901           return "menuMode.ICS Client";
3902         else if (appData.noChessProgram ||
3903                  *appData.cmailGameName != NULLCHAR)
3904           return "menuMode.Edit Game";
3905         else
3906           return "menuMode.Machine Black";
3907       case MachinePlaysBlack:
3908         return "menuMode.Machine Black";
3909       case MachinePlaysWhite:
3910         return "menuMode.Machine White";
3911       case AnalyzeMode:
3912         return "menuMode.Analysis Mode";
3913       case AnalyzeFile:
3914         return "menuMode.Analyze File";
3915       case TwoMachinesPlay:
3916         return "menuMode.Two Machines";
3917       case EditGame:
3918         return "menuMode.Edit Game";
3919       case PlayFromGameFile:
3920         return "menuFile.Load Game";
3921       case EditPosition:
3922         return "menuMode.Edit Position";
3923       case Training:
3924         return "menuMode.Training";
3925       case IcsPlayingWhite:
3926       case IcsPlayingBlack:
3927       case IcsObserving:
3928       case IcsIdle:
3929       case IcsExamining:
3930         return "menuMode.ICS Client";
3931       default:
3932       case EndOfGame:
3933         return NULL;
3934     }
3935 }
3936
3937 void ModeHighlight()
3938 {
3939     static int oldPausing = FALSE;
3940     static GameMode oldmode = (GameMode) -1;
3941     char *wname;
3942
3943    // todo this toggling of the pause button doesn't seem to work?
3944     // e.g. select pause from buttonbar doesn't activate menumode.pause
3945
3946     //    if (!boardWidget || !XtIsRealized(boardWidget)) return;
3947
3948     if (pausing != oldPausing) {
3949       oldPausing = pausing;
3950       gtk_button_set_relief(GTK_BUTTON (gtk_builder_get_object (builder, "menuMode.Pause")),pausing?GTK_RELIEF_NORMAL:GTK_RELIEF_NONE);
3951       /* toggle background color in showbuttonbar */
3952       if (appData.showButtonBar) {
3953         if (pausing) {
3954           gtk_button_pressed(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3955         } else {
3956           gtk_button_released(GTK_BUTTON (gtk_builder_get_object (builder, "buttonbar.Pause")));
3957         }
3958       }
3959     }
3960
3961     // probably not needed anymore
3962 //    wname = ModeToWidgetName(oldmode);
3963 //    if(wname)
3964 //       gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, wname)),True);
3965
3966     oldmode = gameMode;
3967
3968     /* Maybe all the enables should be handled here, not just this one */
3969     gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menuMode.Training")),
3970                              gameMode == Training || gameMode == PlayFromGameFile);
3971 }
3972
3973
3974 /*
3975  * Button/menu procedures
3976  */
3977
3978 int LoadGamePopUp(f, gameNumber, title)
3979      FILE *f;
3980      int gameNumber;
3981      char *title;
3982 {
3983     cmailMsgLoaded = FALSE;
3984
3985     if (gameNumber == 0) 
3986       {
3987         int error = GameListBuild(f);
3988
3989         if (error) 
3990           {
3991             DisplayError(_("Cannot build game list"), error);
3992           } 
3993         else if (!ListEmpty(&gameList) 
3994                  && ((ListGame *) gameList.tailPred)->number > 1) 
3995           {
3996             /* we need an answer which game to load, so let's make it modal for a while*/
3997             gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , TRUE);  
3998             GameListPopUp(f, title);
3999             gtk_window_set_modal(GTK_WINDOW(GUI_GameList) , FALSE);  
4000
4001             return TRUE;
4002           };
4003
4004         GameListDestroy();
4005         gameNumber = 1;
4006       };
4007
4008     return LoadGame(f, gameNumber, title, FALSE);
4009 }
4010
4011 void ReloadCmailMsgProc(w, event, prms, nprms)
4012      Widget w;
4013      XEvent *event;
4014      String *prms;
4015      Cardinal *nprms;
4016 {
4017     ReloadCmailMsgEvent(FALSE);
4018 }
4019
4020 void MailMoveProc(w, event, prms, nprms)
4021      Widget w;
4022      XEvent *event;
4023      String *prms;
4024      Cardinal *nprms;
4025 {
4026     MailMoveEvent();
4027 }
4028
4029 /* this variable is shared between CopyPositionProc and SendPositionSelection */
4030 char *selected_fen_position=NULL;
4031
4032 Boolean
4033 SendPositionSelection(Widget w, Atom *selection, Atom *target,
4034                  Atom *type_return, XtPointer *value_return,
4035                  unsigned long *length_return, int *format_return)
4036 {
4037   char *selection_tmp;
4038
4039   if (!selected_fen_position) return False; /* should never happen */
4040 //  if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4041 //    /* note: since no XtSelectionDoneProc was registered, Xt will
4042 //     * automatically call XtFree on the value returned.  So have to
4043 //     * make a copy of it allocated with XtMalloc */
4044 //    selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
4045 //    strcpy(selection_tmp, selected_fen_position);
4046 //
4047 //    *value_return=selection_tmp;
4048 //    *length_return=strlen(selection_tmp);
4049 //    *type_return=*target;
4050 //    *format_return = 8; /* bits per byte */
4051 //    return True;
4052 //  } else if (*target == XA_TARGETS(xDisplay)) {
4053 //    Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4054 //    targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4055 //    targets_tmp[1] = XA_STRING;
4056 //    *value_return = targets_tmp;
4057 //    *type_return = XA_ATOM;
4058 //    *length_return = 2;
4059 //    *format_return = 8 * sizeof(Atom);
4060 //    if (*format_return > 32) {
4061 //      *length_return *= *format_return / 32;
4062 //      *format_return = 32;
4063 //    }
4064 //    return True;
4065 //  } else {
4066 //    return False;
4067 //  }
4068 }
4069
4070 /* note: when called from menu all parameters are NULL, so no clue what the
4071  * Widget which was clicked on was, or what the click event was
4072  */
4073 void CopyPositionProc(w, event, prms, nprms)
4074   Widget w;
4075   XEvent *event;
4076   String *prms;
4077   Cardinal *nprms;
4078   {
4079     /*
4080      * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4081      * have a notion of a position that is selected but not copied.
4082      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4083      */
4084     if(gameMode == EditPosition) EditPositionDone(TRUE);
4085     if (selected_fen_position) free(selected_fen_position);
4086     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
4087     if (!selected_fen_position) return;
4088 //    XtOwnSelection(menuBarWidget, XA_PRIMARY,
4089 //                 CurrentTime,
4090 //                 SendPositionSelection,
4091 //                 NULL/* lose_ownership_proc */ ,
4092 //                 NULL/* transfer_done_proc */);
4093 //    XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4094 //                 CurrentTime,
4095 //                 SendPositionSelection,
4096 //                 NULL/* lose_ownership_proc */ ,
4097 //                 NULL/* transfer_done_proc */);
4098   }
4099
4100 /* function called when the data to Paste is ready */
4101 static void
4102 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
4103            Atom *type, XtPointer value, unsigned long *len, int *format)
4104 {
4105   char *fenstr=value;
4106   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
4107   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
4108   EditPositionPasteFEN(fenstr);
4109   XtFree(value);
4110 }
4111
4112 /* called when Paste Position button is pressed,
4113  * all parameters will be NULL */
4114 void PastePositionProc(w, event, prms, nprms)
4115   Widget w;
4116   XEvent *event;
4117   String *prms;
4118   Cardinal *nprms;
4119 {
4120 //    XtGetSelectionValue(menuBarWidget, 
4121 //      appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4122 //      /* (XtSelectionCallbackProc) */ PastePositionCB,
4123 //      NULL, /* client_data passed to PastePositionCB */
4124 //
4125 //      /* better to use the time field from the event that triggered the
4126 //       * call to this function, but that isn't trivial to get
4127 //       */
4128 //      CurrentTime
4129 //    );
4130     return;
4131 }
4132
4133 static Boolean
4134 SendGameSelection(Widget w, Atom *selection, Atom *target,
4135                   Atom *type_return, XtPointer *value_return,
4136                   unsigned long *length_return, int *format_return)
4137 {
4138   char *selection_tmp;
4139
4140 //  if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
4141 //    FILE* f = fopen(gameCopyFilename, "r");
4142 //    long len;
4143 //    size_t count;
4144 //    if (f == NULL) return False;
4145 //    fseek(f, 0, 2);
4146 //    len = ftell(f);
4147 //    rewind(f);
4148 //    selection_tmp = XtMalloc(len + 1);
4149 //    count = fread(selection_tmp, 1, len, f);
4150 //    if (len != count) {
4151 //      XtFree(selection_tmp);
4152 //      return False;
4153 //    }
4154 //    selection_tmp[len] = NULLCHAR;
4155 //    *value_return = selection_tmp;
4156 //    *length_return = len;
4157 //    *type_return = *target;
4158 //    *format_return = 8; /* bits per byte */
4159 //    return True;
4160 //  } else if (*target == XA_TARGETS(xDisplay)) {
4161 //    Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
4162 //    targets_tmp[0] = XA_UTF8_STRING(xDisplay);
4163 //    targets_tmp[1] = XA_STRING;
4164 //    *value_return = targets_tmp;
4165 //    *type_return = XA_ATOM;
4166 //    *length_return = 2;
4167 //    *format_return = 8 * sizeof(Atom);
4168 //    if (*format_return > 32) {
4169 //      *length_return *= *format_return / 32;
4170 //      *format_return = 32;
4171 //    }
4172 //    return True;
4173 //  } else {
4174 //    return False;
4175 //  }
4176 }
4177
4178 /* note: when called from menu all parameters are NULL, so no clue what the
4179  * Widget which was clicked on was, or what the click event was
4180  */
4181 void CopyGameProc(w, event, prms, nprms)
4182   Widget w;
4183   XEvent *event;
4184   String *prms;
4185   Cardinal *nprms;
4186 {
4187   int ret;
4188
4189   ret = SaveGameToFile(gameCopyFilename, FALSE);
4190   if (!ret) return;
4191
4192   /*
4193    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
4194    * have a notion of a game that is selected but not copied.
4195    * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
4196    */
4197 //  XtOwnSelection(menuBarWidget, XA_PRIMARY,
4198 //               CurrentTime,
4199 //               SendGameSelection,
4200 //               NULL/* lose_ownership_proc */ ,
4201 //               NULL/* transfer_done_proc */);
4202 //  XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
4203 //               CurrentTime,
4204 //               SendGameSelection,
4205 //               NULL/* lose_ownership_proc */ ,
4206 //               NULL/* transfer_done_proc */);
4207 }
4208
4209 /* function called when the data to Paste is ready */
4210 static void
4211 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
4212             Atom *type, XtPointer value, unsigned long *len, int *format)
4213 {
4214   FILE* f;
4215   if (value == NULL || *len == 0) {
4216     return; /* nothing had been selected to copy */
4217   }
4218   f = fopen(gamePasteFilename, "w");
4219   if (f == NULL) {
4220     DisplayError(_("Can't open temp file"), errno);
4221     return;
4222   }
4223   fwrite(value, 1, *len, f);
4224   fclose(f);
4225   XtFree(value);
4226   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
4227 }
4228
4229 /* called when Paste Game button is pressed,
4230  * all parameters will be NULL */
4231 void PasteGameProc(w, event, prms, nprms)
4232   Widget w;
4233   XEvent *event;
4234   String *prms;
4235   Cardinal *nprms;
4236 {
4237 //    XtGetSelectionValue(menuBarWidget,
4238 //      appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
4239 //      /* (XtSelectionCallbackProc) */ PasteGameCB,
4240 //      NULL, /* client_data passed to PasteGameCB */
4241 //
4242 //      /* better to use the time field from the event that triggered the
4243 //       * call to this function, but that isn't trivial to get
4244 //       */
4245 //      CurrentTime
4246 //    );
4247 //    return;
4248 }
4249
4250 void SaveOnExitProc(w, event, prms, nprms)
4251      Widget w;
4252      XEvent *event;
4253      String *prms;
4254      Cardinal *nprms;
4255 {
4256     Arg args[16];
4257
4258     saveSettingsOnExit = !saveSettingsOnExit;
4259
4260     if (saveSettingsOnExit) {
4261         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
4262     } else {
4263         XtSetArg(args[0], XtNleftBitmap, None);
4264     }
4265     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Save Settings on Exit"),
4266                 args, 1);
4267 }
4268
4269 void SaveSettingsProc(w, event, prms, nprms)
4270      Widget w;
4271      XEvent *event;
4272      String *prms;
4273      Cardinal *nprms;
4274 {
4275      SaveSettings(settingsFileName);
4276 }
4277
4278
4279 void AutoSaveGame()
4280 {
4281   SaveGameProc(NULL, NULL);
4282   return;
4283 }
4284
4285
4286 void EditCommentProc(w, event, prms, nprms)
4287      Widget w;
4288      XEvent *event;
4289      String *prms;
4290      Cardinal *nprms;
4291 {
4292     if (editUp) {
4293         EditCommentPopDown();
4294     } else {
4295         EditCommentEvent();
4296     }
4297 }
4298
4299 void IcsInputBoxProc(w, event, prms, nprms)
4300      Widget w;
4301      XEvent *event;
4302      String *prms;
4303      Cardinal *nprms;
4304 {
4305     if (ICSInputBoxUp) {
4306         ICSInputBoxPopDown();
4307     } else {
4308         ICSInputBoxPopUp();
4309     }
4310 }
4311
4312
4313 void EnterKeyProc(w, event, prms, nprms)
4314      Widget w;
4315      XEvent *event;
4316      String *prms;
4317      Cardinal *nprms;
4318 {
4319     if (ICSInputBoxUp == True)
4320       ICSInputSendText();
4321 }
4322
4323
4324 void DebugProc(w, event, prms, nprms)
4325      Widget w;
4326      XEvent *event;
4327      String *prms;
4328      Cardinal *nprms;
4329 {
4330     appData.debugMode = !appData.debugMode;
4331 }
4332
4333 void AboutGameProc(w, event, prms, nprms)
4334      Widget w;
4335      XEvent *event;
4336      String *prms;
4337      Cardinal *nprms;
4338 {
4339     AboutGameEvent();
4340 }
4341
4342 void NothingProc(w, event, prms, nprms)
4343      Widget w;
4344      XEvent *event;
4345      String *prms;
4346      Cardinal *nprms;
4347 {
4348     return;
4349 }
4350
4351 void Iconify(w, event, prms, nprms)
4352      Widget w;
4353      XEvent *event;
4354      String *prms;
4355      Cardinal *nprms;
4356 {
4357     Arg args[16];
4358
4359 //    fromX = fromY = -1;
4360 //    XtSetArg(args[0], XtNiconic, True);
4361 //    XtSetValues(shellWidget, args, 1);
4362 }
4363
4364 void DisplayMessage(message, extMessage)
4365      gchar *message, *extMessage;
4366 {
4367     char buf[MSG_SIZ];
4368     Arg arg;
4369
4370     if (extMessage) {
4371         if (*message) {
4372             snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
4373             message = buf;
4374         } else {
4375             message = extMessage;
4376         }
4377     }
4378     gtk_label_set_text( GTK_LABEL(gtk_builder_get_object (builder, "Messages")),message);
4379
4380     return;
4381 }
4382
4383 void DisplayTitle(text)
4384      char *text;
4385 {
4386     gchar title[MSG_SIZ];
4387
4388     if (text == NULL) text = "";
4389
4390     if (appData.titleInWindow)
4391       {
4392         /* TODO */
4393       }
4394
4395     if (*text != NULLCHAR)
4396       {
4397         strcpy(title, text);
4398       }
4399     else if (appData.icsActive)
4400       {
4401         snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
4402       }
4403     else if (appData.cmailGameName[0] != NULLCHAR)
4404       {
4405         snprintf(title,sizeof(title), "%s: %s", programName, "CMail");
4406 #ifdef GOTHIC
4407     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
4408       }
4409     else if (gameInfo.variant == VariantGothic)
4410       {
4411         strcpy(title, GOTHIC);
4412 #endif
4413 #ifdef FALCON
4414       }
4415     else if (gameInfo.variant == VariantFalcon)
4416       {
4417         strcpy(title, FALCON);
4418 #endif
4419       }
4420     else if (appData.noChessProgram)
4421       {
4422         strcpy(title, programName);
4423       }
4424     else
4425       {
4426         snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
4427       }
4428     gtk_window_set_title(GTK_WINDOW(GUI_Window),title);
4429
4430     return;
4431 }
4432
4433
4434 void DisplayError(message, error)
4435      String message;
4436      int error;
4437 {
4438     char buf[MSG_SIZ];
4439
4440     if (error == 0) {
4441         if (appData.debugMode || appData.matchMode) {
4442             fprintf(stderr, "%s: %s\n", programName, message);
4443         }
4444     } else {
4445         if (appData.debugMode || appData.matchMode) {
4446             fprintf(stderr, "%s: %s: %s\n",
4447                     programName, message, strerror(error));
4448         }
4449         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4450         message = buf;
4451     }
4452     ErrorPopUp(_("Error"), message, FALSE);
4453 }
4454
4455
4456 void DisplayMoveError(message)
4457      String message;
4458 {
4459     fromX = fromY = -1;
4460     ClearHighlights();
4461     DrawPosition(FALSE, NULL);
4462     if (appData.debugMode || appData.matchMode) {
4463         fprintf(stderr, "%s: %s\n", programName, message);
4464     }
4465     if (appData.popupMoveErrors) {
4466         ErrorPopUp(_("Error"), message, FALSE);
4467     } else {
4468         DisplayMessage(message, "");
4469     }
4470 }
4471
4472
4473 void DisplayFatalError(message, error, status)
4474      String message;
4475      int error, status;
4476 {
4477     char buf[MSG_SIZ];
4478
4479     errorExitStatus = status;
4480     if (error == 0) {
4481         fprintf(stderr, "%s: %s\n", programName, message);
4482     } else {
4483         fprintf(stderr, "%s: %s: %s\n",
4484                 programName, message, strerror(error));
4485         snprintf(buf, sizeof(buf), "%s: %s", message, strerror(error));
4486         message = buf;
4487     }
4488     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
4489       ErrorPopUp(status ? _("Fatal Error") : _("Exiting"), message, TRUE);
4490     } else {
4491       ExitEvent(status);
4492     }
4493 }
4494
4495 void DisplayInformation(message)
4496      String message;
4497 {
4498     ErrorPopDown();
4499     ErrorPopUp(_("Information"), message, TRUE);
4500 }
4501
4502 void DisplayNote(message)
4503      String message;
4504 {
4505     ErrorPopDown();
4506     ErrorPopUp(_("Note"), message, FALSE);
4507 }
4508
4509 static int
4510 NullXErrorCheck(dpy, error_event)
4511      Display *dpy;
4512      XErrorEvent *error_event;
4513 {
4514     return 0;
4515 }
4516
4517 void DisplayIcsInteractionTitle(message)
4518      String message;
4519 {
4520   if (oldICSInteractionTitle == NULL) {
4521     /* Magic to find the old window title, adapted from vim */
4522     char *wina = getenv("WINDOWID");
4523     if (wina != NULL) {
4524       Window win = (Window) atoi(wina);
4525       Window root, parent, *children;
4526       unsigned int nchildren;
4527       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
4528 //      for (;;) {
4529 //      if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
4530 //      if (!XQueryTree(xDisplay, win, &root, &parent,
4531 //                      &children, &nchildren)) break;
4532 //      if (children) XFree((void *)children);
4533 //      if (parent == root || parent == 0) break;
4534 //      win = parent;
4535 //      }
4536       XSetErrorHandler(oldHandler);
4537     }
4538     if (oldICSInteractionTitle == NULL) {
4539       oldICSInteractionTitle = "xterm";
4540     }
4541   }
4542   printf("\033]0;%s\007", message);
4543   fflush(stdout);
4544 }
4545
4546 char pendingReplyPrefix[MSG_SIZ];
4547 ProcRef pendingReplyPR;
4548
4549 void AskQuestionProc(w, event, prms, nprms)
4550      Widget w;
4551      XEvent *event;
4552      String *prms;
4553      Cardinal *nprms;
4554 {
4555     if (*nprms != 4) {
4556         fprintf(stderr, _("AskQuestionProc needed 4 parameters, got %d\n"),
4557                 *nprms);
4558         return;
4559     }
4560     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
4561 }
4562
4563 void AskQuestionPopDown()
4564 {
4565     if (!askQuestionUp) return;
4566     XtPopdown(askQuestionShell);
4567     XtDestroyWidget(askQuestionShell);
4568     askQuestionUp = False;
4569 }
4570
4571 void AskQuestionReplyAction(w, event, prms, nprms)
4572      Widget w;
4573      XEvent *event;
4574      String *prms;
4575      Cardinal *nprms;
4576 {
4577     char buf[MSG_SIZ];
4578     int err;
4579     String reply;
4580
4581     reply = XawDialogGetValueString(w = XtParent(w));
4582     strcpy(buf, pendingReplyPrefix);
4583     if (*buf) strcat(buf, " ");
4584     strcat(buf, reply);
4585     strcat(buf, "\n");
4586     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
4587     AskQuestionPopDown();
4588
4589     if (err) DisplayFatalError(_("Error writing to chess program"), err, 0);
4590 }
4591
4592 void AskQuestionCallback(w, client_data, call_data)
4593      Widget w;
4594      XtPointer client_data, call_data;
4595 {
4596     String name;
4597     Arg args[16];
4598
4599     XtSetArg(args[0], XtNlabel, &name);
4600     XtGetValues(w, args, 1);
4601
4602     if (strcmp(name, _("cancel")) == 0) {
4603         AskQuestionPopDown();
4604     } else {
4605         AskQuestionReplyAction(w, NULL, NULL, NULL);
4606     }
4607 }
4608
4609 void AskQuestion(title, question, replyPrefix, pr)
4610      char *title, *question, *replyPrefix;
4611      ProcRef pr;
4612 {
4613     Arg args[16];
4614     Widget popup, layout, dialog, edit;
4615     Window root, child;
4616     int x, y, i;
4617     int win_x, win_y;
4618     unsigned int mask;
4619
4620     strcpy(pendingReplyPrefix, replyPrefix);
4621     pendingReplyPR = pr;
4622
4623     i = 0;
4624     XtSetArg(args[i], XtNresizable, True); i++;
4625     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4626 //    askQuestionShell = popup =
4627 //      XtCreatePopupShell(title, transientShellWidgetClass,
4628 //                       shellWidget, args, i);
4629 //
4630 //    layout =
4631 //      XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4632 //                          layoutArgs, XtNumber(layoutArgs));
4633 //
4634     i = 0;
4635     XtSetArg(args[i], XtNlabel, question); i++;
4636     XtSetArg(args[i], XtNvalue, ""); i++;
4637     XtSetArg(args[i], XtNborderWidth, 0); i++;
4638     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
4639                                    layout, args, i);
4640
4641     XawDialogAddButton(dialog, _("enter"), AskQuestionCallback,
4642                        (XtPointer) dialog);
4643     XawDialogAddButton(dialog, _("cancel"), AskQuestionCallback,
4644                        (XtPointer) dialog);
4645
4646     XtRealizeWidget(popup);
4647     //    CatchDeleteWindow(popup, "AskQuestionPopDown");
4648
4649 //    XQueryPointer(xDisplay, xBoardWindow, &root, &child,
4650 //                &x, &y, &win_x, &win_y, &mask);
4651 //
4652 //    XtSetArg(args[0], XtNx, x - 10);
4653 //    XtSetArg(args[1], XtNy, y - 30);
4654 //    XtSetValues(popup, args, 2);
4655 //
4656 //    XtPopup(popup, XtGrabExclusive);
4657 //    askQuestionUp = True;
4658 //
4659 //    edit = XtNameToWidget(dialog, "*value");
4660 //    XtSetKeyboardFocus(popup, edit);
4661 }
4662
4663
4664 void
4665 PlaySound(name)
4666      char *name;
4667 {
4668   if (*name == NULLCHAR) {
4669     return;
4670   } else if (strcmp(name, "$") == 0) {
4671     putc(BELLCHAR, stderr);
4672   } else {
4673     char buf[2048];
4674     snprintf(buf, sizeof(buf), "%s '%s' &", appData.soundProgram, name);
4675     system(buf);
4676   }
4677 }
4678
4679 void
4680 RingBell()
4681 {
4682   PlaySound(appData.soundMove);
4683 }
4684
4685 void
4686 PlayIcsWinSound()
4687 {
4688   PlaySound(appData.soundIcsWin);
4689 }
4690
4691 void
4692 PlayIcsLossSound()
4693 {
4694   PlaySound(appData.soundIcsLoss);
4695 }
4696
4697 void
4698 PlayIcsDrawSound()
4699 {
4700   PlaySound(appData.soundIcsDraw);
4701 }
4702
4703 void
4704 PlayIcsUnfinishedSound()
4705 {
4706   PlaySound(appData.soundIcsUnfinished);
4707 }
4708
4709 void
4710 PlayAlarmSound()
4711 {
4712   PlaySound(appData.soundIcsAlarm);
4713 }
4714
4715 void
4716 EchoOn()
4717 {
4718     system("stty echo");
4719 }
4720
4721 void
4722 EchoOff()
4723 {
4724     system("stty -echo");
4725 }
4726
4727 void
4728 Colorize(cc, continuation)
4729      ColorClass cc;
4730      int continuation;
4731 {
4732     char buf[MSG_SIZ];
4733     int count, outCount, error;
4734
4735     if (textColors[(int)cc].bg > 0) {
4736         if (textColors[(int)cc].fg > 0) {
4737             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
4738                     textColors[(int)cc].fg, textColors[(int)cc].bg);
4739         } else {
4740             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4741                     textColors[(int)cc].bg);
4742         }
4743     } else {
4744         if (textColors[(int)cc].fg > 0) {
4745             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
4746                     textColors[(int)cc].fg);
4747         } else {
4748             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
4749         }
4750     }
4751     count = strlen(buf);
4752     outCount = OutputToProcess(NoProc, buf, count, &error);
4753     if (outCount < count) {
4754         DisplayFatalError(_("Error writing to display"), error, 1);
4755     }
4756
4757     if (continuation) return;
4758     switch (cc) {
4759     case ColorShout:
4760       PlaySound(appData.soundShout);
4761       break;
4762     case ColorSShout:
4763       PlaySound(appData.soundSShout);
4764       break;
4765     case ColorChannel1:
4766       PlaySound(appData.soundChannel1);
4767       break;
4768     case ColorChannel:
4769       PlaySound(appData.soundChannel);
4770       break;
4771     case ColorKibitz:
4772       PlaySound(appData.soundKibitz);
4773       break;
4774     case ColorTell:
4775       PlaySound(appData.soundTell);
4776       break;
4777     case ColorChallenge:
4778       PlaySound(appData.soundChallenge);
4779       break;
4780     case ColorRequest:
4781       PlaySound(appData.soundRequest);
4782       break;
4783     case ColorSeek:
4784       PlaySound(appData.soundSeek);
4785       break;
4786     case ColorNormal:
4787     case ColorNone:
4788     default:
4789       break;
4790     }
4791 }
4792
4793 char *UserName()
4794 {
4795     return getpwuid(getuid())->pw_name;
4796 }
4797
4798 static char *ExpandPathName(path)
4799      char *path;
4800 {
4801     static char static_buf[2000];
4802     char *d, *s, buf[2000];
4803     struct passwd *pwd;
4804
4805     s = path;
4806     d = static_buf;
4807
4808     while (*s && isspace(*s))
4809       ++s;
4810
4811     if (!*s) {
4812         *d = 0;
4813         return static_buf;
4814     }
4815
4816     if (*s == '~') {
4817         if (*(s+1) == '/') {
4818             strcpy(d, getpwuid(getuid())->pw_dir);
4819             strcat(d, s+1);
4820         }
4821         else {
4822             strcpy(buf, s+1);
4823             *strchr(buf, '/') = 0;
4824             pwd = getpwnam(buf);
4825             if (!pwd)
4826               {
4827                   fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
4828                           buf, path);
4829                   return NULL;
4830               }
4831             strcpy(d, pwd->pw_dir);
4832             strcat(d, strchr(s+1, '/'));
4833         }
4834     }
4835     else
4836       strcpy(d, s);
4837
4838     return static_buf;
4839 }
4840
4841 char *HostName()
4842 {
4843     static char host_name[MSG_SIZ];
4844
4845 #if HAVE_GETHOSTNAME
4846     gethostname(host_name, MSG_SIZ);
4847     return host_name;
4848 #else  /* not HAVE_GETHOSTNAME */
4849 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
4850     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
4851     return host_name;
4852 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4853     return "localhost";
4854 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
4855 #endif /* not HAVE_GETHOSTNAME */
4856 }
4857
4858 guint delayedEventTimerTag = 0;
4859 DelayedEventCallback delayedEventCallback = 0;
4860
4861 void
4862 FireDelayedEvent(data)
4863      gpointer data;
4864 {
4865   /* remove timer */
4866   g_source_remove(delayedEventTimerTag);
4867   delayedEventTimerTag = 0;
4868
4869   /* call function */
4870   delayedEventCallback();
4871
4872   return;
4873 }
4874
4875 void
4876 ScheduleDelayedEvent(cb, millisec)
4877      DelayedEventCallback cb; guint millisec;
4878 {
4879     if(delayedEventTimerTag && delayedEventCallback == cb)
4880         // [HGM] alive: replace, rather than add or flush identical event
4881         g_source_remove(delayedEventTimerTag);
4882     delayedEventCallback = cb;
4883     delayedEventTimerTag = g_timeout_add(millisec,(GSourceFunc) FireDelayedEvent, NULL);
4884     return;
4885 }
4886
4887 DelayedEventCallback
4888 GetDelayedEvent()
4889 {
4890   if (delayedEventTimerTag)
4891     {
4892       return delayedEventCallback;
4893     }
4894   else
4895     {
4896       return NULL;
4897     }
4898 }
4899
4900 void
4901 CancelDelayedEvent()
4902 {
4903   if (delayedEventTimerTag)
4904     {
4905       g_source_remove(delayedEventTimerTag);
4906       delayedEventTimerTag = 0;
4907     }
4908
4909   return;
4910 }
4911
4912 guint loadGameTimerTag = 0;
4913
4914 int LoadGameTimerRunning()
4915 {
4916     return loadGameTimerTag != 0;
4917 }
4918
4919 int StopLoadGameTimer()
4920 {
4921     if (loadGameTimerTag != 0) {
4922         g_source_remove(loadGameTimerTag);
4923         loadGameTimerTag = 0;
4924         return TRUE;
4925     } else {
4926         return FALSE;
4927     }
4928 }
4929
4930 void
4931 LoadGameTimerCallback(data)
4932      gpointer data;
4933 {
4934   /* remove timer */
4935   g_source_remove(loadGameTimerTag);
4936   loadGameTimerTag = 0;
4937
4938   AutoPlayGameLoop();
4939   return;
4940 }
4941
4942 void
4943 StartLoadGameTimer(millisec)
4944      long millisec;
4945 {
4946   loadGameTimerTag =
4947     g_timeout_add( millisec, (GSourceFunc) LoadGameTimerCallback, NULL);
4948   return;
4949 }
4950
4951 guint analysisClockTag = 0;
4952
4953 gboolean
4954 AnalysisClockCallback(data)
4955      gpointer data;
4956 {
4957     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
4958          || appData.icsEngineAnalyze)
4959       {
4960         AnalysisPeriodicEvent(0);
4961         return 1; /* keep on going */
4962       }
4963     return 0; /* stop timer */
4964 }
4965
4966 void
4967 StartAnalysisClock()
4968 {
4969   analysisClockTag =
4970     g_timeout_add( 2000,(GSourceFunc) AnalysisClockCallback, NULL);
4971   return;
4972 }
4973
4974 guint clockTimerTag = 0;
4975
4976 int ClockTimerRunning()
4977 {
4978     return clockTimerTag != 0;
4979 }
4980
4981 int StopClockTimer()
4982 {
4983     if (clockTimerTag != 0)
4984       {
4985         g_source_remove(clockTimerTag);
4986         clockTimerTag = 0;
4987         return TRUE;
4988       }
4989     else
4990       {
4991         return FALSE;
4992       }
4993 }
4994
4995 void
4996 ClockTimerCallback(data)
4997      gpointer data;
4998 {
4999   /* remove timer */
5000   g_source_remove(clockTimerTag);
5001   clockTimerTag = 0;
5002
5003   DecrementClocks();
5004   return;
5005 }
5006
5007 void
5008 StartClockTimer(millisec)
5009      long millisec;
5010 {
5011   clockTimerTag = g_timeout_add(millisec,(GSourceFunc) ClockTimerCallback,NULL);
5012   return;
5013 }
5014
5015 void
5016 DisplayTimerLabel(w, color, timer, highlight)
5017      GtkWidget *w;
5018      char *color;
5019      long timer;
5020      int highlight;
5021 {
5022   gchar buf[MSG_SIZ];
5023
5024
5025   if (appData.clockMode) {
5026     sprintf(buf, "%s: %s", color, TimeString(timer));
5027   } else {
5028     sprintf(buf, "%s  ", color);
5029   }
5030   gtk_label_set_text(GTK_LABEL(w),buf);
5031
5032   /* check for low time warning */
5033 //    Pixel foregroundOrWarningColor = timerForegroundPixel;
5034
5035 //    if (timer > 0 &&
5036 //        appData.lowTimeWarning &&
5037 //        (timer / 1000) < appData.icsAlarmTime)
5038 //      foregroundOrWarningColor = lowTimeWarningColor;
5039 //
5040 //    if (appData.clockMode) {
5041 //      sprintf(buf, "%s: %s", color, TimeString(timer));
5042 //      XtSetArg(args[0], XtNlabel, buf);
5043 //    } else {
5044 //      sprintf(buf, "%s  ", color);
5045 //      XtSetArg(args[0], XtNlabel, buf);
5046 //    }
5047 //
5048 //    if (highlight) {
5049 //
5050 //      XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
5051 //      XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
5052 //    } else {
5053 //      XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
5054 //      XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
5055 //    }
5056 //
5057 //    XtSetValues(w, args, 3);
5058 //
5059 }
5060
5061 void
5062 DisplayWhiteClock(timeRemaining, highlight)
5063      long timeRemaining;
5064      int highlight;
5065 {
5066   if(appData.noGUI) return;
5067
5068   DisplayTimerLabel(GUI_Whiteclock, _("White"), timeRemaining, highlight);
5069   if (highlight && WindowIcon == BlackIcon)
5070     {
5071       WindowIcon = WhiteIcon;
5072       gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5073     }
5074 }
5075
5076 void
5077 DisplayBlackClock(timeRemaining, highlight)
5078      long timeRemaining;
5079      int highlight;
5080 {
5081     if(appData.noGUI) return;
5082
5083     DisplayTimerLabel(GUI_Blackclock, _("Black"), timeRemaining, highlight);
5084     if (highlight && WindowIcon == WhiteIcon)
5085       {
5086         WindowIcon = BlackIcon;
5087         gtk_window_set_icon(GTK_WINDOW(GUI_Window),WindowIcon);
5088       }
5089 }
5090
5091 #define CPNone 0
5092 #define CPReal 1
5093 #define CPComm 2
5094 #define CPSock 3
5095 #define CPLoop 4
5096 typedef int CPKind;
5097
5098 typedef struct {
5099     CPKind kind;
5100     int pid;
5101     int fdTo, fdFrom;
5102 } ChildProc;
5103
5104
5105 int StartChildProcess(cmdLine, dir, pr)
5106      char *cmdLine;
5107      char *dir;
5108      ProcRef *pr;
5109 {
5110     char *argv[64], *p;
5111     int i, pid;
5112     int to_prog[2], from_prog[2];
5113     ChildProc *cp;
5114     char buf[MSG_SIZ];
5115
5116     if (appData.debugMode) {
5117         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
5118     }
5119
5120     /* We do NOT feed the cmdLine to the shell; we just
5121        parse it into blank-separated arguments in the
5122        most simple-minded way possible.
5123        */
5124     i = 0;
5125     strcpy(buf, cmdLine);
5126     p = buf;
5127     for (;;) {
5128         while(*p == ' ') p++;
5129         argv[i++] = p;
5130         if(*p == '"' || *p == '\'')
5131              p = strchr(++argv[i-1], *p);
5132         else p = strchr(p, ' ');
5133         if (p == NULL) break;
5134         *p++ = NULLCHAR;
5135     }
5136     argv[i] = NULL;
5137
5138     SetUpChildIO(to_prog, from_prog);
5139
5140     if ((pid = fork()) == 0) {
5141         /* Child process */
5142         // [HGM] PSWBTM: made order resistant against case where fd of created pipe was 0 or 1
5143         close(to_prog[1]);     // first close the unused pipe ends
5144         close(from_prog[0]);
5145         dup2(to_prog[0], 0);   // to_prog was created first, nd is the only one to use 0 or 1
5146         dup2(from_prog[1], 1);
5147         if(to_prog[0] >= 2) close(to_prog[0]); // if 0 or 1, the dup2 already cosed the original
5148         close(from_prog[1]);                   // and closing again loses one of the pipes!
5149         if(fileno(stderr) >= 2) // better safe than sorry...
5150                 dup2(1, fileno(stderr)); /* force stderr to the pipe */
5151
5152         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
5153             perror(dir);
5154             exit(1);
5155         }
5156
5157         nice(appData.niceEngines); // [HGM] nice: adjust priority of engine proc
5158
5159         execvp(argv[0], argv);
5160
5161         /* If we get here, exec failed */
5162         perror(argv[0]);
5163         exit(1);
5164     }
5165
5166     /* Parent process */
5167     close(to_prog[0]);
5168     close(from_prog[1]);
5169
5170     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5171     cp->kind = CPReal;
5172     cp->pid = pid;
5173     cp->fdFrom = from_prog[0];
5174     cp->fdTo = to_prog[1];
5175     *pr = (ProcRef) cp;
5176     return 0;
5177 }
5178
5179 // [HGM] kill: implement the 'hard killing' of AS's Winboard_x
5180 static RETSIGTYPE AlarmCallBack(int n)
5181 {
5182     return;
5183 }
5184
5185 void
5186 DestroyChildProcess(pr, signalType)
5187      ProcRef pr;
5188      int signalType;
5189 {
5190     ChildProc *cp = (ChildProc *) pr;
5191
5192     if (cp->kind != CPReal) return;
5193     cp->kind = CPNone;
5194     if (signalType == 10) { // [HGM] kill: if it does not terminate in 3 sec, kill
5195         signal(SIGALRM, AlarmCallBack);
5196         alarm(3);
5197         if(wait((int *) 0) == -1) { // process does not terminate on its own accord
5198             kill(cp->pid, SIGKILL); // kill it forcefully
5199             wait((int *) 0);        // and wait again
5200         }
5201     } else {
5202         if (signalType) {
5203             kill(cp->pid, signalType == 9 ? SIGKILL : SIGTERM); // [HGM] kill: use hard kill if so requested
5204         }
5205         /* Process is exiting either because of the kill or because of
5206            a quit command sent by the backend; either way, wait for it to die.
5207         */
5208         wait((int *) 0);
5209     }
5210     close(cp->fdFrom);
5211     close(cp->fdTo);
5212 }
5213
5214 void
5215 InterruptChildProcess(pr)
5216      ProcRef pr;
5217 {
5218     ChildProc *cp = (ChildProc *) pr;
5219
5220     if (cp->kind != CPReal) return;
5221     (void) kill(cp->pid, SIGINT); /* stop it thinking */
5222 }
5223
5224 int OpenTelnet(host, port, pr)
5225      char *host;
5226      char *port;
5227      ProcRef *pr;
5228 {
5229     char cmdLine[MSG_SIZ];
5230
5231     if (port[0] == NULLCHAR) {
5232       snprintf(cmdLine, sizeof(cmdLine), "%s %s", appData.telnetProgram, host);
5233     } else {
5234       snprintf(cmdLine, sizeof(cmdLine), "%s %s %s", appData.telnetProgram, host, port);
5235     }
5236     return StartChildProcess(cmdLine, "", pr);
5237 }
5238
5239 int OpenTCP(host, port, pr)
5240      char *host;
5241      char *port;
5242      ProcRef *pr;
5243 {
5244 #if OMIT_SOCKETS
5245     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
5246 #else  /* !OMIT_SOCKETS */
5247     int s;
5248     struct sockaddr_in sa;
5249     struct hostent     *hp;
5250     unsigned short uport;
5251     ChildProc *cp;
5252
5253     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
5254         return errno;
5255     }
5256
5257     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5258     sa.sin_family = AF_INET;
5259     sa.sin_addr.s_addr = INADDR_ANY;
5260     uport = (unsigned short) 0;
5261     sa.sin_port = htons(uport);
5262     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
5263         return errno;
5264     }
5265
5266     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
5267     if (!(hp = gethostbyname(host))) {
5268         int b0, b1, b2, b3;
5269         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
5270             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
5271             hp->h_addrtype = AF_INET;
5272             hp->h_length = 4;
5273             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
5274             hp->h_addr_list[0] = (char *) malloc(4);
5275             hp->h_addr_list[0][0] = b0;
5276             hp->h_addr_list[0][1] = b1;
5277             hp->h_addr_list[0][2] = b2;
5278             hp->h_addr_list[0][3] = b3;
5279         } else {
5280             return ENOENT;
5281         }
5282     }
5283     sa.sin_family = hp->h_addrtype;
5284     uport = (unsigned short) atoi(port);
5285     sa.sin_port = htons(uport);
5286     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
5287
5288     if (connect(s, (struct sockaddr *) &sa,
5289                 sizeof(struct sockaddr_in)) < 0) {
5290         return errno;
5291     }
5292
5293     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5294     cp->kind = CPSock;
5295     cp->pid = 0;
5296     cp->fdFrom = s;
5297     cp->fdTo = s;
5298     *pr = (ProcRef) cp;
5299
5300 #endif /* !OMIT_SOCKETS */
5301
5302     return 0;
5303 }
5304
5305 int OpenCommPort(name, pr)
5306      char *name;
5307      ProcRef *pr;
5308 {
5309     int fd;
5310     ChildProc *cp;
5311
5312     fd = open(name, 2, 0);
5313     if (fd < 0) return errno;
5314
5315     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5316     cp->kind = CPComm;
5317     cp->pid = 0;
5318     cp->fdFrom = fd;
5319     cp->fdTo = fd;
5320     *pr = (ProcRef) cp;
5321
5322     return 0;
5323 }
5324
5325 int OpenLoopback(pr)
5326      ProcRef *pr;
5327 {
5328     ChildProc *cp;
5329     int to[2], from[2];
5330
5331     SetUpChildIO(to, from);
5332
5333     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
5334     cp->kind = CPLoop;
5335     cp->pid = 0;
5336     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
5337     cp->fdTo = to[1];
5338     *pr = (ProcRef) cp;
5339
5340     return 0;
5341 }
5342
5343 int OpenRcmd(host, user, cmd, pr)
5344      char *host, *user, *cmd;
5345      ProcRef *pr;
5346 {
5347     DisplayFatalError(_("internal rcmd not implemented for Unix"), 0, 1);
5348     return -1;
5349 }
5350
5351 #define INPUT_SOURCE_BUF_SIZE 8192
5352
5353 typedef struct {
5354     CPKind kind;
5355     int fd;
5356     int lineByLine;
5357     char *unused;
5358     InputCallback func;
5359     guint sid;
5360     char buf[INPUT_SOURCE_BUF_SIZE];
5361     VOIDSTAR closure;
5362 } InputSource;
5363
5364 void
5365 DoInputCallback(io,cond,data)
5366      GIOChannel   *io;
5367      GIOCondition  cond;
5368      gpointer *data;
5369 {
5370   /* read input from one of the input source (for example a chess program, ICS, etc).
5371    * and call a function that will handle the input
5372    */
5373
5374   int count; /* how many bytes did we read */
5375   int error; 
5376   char *p, *q;
5377   
5378   /* All information (callback function, file descriptor, etc) is
5379    * saved in an InputSource structure 
5380    */
5381   InputSource *is = (InputSource *) data; 
5382   
5383   if (is->lineByLine) 
5384     {
5385       count = read(is->fd, is->unused,
5386                    INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
5387
5388       if (count <= 0) 
5389         {
5390           (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
5391           return;
5392         }
5393       is->unused += count;
5394       p = is->buf;
5395       /* break input into lines and call the callback function on each
5396        * line 
5397        */
5398       while (p < is->unused) 
5399         {
5400           q = memchr(p, '\n', is->unused - p);
5401           if (q == NULL) break;
5402           q++;
5403           (is->func)(is, is->closure, p, q - p, 0);
5404           p = q;
5405         }
5406       /* remember not yet used part of the buffer */
5407       q = is->buf;
5408       while (p < is->unused) 
5409         {
5410           *q++ = *p++;
5411         }
5412       is->unused = q;
5413     }
5414   else 
5415     {
5416       /* read maximum length of input buffer and send the whole buffer
5417        * to the callback function 
5418        */
5419       count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
5420       if (count == -1)
5421         error = errno;
5422       else
5423         error = 0;
5424       (is->func)(is, is->closure, is->buf, count, error);
5425     }
5426   
5427   return;
5428 }
5429
5430 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
5431      ProcRef pr;
5432      int lineByLine;
5433      InputCallback func;
5434      VOIDSTAR closure;
5435 {
5436     InputSource *is;
5437     GIOChannel *channel;
5438     ChildProc *cp = (ChildProc *) pr;
5439
5440     is = (InputSource *) calloc(1, sizeof(InputSource));
5441     is->lineByLine = lineByLine;
5442     is->func = func;
5443     if (pr == NoProc) {
5444         is->kind = CPReal;
5445         is->fd = fileno(stdin);
5446     } else {
5447         is->kind = cp->kind;
5448         is->fd = cp->fdFrom;
5449     }
5450     if (lineByLine) 
5451       is->unused = is->buf;
5452     else
5453       is->unused = NULL;
5454
5455 //    is->xid = XtAppAddInput(appContext, is->fd,
5456 //                          (XtPointer) (XtInputReadMask),
5457 //                          (XtInputCallbackProc) DoInputCallback,
5458 //                          (XtPointer) is);
5459 //
5460
5461     /* TODO: will this work on windows?*/
5462
5463     channel = g_io_channel_unix_new(is->fd);
5464     g_io_channel_set_close_on_unref (channel, TRUE);
5465     is->sid = g_io_add_watch(channel, G_IO_IN,(GIOFunc) DoInputCallback, is);
5466     is->closure = closure;
5467     return (InputSourceRef) is;
5468 }
5469
5470 void
5471 RemoveInputSource(isr)
5472      InputSourceRef isr;
5473 {
5474     InputSource *is = (InputSource *) isr;
5475
5476     if (is->sid == 0) return;
5477     g_source_remove(is->sid);
5478     is->sid = 0;
5479     return;
5480 }
5481
5482 int OutputToProcess(pr, message, count, outError)
5483      ProcRef pr;
5484      char *message;
5485      int count;
5486      int *outError;
5487 {
5488     static int line = 0;
5489     ChildProc *cp = (ChildProc *) pr;
5490     int outCount;
5491
5492     if (pr == NoProc)
5493     {
5494         if (appData.noJoin || !appData.useInternalWrap)
5495             outCount = fwrite(message, 1, count, stdout);
5496         else
5497         {
5498             int width = get_term_width();
5499             int len = wrap(NULL, message, count, width, &line);
5500             char *msg = malloc(len);
5501             int dbgchk;
5502
5503             if (!msg)
5504                 outCount = fwrite(message, 1, count, stdout);
5505             else
5506             {
5507                 dbgchk = wrap(msg, message, count, width, &line);
5508                 if (dbgchk != len && appData.debugMode)
5509                     fprintf(debugFP, "wrap(): dbgchk(%d) != len(%d)\n", dbgchk, len);
5510                 outCount = fwrite(msg, 1, dbgchk, stdout);
5511                 free(msg);
5512             }
5513         }
5514     }
5515     else
5516       outCount = write(cp->fdTo, message, count);
5517
5518     if (outCount == -1)
5519       *outError = errno;
5520     else
5521       *outError = 0;
5522
5523     return outCount;
5524 }
5525
5526 /* Output message to process, with "ms" milliseconds of delay
5527    between each character. This is needed when sending the logon
5528    script to ICC, which for some reason doesn't like the
5529    instantaneous send. */
5530 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
5531      ProcRef pr;
5532      char *message;
5533      int count;
5534      int *outError;
5535      long msdelay;
5536 {
5537     ChildProc *cp = (ChildProc *) pr;
5538     int outCount = 0;
5539     int r;
5540
5541     while (count--) {
5542         r = write(cp->fdTo, message++, 1);
5543         if (r == -1) {
5544             *outError = errno;
5545             return outCount;
5546         }
5547         ++outCount;
5548         if (msdelay >= 0)
5549           TimeDelay(msdelay);
5550     }
5551
5552     return outCount;
5553 }
5554
5555 /****   Animation code by Hugh Fisher, DCS, ANU.
5556
5557         Known problem: if a window overlapping the board is
5558         moved away while a piece is being animated underneath,
5559         the newly exposed area won't be updated properly.
5560         I can live with this.
5561
5562         Known problem: if you look carefully at the animation
5563         of pieces in mono mode, they are being drawn as solid
5564         shapes without interior detail while moving. Fixing
5565         this would be a major complication for minimal return.
5566 ****/
5567
5568 /*      Masks for XPM pieces. Black and white pieces can have
5569         different shapes, but in the interest of retaining my
5570         sanity pieces must have the same outline on both light
5571         and dark squares, and all pieces must use the same
5572         background square colors/images.                */
5573
5574 static int xpmDone = 0;
5575
5576 static void
5577 CreateAnimMasks (pieceDepth)
5578      int pieceDepth;
5579 {
5580   ChessSquare   piece;
5581   Pixmap        buf;
5582   GC            bufGC, maskGC;
5583   int           kind, n;
5584   unsigned long plane;
5585   XGCValues     values;
5586
5587   /* just return for gtk at the moment */
5588   return;
5589
5590   /* Need a bitmap just to get a GC with right depth */
5591 //  buf = XCreatePixmap(xDisplay, xBoardWindow,
5592 //                      8, 8, 1);
5593   values.foreground = 1;
5594   values.background = 0;
5595   /* Don't use XtGetGC, not read only */
5596 //  maskGC = XCreateGC(xDisplay, buf,
5597 //                  GCForeground | GCBackground, &values);
5598 //  XFreePixmap(xDisplay, buf);
5599 //
5600 //  buf = XCreatePixmap(xDisplay, xBoardWindow,
5601 //                    squareSize, squareSize, pieceDepth);
5602 //  values.foreground = XBlackPixel(xDisplay, xScreen);
5603 //  values.background = XWhitePixel(xDisplay, xScreen);
5604 //  bufGC = XCreateGC(xDisplay, buf,
5605 //                  GCForeground | GCBackground, &values);
5606 //
5607   for (piece = WhitePawn; piece <= BlackKing; piece++) {
5608     /* Begin with empty mask */
5609 //    if(!xpmDone) // [HGM] pieces: keep using existing
5610 //    xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
5611 //                               squareSize, squareSize, 1);
5612 //    XSetFunction(xDisplay, maskGC, GXclear);
5613 //    XFillRectangle(xDisplay, xpmMask[piece], maskGC,
5614 //                 0, 0, squareSize, squareSize);
5615 //
5616     /* Take a copy of the piece */
5617     if (White(piece))
5618       kind = 0;
5619     else
5620       kind = 2;
5621 //    XSetFunction(xDisplay, bufGC, GXcopy);
5622 //    XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % (int)BlackPawn],
5623 //            buf, bufGC,
5624 //            0, 0, squareSize, squareSize, 0, 0);
5625
5626     /* XOR the background (light) over the piece */
5627 //    XSetFunction(xDisplay, bufGC, GXxor);
5628 //    if (useImageSqs)
5629 //      XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
5630 //              0, 0, squareSize, squareSize, 0, 0);
5631 //    else {
5632 //      XSetForeground(xDisplay, bufGC, lightSquareColor);
5633 //      XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
5634 //    }
5635
5636     /* We now have an inverted piece image with the background
5637        erased. Construct mask by just selecting all the non-zero
5638        pixels - no need to reconstruct the original image.      */
5639     //    XSetFunction(xDisplay, maskGC, GXor);
5640     plane = 1;
5641     /* Might be quicker to download an XImage and create bitmap
5642        data from it rather than this N copies per piece, but it
5643        only takes a fraction of a second and there is a much
5644        longer delay for loading the pieces.             */
5645 //    for (n = 0; n < pieceDepth; n ++) {
5646 //      XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
5647 //               0, 0, squareSize, squareSize,
5648 //               0, 0, plane);
5649 //      plane = plane << 1;
5650 //    }
5651   }
5652   /* Clean up */
5653 //  XFreePixmap(xDisplay, buf);
5654 //  XFreeGC(xDisplay, bufGC);
5655 //  XFreeGC(xDisplay, maskGC);
5656 }
5657
5658 static void
5659 InitAnimState (anim, info)
5660   AnimState * anim;
5661   XWindowAttributes * info;
5662 {
5663   XtGCMask  mask;
5664   XGCValues values;
5665
5666   /* Each buffer is square size, same depth as window */
5667 //  anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
5668 //                      squareSize, squareSize, info->depth);
5669 //  anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
5670 //                      squareSize, squareSize, info->depth);
5671 //
5672 //  /* Create a plain GC for blitting */
5673 //  mask = GCForeground | GCBackground | GCFunction |
5674 //         GCPlaneMask | GCGraphicsExposures;
5675 //  values.foreground = XBlackPixel(xDisplay, xScreen);
5676 //  values.background = XWhitePixel(xDisplay, xScreen);
5677 //  values.function   = GXcopy;
5678 //  values.plane_mask = AllPlanes;
5679 //  values.graphics_exposures = False;
5680 //  anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
5681 //
5682 //  /* Piece will be copied from an existing context at
5683 //     the start of each new animation/drag. */
5684 //  anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
5685 //
5686 //  /* Outline will be a read-only copy of an existing */
5687 //  anim->outlineGC = None;
5688 }
5689
5690 static void
5691 CreateAnimVars ()
5692 {
5693   static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
5694   XWindowAttributes info;
5695
5696   /* for gtk at the moment just ... */
5697   return;
5698
5699   if (xpmDone && gameInfo.variant == old) return;
5700   if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
5701   //  XGetWindowAttributes(xDisplay, xBoardWindow, &info);
5702
5703   //  InitAnimState(&game, &info);
5704   //  InitAnimState(&player, &info);
5705
5706   /* For XPM pieces, we need bitmaps to use as masks. */
5707   //  if (useImages)
5708   //    CreateAnimMasks(info.depth);
5709    xpmDone = 1;
5710 }
5711
5712 #ifndef HAVE_USLEEP
5713
5714 static Boolean frameWaiting;
5715
5716 static RETSIGTYPE FrameAlarm (sig)
5717      int sig;
5718 {
5719   frameWaiting = False;
5720   /* In case System-V style signals.  Needed?? */
5721   signal(SIGALRM, FrameAlarm);
5722 }
5723
5724 static void
5725 FrameDelay (time)
5726      int time;
5727 {
5728   struct itimerval delay;
5729
5730   XSync(xDisplay, False);
5731
5732   if (time > 0) {
5733     frameWaiting = True;
5734     signal(SIGALRM, FrameAlarm);
5735     delay.it_interval.tv_sec =
5736       delay.it_value.tv_sec = time / 1000;
5737     delay.it_interval.tv_usec =
5738       delay.it_value.tv_usec = (time % 1000) * 1000;
5739     setitimer(ITIMER_REAL, &delay, NULL);
5740     while (frameWaiting) pause();
5741     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
5742     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
5743     setitimer(ITIMER_REAL, &delay, NULL);
5744   }
5745 }
5746
5747 #else
5748
5749 static void
5750 FrameDelay (time)
5751      int time;
5752 {
5753   //  XSync(xDisplay, False);
5754   if (time > 0)
5755     usleep(time * 1000);
5756 }
5757
5758 #endif
5759
5760 /*      Convert board position to corner of screen rect and color       */
5761
5762 static void
5763 ScreenSquare(column, row, pt, color)
5764      int column; int row; XPoint * pt; int * color;
5765 {
5766   if (flipView) {
5767     pt->x = lineGap + ((BOARD_WIDTH-1)-column) * (squareSize + lineGap);
5768     pt->y = lineGap + row * (squareSize + lineGap);
5769   } else {
5770     pt->x = lineGap + column * (squareSize + lineGap);
5771     pt->y = lineGap + ((BOARD_HEIGHT-1)-row) * (squareSize + lineGap);
5772   }
5773   *color = SquareColor(row, column);
5774 }
5775
5776 /*      Convert window coords to square                 */
5777
5778 static void
5779 BoardSquare(x, y, column, row)
5780      int x; int y; int * column; int * row;
5781 {
5782   *column = EventToSquare(x, BOARD_WIDTH);
5783   if (flipView && *column >= 0)
5784     *column = BOARD_WIDTH - 1 - *column;
5785   *row = EventToSquare(y, BOARD_HEIGHT);
5786   if (!flipView && *row >= 0)
5787     *row = BOARD_HEIGHT - 1 - *row;
5788 }
5789
5790 /*   Utilities  */
5791
5792 #undef Max  /* just in case */
5793 #undef Min
5794 #define Max(a, b) ((a) > (b) ? (a) : (b))
5795 #define Min(a, b) ((a) < (b) ? (a) : (b))
5796
5797 static void
5798 SetRect(rect, x, y, width, height)
5799      XRectangle * rect; int x; int y; int width; int height;
5800 {
5801   rect->x = x;
5802   rect->y = y;
5803   rect->width  = width;
5804   rect->height = height;
5805 }
5806
5807 /*      Test if two frames overlap. If they do, return
5808         intersection rect within old and location of
5809         that rect within new. */
5810
5811 static Boolean
5812 Intersect(old, new, size, area, pt)
5813      XPoint * old; XPoint * new;
5814      int size; XRectangle * area; XPoint * pt;
5815 {
5816   if (old->x > new->x + size || new->x > old->x + size ||
5817       old->y > new->y + size || new->y > old->y + size) {
5818     return False;
5819   } else {
5820     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
5821             size - abs(old->x - new->x), size - abs(old->y - new->y));
5822     pt->x = Max(old->x - new->x, 0);
5823     pt->y = Max(old->y - new->y, 0);
5824     return True;
5825   }
5826 }
5827
5828 /*      For two overlapping frames, return the rect(s)
5829         in the old that do not intersect with the new.   */
5830
5831 static void
5832 CalcUpdateRects(old, new, size, update, nUpdates)
5833      XPoint * old; XPoint * new; int size;
5834      XRectangle update[]; int * nUpdates;
5835 {
5836   int        count;
5837
5838   /* If old = new (shouldn't happen) then nothing to draw */
5839   if (old->x == new->x && old->y == new->y) {
5840     *nUpdates = 0;
5841     return;
5842   }
5843   /* Work out what bits overlap. Since we know the rects
5844      are the same size we don't need a full intersect calc. */
5845   count = 0;
5846   /* Top or bottom edge? */
5847   if (new->y > old->y) {
5848     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
5849     count ++;
5850   } else if (old->y > new->y) {
5851     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
5852                               size, old->y - new->y);
5853     count ++;
5854   }
5855   /* Left or right edge - don't overlap any update calculated above. */
5856   if (new->x > old->x) {
5857     SetRect(&(update[count]), old->x, Max(new->y, old->y),
5858                               new->x - old->x, size - abs(new->y - old->y));
5859     count ++;
5860   } else if (old->x > new->x) {
5861     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
5862                               old->x - new->x, size - abs(new->y - old->y));
5863     count ++;
5864   }
5865   /* Done */
5866   *nUpdates = count;
5867 }
5868
5869 /*      Generate a series of frame coords from start->mid->finish.
5870         The movement rate doubles until the half way point is
5871         reached, then halves back down to the final destination,
5872         which gives a nice slow in/out effect. The algorithmn
5873         may seem to generate too many intermediates for short
5874         moves, but remember that the purpose is to attract the
5875         viewers attention to the piece about to be moved and
5876         then to where it ends up. Too few frames would be less
5877         noticeable.                                             */
5878
5879 static void
5880 Tween(start, mid, finish, factor, frames, nFrames)
5881      XPoint * start; XPoint * mid;
5882      XPoint * finish; int factor;
5883      XPoint frames[]; int * nFrames;
5884 {
5885   int fraction, n, count;
5886
5887   count = 0;
5888
5889   /* Slow in, stepping 1/16th, then 1/8th, ... */
5890   fraction = 1;
5891   for (n = 0; n < factor; n++)
5892     fraction *= 2;
5893   for (n = 0; n < factor; n++) {
5894     frames[count].x = start->x + (mid->x - start->x) / fraction;
5895     frames[count].y = start->y + (mid->y - start->y) / fraction;
5896     count ++;
5897     fraction = fraction / 2;
5898   }
5899
5900   /* Midpoint */
5901   frames[count] = *mid;
5902   count ++;
5903
5904   /* Slow out, stepping 1/2, then 1/4, ... */
5905   fraction = 2;
5906   for (n = 0; n < factor; n++) {
5907     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
5908     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
5909     count ++;
5910     fraction = fraction * 2;
5911   }
5912   *nFrames = count;
5913 }
5914
5915 /*      Draw a piece on the screen without disturbing what's there      */
5916
5917 static void
5918 SelectGCMask(piece, clip, outline, mask)
5919      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
5920 {
5921   GC source;
5922
5923   /* Bitmap for piece being moved. */
5924   if (appData.monoMode) {
5925       *mask = *pieceToSolid(piece);
5926   } else if (useImages) {
5927 #if HAVE_LIBXPM
5928       *mask = xpmMask[piece];
5929 #else
5930       *mask = ximMaskPm[piece];
5931 #endif
5932   } else {
5933       *mask = *pieceToSolid(piece);
5934   }
5935
5936   /* GC for piece being moved. Square color doesn't matter, but
5937      since it gets modified we make a copy of the original. */
5938   if (White(piece)) {
5939     if (appData.monoMode)
5940       source = bwPieceGC;
5941     else
5942       source = wlPieceGC;
5943   } else {
5944     if (appData.monoMode)
5945       source = wbPieceGC;
5946     else
5947       source = blPieceGC;
5948   }
5949   //  XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
5950
5951   /* Outline only used in mono mode and is not modified */
5952   if (White(piece))
5953     *outline = bwPieceGC;
5954   else
5955     *outline = wbPieceGC;
5956 }
5957
5958 static void
5959 OverlayPiece(piece, clip, outline,  dest)
5960      ChessSquare piece; GC clip; GC outline; Drawable dest;
5961 {
5962   int   kind;
5963
5964   if (!useImages) {
5965     /* Draw solid rectangle which will be clipped to shape of piece */
5966 //    XFillRectangle(xDisplay, dest, clip,
5967 //                 0, 0, squareSize, squareSize)
5968 ;
5969     if (appData.monoMode)
5970       /* Also draw outline in contrasting color for black
5971          on black / white on white cases                */
5972 //      XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
5973 //               0, 0, squareSize, squareSize, 0, 0, 1)
5974 ;
5975   } else {
5976     /* Copy the piece */
5977     if (White(piece))
5978       kind = 0;
5979     else
5980       kind = 2;
5981 //    XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
5982 //            dest, clip,
5983 //            0, 0, squareSize, squareSize,
5984 //            0, 0);
5985   }
5986 }
5987
5988 /* Animate the movement of a single piece */
5989
5990 static void
5991 BeginAnimation(anim, piece, startColor, start)
5992      AnimState *anim;
5993      ChessSquare piece;
5994      int startColor;
5995      XPoint * start;
5996 {
5997   Pixmap mask;
5998
5999   /* The old buffer is initialised with the start square (empty) */
6000   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
6001   anim->prevFrame = *start;
6002
6003   /* The piece will be drawn using its own bitmap as a matte    */
6004 //  SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
6005 //  XSetClipMask(xDisplay, anim->pieceGC, mask);
6006 }
6007
6008 static void
6009 AnimationFrame(anim, frame, piece)
6010      AnimState *anim;
6011      XPoint *frame;
6012      ChessSquare piece;
6013 {
6014   XRectangle updates[4];
6015   XRectangle overlap;
6016   XPoint     pt;
6017   int        count, i;
6018
6019   /* Save what we are about to draw into the new buffer */
6020 //  XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
6021 //          frame->x, frame->y, squareSize, squareSize,
6022 //          0, 0);
6023
6024   /* Erase bits of the previous frame */
6025   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
6026     /* Where the new frame overlapped the previous,
6027        the contents in newBuf are wrong. */
6028 //    XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
6029 //            overlap.x, overlap.y,
6030 //            overlap.width, overlap.height,
6031 //            pt.x, pt.y);
6032     /* Repaint the areas in the old that don't overlap new */
6033     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
6034     for (i = 0; i < count; i++)
6035 //      XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6036 //              updates[i].x - anim->prevFrame.x,
6037 //              updates[i].y - anim->prevFrame.y,
6038 //              updates[i].width, updates[i].height,
6039 //              updates[i].x, updates[i].y)
6040 ;
6041   } else {
6042     /* Easy when no overlap */
6043 //    XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6044 //                0, 0, squareSize, squareSize,
6045 //                anim->prevFrame.x, anim->prevFrame.y);
6046   }
6047
6048   /* Save this frame for next time round */
6049 //  XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
6050 //              0, 0, squareSize, squareSize,
6051 //              0, 0);
6052   anim->prevFrame = *frame;
6053
6054   /* Draw piece over original screen contents, not current,
6055      and copy entire rect. Wipes out overlapping piece images. */
6056   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
6057 //  XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
6058 //              0, 0, squareSize, squareSize,
6059 //              frame->x, frame->y);
6060 }
6061
6062 static void
6063 EndAnimation (anim, finish)
6064      AnimState *anim;
6065      XPoint *finish;
6066 {
6067   XRectangle updates[4];
6068   XRectangle overlap;
6069   XPoint     pt;
6070   int        count, i;
6071
6072   /* The main code will redraw the final square, so we
6073      only need to erase the bits that don't overlap.    */
6074   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
6075     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
6076     for (i = 0; i < count; i++)
6077 //      XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6078 //              updates[i].x - anim->prevFrame.x,
6079 //              updates[i].y - anim->prevFrame.y,
6080 //              updates[i].width, updates[i].height,
6081 //              updates[i].x, updates[i].y)
6082 ;
6083   } else {
6084 //    XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
6085 //              0, 0, squareSize, squareSize,
6086 //              anim->prevFrame.x, anim->prevFrame.y);
6087   }
6088 }
6089
6090 static void
6091 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
6092      AnimState *anim;
6093      ChessSquare piece; int startColor;
6094      XPoint * start; XPoint * finish;
6095      XPoint frames[]; int nFrames;
6096 {
6097   int n;
6098
6099   BeginAnimation(anim, piece, startColor, start);
6100   for (n = 0; n < nFrames; n++) {
6101     AnimationFrame(anim, &(frames[n]), piece);
6102     FrameDelay(appData.animSpeed);
6103   }
6104   EndAnimation(anim, finish);
6105 }
6106
6107 /* Main control logic for deciding what to animate and how */
6108
6109 void
6110 AnimateMove(board, fromX, fromY, toX, toY)
6111      Board board;
6112      int fromX;
6113      int fromY;
6114      int toX;
6115      int toY;
6116 {
6117   ChessSquare piece;
6118   int hop;
6119   XPoint      start, finish, mid;
6120   XPoint      frames[kFactor * 2 + 1];
6121   int         nFrames, startColor, endColor;
6122
6123   /* Are we animating? */
6124   if (!appData.animate || appData.blindfold)
6125     return;
6126
6127   if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
6128      board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
6129         return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
6130
6131   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
6132   piece = board[fromY][fromX];
6133   if (piece >= EmptySquare) return;
6134
6135 #if DONT_HOP
6136   hop = FALSE;
6137 #else
6138   hop = (piece == WhiteKnight || piece == BlackKnight);
6139 #endif
6140
6141   if (appData.debugMode) {
6142       fprintf(debugFP, hop ? _("AnimateMove: piece %d hops from %d,%d to %d,%d \n") :
6143                              _("AnimateMove: piece %d slides from %d,%d to %d,%d \n"),
6144              piece, fromX, fromY, toX, toY);  }
6145
6146   ScreenSquare(fromX, fromY, &start, &startColor);
6147   ScreenSquare(toX, toY, &finish, &endColor);
6148
6149   if (hop) {
6150     /* Knight: make diagonal movement then straight */
6151     if (abs(toY - fromY) < abs(toX - fromX)) {
6152        mid.x = start.x + (finish.x - start.x) / 2;
6153        mid.y = finish.y;
6154      } else {
6155        mid.x = finish.x;
6156        mid.y = start.y + (finish.y - start.y) / 2;
6157      }
6158   } else {
6159     mid.x = start.x + (finish.x - start.x) / 2;
6160     mid.y = start.y + (finish.y - start.y) / 2;
6161   }
6162
6163   /* Don't use as many frames for very short moves */
6164   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
6165     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
6166   else
6167     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
6168   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
6169
6170   /* Be sure end square is redrawn */
6171   damage[toY][toX] = True;
6172 }
6173
6174 void
6175 DragPieceBegin(x, y)
6176      int x; int y;
6177 {
6178     int  boardX, boardY, color;
6179     XPoint corner;
6180
6181     /* Are we animating? */
6182     if (!appData.animateDragging || appData.blindfold)
6183       return;
6184
6185     /* Figure out which square we start in and the
6186        mouse position relative to top left corner. */
6187     BoardSquare(x, y, &boardX, &boardY);
6188     player.startBoardX = boardX;
6189     player.startBoardY = boardY;
6190     ScreenSquare(boardX, boardY, &corner, &color);
6191     player.startSquare  = corner;
6192     player.startColor   = color;
6193     /* As soon as we start dragging, the piece will jump slightly to
6194        be centered over the mouse pointer. */
6195     player.mouseDelta.x = squareSize/2;
6196     player.mouseDelta.y = squareSize/2;
6197     /* Initialise animation */
6198     player.dragPiece = PieceForSquare(boardX, boardY);
6199     /* Sanity check */
6200     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
6201         player.dragActive = True;
6202         BeginAnimation(&player, player.dragPiece, color, &corner);
6203         /* Mark this square as needing to be redrawn. Note that
6204            we don't remove the piece though, since logically (ie
6205            as seen by opponent) the move hasn't been made yet. */
6206            if(boardX == BOARD_RGHT+1 && PieceForSquare(boardX-1, boardY) > 1 ||
6207               boardX == BOARD_LEFT-2 && PieceForSquare(boardX+1, boardY) > 1)
6208 //           XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
6209 //                   corner.x, corner.y, squareSize, squareSize,
6210 //                   0, 0); // [HGM] zh: unstack in stead of grab
6211         damage[boardY][boardX] = True;
6212     } else {
6213         player.dragActive = False;
6214     }
6215 }
6216
6217 static void
6218 DragPieceMove(x, y)
6219      int x; int y;
6220 {
6221     XPoint corner;
6222
6223     /* Are we animating? */
6224     if (!appData.animateDragging || appData.blindfold)
6225       return;
6226
6227     /* Sanity check */
6228     if (! player.dragActive)
6229       return;
6230     /* Move piece, maintaining same relative position
6231        of mouse within square    */
6232     corner.x = x - player.mouseDelta.x;
6233     corner.y = y - player.mouseDelta.y;
6234     AnimationFrame(&player, &corner, player.dragPiece);
6235 #if HIGHDRAG*0
6236     if (appData.highlightDragging) {
6237         int boardX, boardY;
6238         BoardSquare(x, y, &boardX, &boardY);
6239         SetHighlights(fromX, fromY, boardX, boardY);
6240     }
6241 #endif
6242 }
6243
6244 void
6245 DragPieceEnd(x, y)
6246      int x; int y;
6247 {
6248     int boardX, boardY, color;
6249     XPoint corner;
6250
6251     /* Are we animating? */
6252     if (!appData.animateDragging || appData.blindfold)
6253       return;
6254
6255     /* Sanity check */
6256     if (! player.dragActive)
6257       return;
6258     /* Last frame in sequence is square piece is
6259        placed on, which may not match mouse exactly. */
6260     BoardSquare(x, y, &boardX, &boardY);
6261     ScreenSquare(boardX, boardY, &corner, &color);
6262     EndAnimation(&player, &corner);
6263
6264     /* Be sure end square is redrawn */
6265     damage[boardY][boardX] = True;
6266
6267     /* This prevents weird things happening with fast successive
6268        clicks which on my Sun at least can cause motion events
6269        without corresponding press/release. */
6270     player.dragActive = False;
6271 }
6272
6273 /* Handle expose event while piece being dragged */
6274
6275 static void
6276 DrawDragPiece ()
6277 {
6278   if (!player.dragActive || appData.blindfold)
6279     return;
6280
6281   /* What we're doing: logically, the move hasn't been made yet,
6282      so the piece is still in it's original square. But visually
6283      it's being dragged around the board. So we erase the square
6284      that the piece is on and draw it at the last known drag point. */
6285   BlankSquare(player.startSquare.x, player.startSquare.y,
6286                 player.startColor, EmptySquare, xBoardWindow);
6287   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
6288   damage[player.startBoardY][player.startBoardX] = TRUE;
6289 }
6290
6291 #include <sys/ioctl.h>
6292 int get_term_width()
6293 {
6294     int fd, default_width;
6295
6296     fd = STDIN_FILENO;
6297     default_width = 79; // this is FICS default anyway...
6298
6299 #if !defined(TIOCGWINSZ) && defined(TIOCGSIZE)
6300     struct ttysize win;
6301     if (!ioctl(fd, TIOCGSIZE, &win))
6302         default_width = win.ts_cols;
6303 #elif defined(TIOCGWINSZ)
6304     struct winsize win;
6305     if (!ioctl(fd, TIOCGWINSZ, &win))
6306         default_width = win.ws_col;
6307 #endif
6308     return default_width;
6309 }
6310
6311 void update_ics_width()
6312 {
6313     static int old_width = 0;
6314     int new_width = get_term_width();
6315
6316     if (old_width != new_width)
6317        ics_printf("set width %d\n", new_width);
6318     old_width = new_width;
6319 }
6320
6321 void NotifyFrontendLogin()
6322 {
6323     update_ics_width();
6324 }