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