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