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