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