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