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