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