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