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