These are the changes I had made in my source tree between 4.2.6 and
[xboard.git] / xboard.c
1 /*
2  * xboard.c -- X front end for XBoard
3  * $Id$
4  *
5  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
6  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
7  *
8  * The following terms apply to Digital Equipment Corporation's copyright
9  * interest in XBoard:
10  * ------------------------------------------------------------------------
11  * All Rights Reserved
12  *
13  * Permission to use, copy, modify, and distribute this software and its
14  * documentation for any purpose and without fee is hereby granted,
15  * provided that the above copyright notice appear in all copies and that
16  * both that copyright notice and this permission notice appear in
17  * supporting documentation, and that the name of Digital not be
18  * used in advertising or publicity pertaining to distribution of the
19  * software without specific, written prior permission.
20  *
21  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
22  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
23  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
24  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
25  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
27  * SOFTWARE.
28  * ------------------------------------------------------------------------
29  *
30  * The following terms apply to the enhanced version of XBoard distributed
31  * by the Free Software Foundation:
32  * ------------------------------------------------------------------------
33  * This program is free software; you can redistribute it and/or modify
34  * it under the terms of the GNU General Public License as published by
35  * the Free Software Foundation; either version 2 of the License, or
36  * (at your option) any later version.
37  *
38  * This program is distributed in the hope that it will be useful,
39  * but WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
41  * GNU General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with this program; if not, write to the Free Software
45  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
46  * ------------------------------------------------------------------------
47  *
48  * See the file ChangeLog for a revision history.
49  */
50
51 #include "config.h"
52
53 #include <stdio.h>
54 #include <ctype.h>
55 #include <signal.h>
56 #include <errno.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <pwd.h>
60
61 #if !OMIT_SOCKETS
62 # if HAVE_SYS_SOCKET_H
63 #  include <sys/socket.h>
64 #  include <netinet/in.h>
65 #  include <netdb.h>
66 # else /* not HAVE_SYS_SOCKET_H */
67 #  if HAVE_LAN_SOCKET_H
68 #   include <lan/socket.h>
69 #   include <lan/in.h>
70 #   include <lan/netdb.h>
71 #  else /* not HAVE_LAN_SOCKET_H */
72 #   define OMIT_SOCKETS 1
73 #  endif /* not HAVE_LAN_SOCKET_H */
74 # endif /* not HAVE_SYS_SOCKET_H */
75 #endif /* !OMIT_SOCKETS */
76
77 #if STDC_HEADERS
78 # include <stdlib.h>
79 # include <string.h>
80 #else /* not STDC_HEADERS */
81 extern char *getenv();
82 # if HAVE_STRING_H
83 #  include <string.h>
84 # else /* not HAVE_STRING_H */
85 #  include <strings.h>
86 # endif /* not HAVE_STRING_H */
87 #endif /* not STDC_HEADERS */
88
89 #if HAVE_SYS_FCNTL_H
90 # include <sys/fcntl.h>
91 #else /* not HAVE_SYS_FCNTL_H */
92 # if HAVE_FCNTL_H
93 #  include <fcntl.h>
94 # endif /* HAVE_FCNTL_H */
95 #endif /* not HAVE_SYS_FCNTL_H */
96
97 #if HAVE_SYS_SYSTEMINFO_H
98 # include <sys/systeminfo.h>
99 #endif /* HAVE_SYS_SYSTEMINFO_H */
100
101 #if TIME_WITH_SYS_TIME
102 # include <sys/time.h>
103 # include <time.h>
104 #else
105 # if HAVE_SYS_TIME_H
106 #  include <sys/time.h>
107 # else
108 #  include <time.h>
109 # endif
110 #endif
111
112 #if HAVE_UNISTD_H
113 # include <unistd.h>
114 #endif
115
116 #if HAVE_SYS_WAIT_H
117 # include <sys/wait.h>
118 #endif
119
120 #if HAVE_DIRENT_H
121 # include <dirent.h>
122 # define NAMLEN(dirent) strlen((dirent)->d_name)
123 # define HAVE_DIR_STRUCT
124 #else
125 # define dirent direct
126 # define NAMLEN(dirent) (dirent)->d_namlen
127 # if HAVE_SYS_NDIR_H
128 #  include <sys/ndir.h>
129 #  define HAVE_DIR_STRUCT
130 # endif
131 # if HAVE_SYS_DIR_H
132 #  include <sys/dir.h>
133 #  define HAVE_DIR_STRUCT
134 # endif
135 # if HAVE_NDIR_H
136 #  include <ndir.h>
137 #  define HAVE_DIR_STRUCT
138 # endif
139 #endif
140
141 #include <X11/Intrinsic.h>
142 #include <X11/StringDefs.h>
143 #include <X11/Shell.h>
144 #include <X11/cursorfont.h>
145 #include <X11/Xatom.h>
146 #if USE_XAW3D
147 #include <X11/Xaw3d/Dialog.h>
148 #include <X11/Xaw3d/Form.h>
149 #include <X11/Xaw3d/List.h>
150 #include <X11/Xaw3d/Label.h>
151 #include <X11/Xaw3d/SimpleMenu.h>
152 #include <X11/Xaw3d/SmeBSB.h>
153 #include <X11/Xaw3d/SmeLine.h>
154 #include <X11/Xaw3d/Box.h>
155 #include <X11/Xaw3d/MenuButton.h>
156 #include <X11/Xaw3d/Text.h>
157 #include <X11/Xaw3d/AsciiText.h>
158 #else
159 #include <X11/Xaw/Dialog.h>
160 #include <X11/Xaw/Form.h>
161 #include <X11/Xaw/List.h>
162 #include <X11/Xaw/Label.h>
163 #include <X11/Xaw/SimpleMenu.h>
164 #include <X11/Xaw/SmeBSB.h>
165 #include <X11/Xaw/SmeLine.h>
166 #include <X11/Xaw/Box.h>
167 #include <X11/Xaw/MenuButton.h>
168 #include <X11/Xaw/Text.h>
169 #include <X11/Xaw/AsciiText.h>
170 #endif
171
172 #if HAVE_LIBXPM
173 #include <X11/xpm.h>
174 #include "pixmaps/pixmaps.h"
175 #define IMAGE_EXT "xpm"
176 #else
177 #define IMAGE_EXT "xim"
178 #include "bitmaps/bitmaps.h"
179 #endif
180
181 #include "bitmaps/icon_white.bm"
182 #include "bitmaps/icon_black.bm"
183 #include "bitmaps/checkmark.bm"
184
185 #include "common.h"
186 #include "frontend.h"
187 #include "backend.h"
188 #include "moves.h"
189 #include "xboard.h"
190 #include "childio.h"
191 #include "xgamelist.h"
192 #include "xhistory.h"
193 #include "xedittags.h"
194
195 #ifdef __EMX__
196 #ifndef HAVE_USLEEP
197 #define HAVE_USLEEP
198 #endif
199 #define usleep(t)   _sleep2(((t)+500)/1000)
200 #endif
201
202 typedef struct {
203     String string;
204     XtActionProc proc;
205 } MenuItem;
206
207 typedef struct {
208     String name;
209     MenuItem *mi;
210 } Menu;
211
212 int main P((int argc, char **argv));
213 RETSIGTYPE CmailSigHandler P((int sig));
214 RETSIGTYPE IntSigHandler P((int sig));
215 void CreateGCs P((void));
216 void CreateXIMPieces P((void));
217 void CreateXPMPieces P((void));
218 void CreatePieces P((void));
219 void CreatePieceMenus P((void));
220 Widget CreateMenuBar P((Menu *mb));
221 Widget CreateButtonBar P ((MenuItem *mi));
222 char *FindFont P((char *pattern, int targetPxlSize));
223 void PieceMenuPopup P((Widget w, XEvent *event,
224                        String *params, Cardinal *num_params));
225 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
226 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
227 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
228                    u_int wreq, u_int hreq));
229 void CreateGrid P((void));
230 int EventToSquare P((int x, int limit));
231 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
232 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
233 void HandleUserMove P((Widget w, XEvent *event,
234                      String *prms, Cardinal *nprms));
235 void AnimateUserMove P((Widget w, XEvent * event,
236                      String * params, Cardinal * nParams));
237 void WhiteClock P((Widget w, XEvent *event,
238                    String *prms, Cardinal *nprms));
239 void BlackClock P((Widget w, XEvent *event,
240                    String *prms, Cardinal *nprms));
241 void DrawPositionProc P((Widget w, XEvent *event,
242                      String *prms, Cardinal *nprms));
243 void XDrawPosition P((Widget w, /*Boolean*/int repaint, 
244                      Board board));
245 void CommentPopUp P((char *title, char *label));
246 void CommentPopDown P((void));
247 void CommentCallback P((Widget w, XtPointer client_data,
248                         XtPointer call_data));
249 void ICSInputBoxPopUp P((void));
250 void ICSInputBoxPopDown P((void));
251 void FileNamePopUp P((char *label, char *def,
252                       FileProc proc, char *openMode));
253 void FileNamePopDown P((void));
254 void FileNameCallback P((Widget w, XtPointer client_data,
255                          XtPointer call_data));
256 void FileNameAction P((Widget w, XEvent *event,
257                        String *prms, Cardinal *nprms));
258 void AskQuestionReplyAction P((Widget w, XEvent *event,
259                           String *prms, Cardinal *nprms));
260 void AskQuestionProc P((Widget w, XEvent *event,
261                           String *prms, Cardinal *nprms));
262 void AskQuestionPopDown P((void));
263 void PromotionPopUp P((void));
264 void PromotionPopDown P((void));
265 void PromotionCallback P((Widget w, XtPointer client_data,
266                           XtPointer call_data));
267 void EditCommentPopDown P((void));
268 void EditCommentCallback P((Widget w, XtPointer client_data,
269                             XtPointer call_data));
270 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
271 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
272 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
273 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
274                          Cardinal *nprms));
275 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
276                          Cardinal *nprms));
277 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
278                        Cardinal *nprms));
279 void LoadPositionProc P((Widget w, XEvent *event,
280                          String *prms, Cardinal *nprms));
281 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
282                          Cardinal *nprms));
283 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
284                          Cardinal *nprms));
285 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
286                        Cardinal *nprms));
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 SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
294 void SavePositionProc P((Widget w, XEvent *event,
295                          String *prms, Cardinal *nprms));
296 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
297 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
298                             Cardinal *nprms));
299 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
300 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
301 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
302                          Cardinal *nprms));
303 void MachineWhiteProc P((Widget w, XEvent *event,
304                          String *prms, Cardinal *nprms));
305 void AnalyzeModeProc P((Widget w, XEvent *event,
306                          String *prms, Cardinal *nprms));
307 void AnalyzeFileProc P((Widget w, XEvent *event,
308                          String *prms, Cardinal *nprms));
309 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
310                         Cardinal *nprms));
311 void IcsClientProc P((Widget w, XEvent *event, String *prms,
312                       Cardinal *nprms));
313 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
314 void EditPositionProc P((Widget w, XEvent *event,
315                          String *prms, Cardinal *nprms));
316 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
317 void EditCommentProc P((Widget w, XEvent *event,
318                         String *prms, Cardinal *nprms));
319 void IcsInputBoxProc P((Widget w, XEvent *event,
320                         String *prms, Cardinal *nprms));
321 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
322 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
323 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
324 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
325 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
326 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
327 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
328 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
329 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
330 void StopObservingProc P((Widget w, XEvent *event, String *prms,
331                           Cardinal *nprms));
332 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
333                           Cardinal *nprms));
334 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
336 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
337 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
339 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
340                          Cardinal *nprms));
341 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
342                         Cardinal *nprms));
343 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
344 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
345                         Cardinal *nprms));
346 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
347                          Cardinal *nprms));
348 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
349                          Cardinal *nprms));
350 void AutocommProc P((Widget w, XEvent *event, String *prms,
351                      Cardinal *nprms));
352 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
353 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
354 void AutobsProc P((Widget w, XEvent *event, String *prms,
355                         Cardinal *nprms));
356 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
357 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
359 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
360                        Cardinal *nprms));
361 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
362 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
363                         Cardinal *nprms));
364 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
365                               Cardinal *nprms));
366 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
367                               Cardinal *nprms));
368 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
371                          Cardinal *nprms));
372 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
373                          Cardinal *nprms));
374 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
375                            Cardinal *nprms));
376 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
377                         Cardinal *nprms));
378 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
379                              Cardinal *nprms));
380 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
383                        Cardinal *nprms));
384 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
385                          Cardinal *nprms));
386 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
387                           Cardinal *nprms));
388 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
389 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
390 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
391 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
393 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
394 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
395 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
396 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
397 void DisplayMove P((int moveNumber));
398 void DisplayTitle P((char *title));
399 void ICSInitScript P((void));
400 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
401 void ErrorPopUp P((char *title, char *text, int modal));
402 void ErrorPopDown P((void));
403 static char *ExpandPathName P((char *path));
404 static void CreateAnimVars P((void));
405 static void DragPieceBegin P((int x, int y));
406 static void DragPieceMove P((int x, int y));
407 static void DragPieceEnd P((int x, int y));
408 static void DrawDragPiece P((void));
409 char *ModeToWidgetName P((GameMode mode));
410
411 /*
412 * XBoard depends on Xt R4 or higher
413 */
414 int xtVersion = XtSpecificationRelease;
415
416 int xScreen;
417 Display *xDisplay;
418 Window xBoardWindow;
419 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
420   jailSquareColor, highlightSquareColor, premoveHighlightColor;
421 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
422   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
423   wjPieceGC, bjPieceGC, prelineGC;
424 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
425 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget, 
426   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16], 
427   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
428   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
429   ICSInputShell, fileNameShell, askQuestionShell;
430 XSegment gridSegments[(BOARD_SIZE + 1) * 2];
431 XSegment jailGridSegments[(BOARD_SIZE + 3) * 2];
432 Font clockFontID, coordFontID;
433 XFontStruct *clockFontStruct, *coordFontStruct;
434 XtAppContext appContext;
435 char *layoutName;
436 char *oldICSInteractionTitle;
437
438 FileProc fileProc;
439 char *fileOpenMode;
440
441 Position commentX = -1, commentY = -1;
442 Dimension commentW, commentH;
443
444 int squareSize, smallLayout = 0, tinyLayout = 0,
445   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
446   ICSInputBoxUp = False, askQuestionUp = False,
447   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
448   editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
449 Pixel timerForegroundPixel, timerBackgroundPixel;
450 Pixel buttonForegroundPixel, buttonBackgroundPixel;
451 char *chessDir, *programName, *programVersion,
452   *gameCopyFilename, *gamePasteFilename;
453
454 #define SOLID 0
455 #define OUTLINE 1
456 Pixmap pieceBitmap[2][6];
457 Pixmap xpmPieceBitmap[4][6];    /* LL, LD, DL, DD */
458 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
459 int useImages, useImageSqs;
460 XImage *ximPieceBitmap[4][6];   /* LL, LD, DL, DD */
461 Pixmap ximMaskPm[6];            /* clipmasks, used for XIM pieces */
462 XImage *ximLightSquare, *ximDarkSquare;
463 XImage *xim_Cross;
464
465 #define pieceToSolid(piece) &pieceBitmap[SOLID][((int)(piece)) % 6]
466 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][((int)(piece)) % 6]
467
468 #define White(piece) ((int)(piece) < (int)BlackPawn)
469
470 /* Variables for doing smooth animation. This whole thing
471    would be much easier if the board was double-buffered,
472    but that would require a fairly major rewrite.       */
473
474 typedef struct {
475         Pixmap  saveBuf;
476         Pixmap  newBuf;
477         GC      blitGC, pieceGC, outlineGC;
478         XPoint  startSquare, prevFrame, mouseDelta;
479         int     startColor;
480         int     dragPiece;
481         Boolean dragActive;
482         int     startBoardX, startBoardY;
483     } AnimState;
484
485 /* There can be two pieces being animated at once: a player
486    can begin dragging a piece before the remote opponent has moved. */
487
488 static AnimState game, player;
489
490 /* Bitmaps for use as masks when drawing XPM pieces.
491    Need one for each black and white piece.             */
492 static Pixmap xpmMask[BlackKing + 1];
493
494 /* This magic number is the number of intermediate frames used
495    in each half of the animation. For short moves it's reduced
496    by 1. The total number of frames will be factor * 2 + 1.  */
497 #define kFactor    4
498
499 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
500
501 MenuItem fileMenu[] = {
502     {"Reset Game", ResetProc},
503     {"----", NothingProc},
504     {"Load Game", LoadGameProc},
505     {"Load Next Game", LoadNextGameProc},
506     {"Load Previous Game", LoadPrevGameProc},
507     {"Reload Same Game", ReloadGameProc},
508     {"Save Game", SaveGameProc},
509     {"----", NothingProc},
510     {"Copy Game", CopyGameProc},
511     {"Paste Game", PasteGameProc},
512     {"----", NothingProc},
513     {"Load Position", LoadPositionProc},
514     {"Load Next Position", LoadNextPositionProc},
515     {"Load Previous Position", LoadPrevPositionProc},
516     {"Reload Same Position", ReloadPositionProc},
517     {"Save Position", SavePositionProc},
518     {"----", NothingProc},
519     {"Copy Position", CopyPositionProc},
520     {"Paste Position", PastePositionProc},
521     {"----", NothingProc},
522     {"Mail Move", MailMoveProc},
523     {"Reload CMail Message", ReloadCmailMsgProc},
524     {"----", NothingProc},
525     {"Exit", QuitProc},
526     {NULL, NULL}
527 };
528
529 MenuItem modeMenu[] = {
530     {"Machine White", MachineWhiteProc},
531     {"Machine Black", MachineBlackProc},
532     {"Two Machines", TwoMachinesProc},
533     {"Analysis Mode", AnalyzeModeProc},
534     {"Analyze File", AnalyzeFileProc },
535     {"ICS Client", IcsClientProc},
536     {"Edit Game", EditGameProc},
537     {"Edit Position", EditPositionProc},
538     {"Training", TrainingProc},
539     {"----", NothingProc},
540     {"Show Game List", ShowGameListProc},
541     {"Show Move List", HistoryShowProc},
542     {"Edit Tags", EditTagsProc},
543     {"Edit Comment", EditCommentProc},
544     {"ICS Input Box", IcsInputBoxProc},
545     {"Pause", PauseProc},
546     {NULL, NULL}
547 };
548
549 MenuItem actionMenu[] = {
550     {"Accept", AcceptProc},
551     {"Decline", DeclineProc},
552     {"Rematch", RematchProc},
553     {"----", NothingProc},    
554     {"Call Flag", CallFlagProc},
555     {"Draw", DrawProc},
556     {"Adjourn", AdjournProc},
557     {"Abort", AbortProc},
558     {"Resign", ResignProc},
559     {"----", NothingProc},    
560     {"Stop Observing", StopObservingProc},
561     {"Stop Examining", StopExaminingProc},
562     {NULL, NULL}
563 };
564
565 MenuItem stepMenu[] = {
566     {"Backward", BackwardProc},
567     {"Forward", ForwardProc},
568     {"Back to Start", ToStartProc},
569     {"Forward to End", ToEndProc},
570     {"Revert", RevertProc},
571     {"Truncate Game", TruncateGameProc},
572     {"----", NothingProc},    
573     {"Move Now", MoveNowProc},
574     {"Retract Move", RetractMoveProc},
575     {NULL, NULL}
576 };    
577
578 MenuItem optionsMenu[] = {
579     {"Always Queen", AlwaysQueenProc},
580     {"Animate Dragging", AnimateDraggingProc},
581     {"Animate Moving", AnimateMovingProc},
582     {"Auto Comment", AutocommProc},
583     {"Auto Flag", AutoflagProc},
584     {"Auto Flip View", AutoflipProc},
585     {"Auto Observe", AutobsProc},
586     {"Auto Raise Board", AutoraiseProc},
587     {"Auto Save", AutosaveProc},
588     {"Blindfold", BlindfoldProc},
589     {"Flash Moves", FlashMovesProc},
590     {"Flip View", FlipViewProc},
591     {"Get Move List", GetMoveListProc},
592 #if HIGHDRAG
593     {"Highlight Dragging", HighlightDraggingProc},
594 #endif
595     {"Highlight Last Move", HighlightLastMoveProc},
596     {"Move Sound", MoveSoundProc},
597     {"ICS Alarm", IcsAlarmProc},
598     {"Old Save Style", OldSaveStyleProc},
599     {"Periodic Updates", PeriodicUpdatesProc},  
600     {"Ponder Next Move", PonderNextMoveProc},
601     {"Popup Exit Message", PopupExitMessageProc},       
602     {"Popup Move Errors", PopupMoveErrorsProc}, 
603     {"Premove", PremoveProc},
604     {"Quiet Play", QuietPlayProc},
605     {"Show Coords", ShowCoordsProc},
606     {"Show Thinking", ShowThinkingProc},
607     {"Test Legality", TestLegalityProc},
608     {NULL, NULL}
609 };
610
611 MenuItem helpMenu[] = {
612     {"Info XBoard", InfoProc},
613     {"Man XBoard", ManProc},
614     {"----", NothingProc},
615     {"Hint", HintProc},
616     {"Book", BookProc},
617     {"----", NothingProc},
618     {"About XBoard", AboutProc},
619     {NULL, NULL}
620 };
621
622 Menu menuBar[] = {
623     {"File", fileMenu},
624     {"Mode", modeMenu},
625     {"Action", actionMenu},
626     {"Step", stepMenu},
627     {"Options", optionsMenu},
628     {"Help", helpMenu},
629     {NULL, NULL}
630 };
631
632 #define PAUSE_BUTTON "P"
633 MenuItem buttonBar[] = {
634     {"<<", ToStartProc},
635     {"<", BackwardProc},
636     {PAUSE_BUTTON, PauseProc},
637     {">", ForwardProc},
638     {">>", ToEndProc},
639     {NULL, NULL}
640 };
641
642 #define PIECE_MENU_SIZE 11
643 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
644     { "White", "----", "Pawn", "Knight", "Bishop", "Rook", "Queen", "King",
645       "----", "Empty square", "Clear board" },
646     { "Black", "----", "Pawn", "Knight", "Bishop", "Rook", "Queen", "King",
647       "----", "Empty square", "Clear board" },
648   };
649 /* must be in same order as PieceMenuStrings! */
650 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
651     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
652         WhiteRook, WhiteQueen, WhiteKing,
653         (ChessSquare) 0, EmptySquare, ClearBoard },
654     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
655         BlackRook, BlackQueen, BlackKing,
656         (ChessSquare) 0, EmptySquare, ClearBoard },
657 };
658
659 #define DROP_MENU_SIZE 6
660 String dropMenuStrings[DROP_MENU_SIZE] = {
661     "----", "Pawn", "Knight", "Bishop", "Rook", "Queen"
662   };
663 /* must be in same order as PieceMenuStrings! */
664 ChessSquare dropMenuTranslation[DROP_MENU_SIZE] = {
665     (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
666     WhiteRook, WhiteQueen
667 };
668
669 typedef struct {
670     char piece;
671     char* widget;
672 } DropMenuEnables;
673
674 DropMenuEnables dmEnables[] = {
675     { 'P', "Pawn" },
676     { 'N', "Knight" },
677     { 'B', "Bishop" },
678     { 'R', "Rook" },
679     { 'Q', "Queen" }
680 };
681
682 Arg shellArgs[] = {
683     { XtNwidth, 0 },
684     { XtNheight, 0 },
685     { XtNminWidth, 0 },
686     { XtNminHeight, 0 },
687     { XtNmaxWidth, 0 },
688     { XtNmaxHeight, 0 }
689 };
690
691 Arg layoutArgs[] = {
692     { XtNborderWidth, 0 },
693     { XtNdefaultDistance, 0 },
694 };
695
696 Arg formArgs[] = {
697     { XtNborderWidth, 0 },
698     { XtNresizable, (XtArgVal) True },
699 };
700
701 Arg boardArgs[] = {
702     { XtNborderWidth, 0 },
703     { XtNwidth, 0 },
704     { XtNheight, 0 }
705 };
706
707 Arg titleArgs[] = {
708     { XtNjustify, (XtArgVal) XtJustifyRight },
709     { XtNlabel, (XtArgVal) "..." },
710     { XtNresizable, (XtArgVal) True },
711     { XtNresize, (XtArgVal) False }
712 };
713
714 Arg messageArgs[] = {
715     { XtNjustify, (XtArgVal) XtJustifyLeft },
716     { XtNlabel, (XtArgVal) "..." },
717     { XtNresizable, (XtArgVal) True },
718     { XtNresize, (XtArgVal) False }
719 };
720
721 Arg timerArgs[] = {
722     { XtNborderWidth, 0 },
723     { XtNjustify, (XtArgVal) XtJustifyLeft }
724 };
725
726 XtResource clientResources[] = {
727     { "whitePieceColor", "whitePieceColor", XtRString, sizeof(String),
728         XtOffset(AppDataPtr, whitePieceColor), XtRString,
729         WHITE_PIECE_COLOR },
730     { "blackPieceColor", "blackPieceColor", XtRString, sizeof(String),
731         XtOffset(AppDataPtr, blackPieceColor), XtRString,
732         BLACK_PIECE_COLOR },
733     { "lightSquareColor", "lightSquareColor", XtRString,
734         sizeof(String), XtOffset(AppDataPtr, lightSquareColor),
735         XtRString, LIGHT_SQUARE_COLOR }, 
736     { "darkSquareColor", "darkSquareColor", XtRString, sizeof(String),
737         XtOffset(AppDataPtr, darkSquareColor), XtRString,
738         DARK_SQUARE_COLOR },
739     { "highlightSquareColor", "highlightSquareColor", XtRString,
740         sizeof(String), XtOffset(AppDataPtr, highlightSquareColor),
741         XtRString, HIGHLIGHT_SQUARE_COLOR },
742     { "premoveHighlightColor", "premoveHighlightColor", XtRString,
743         sizeof(String), XtOffset(AppDataPtr, premoveHighlightColor),
744         XtRString, PREMOVE_HIGHLIGHT_COLOR },
745     { "movesPerSession", "movesPerSession", XtRInt, sizeof(int),
746         XtOffset(AppDataPtr, movesPerSession), XtRImmediate,
747         (XtPointer) MOVES_PER_SESSION },
748     { "timeIncrement", "timeIncrement", XtRInt, sizeof(int),
749         XtOffset(AppDataPtr, timeIncrement), XtRImmediate,
750         (XtPointer) TIME_INCREMENT },
751     { "initString", "initString", XtRString, sizeof(String),
752         XtOffset(AppDataPtr, initString), XtRString, INIT_STRING },
753     { "secondInitString", "secondInitString", XtRString, sizeof(String),
754         XtOffset(AppDataPtr, secondInitString), XtRString, INIT_STRING },
755     { "firstComputerString", "firstComputerString", XtRString,
756         sizeof(String), XtOffset(AppDataPtr, firstComputerString), XtRString,
757       COMPUTER_STRING },
758     { "secondComputerString", "secondComputerString", XtRString,
759         sizeof(String), XtOffset(AppDataPtr, secondComputerString), XtRString,
760       COMPUTER_STRING },
761     { "firstChessProgram", "firstChessProgram", XtRString,
762         sizeof(String), XtOffset(AppDataPtr, firstChessProgram),
763         XtRString, FIRST_CHESS_PROGRAM },
764     { "secondChessProgram", "secondChessProgram", XtRString,
765         sizeof(String), XtOffset(AppDataPtr, secondChessProgram),
766         XtRString, SECOND_CHESS_PROGRAM },
767     { "firstPlaysBlack", "firstPlaysBlack", XtRBoolean,
768         sizeof(Boolean), XtOffset(AppDataPtr, firstPlaysBlack),
769         XtRImmediate, (XtPointer) False },
770     { "noChessProgram", "noChessProgram", XtRBoolean,
771         sizeof(Boolean), XtOffset(AppDataPtr, noChessProgram),
772         XtRImmediate, (XtPointer) False },
773     { "firstHost", "firstHost", XtRString, sizeof(String),
774         XtOffset(AppDataPtr, firstHost), XtRString, FIRST_HOST },
775     { "secondHost", "secondHost", XtRString, sizeof(String),
776         XtOffset(AppDataPtr, secondHost), XtRString, SECOND_HOST },
777     { "firstDirectory", "firstDirectory", XtRString, sizeof(String),
778         XtOffset(AppDataPtr, firstDirectory), XtRString, "" },
779     { "secondDirectory", "secondDirectory", XtRString, sizeof(String),
780         XtOffset(AppDataPtr, secondDirectory), XtRString, "" },
781     { "bitmapDirectory", "bitmapDirectory", XtRString,
782         sizeof(String), XtOffset(AppDataPtr, bitmapDirectory),
783         XtRString, "" },
784     { "remoteShell", "remoteShell", XtRString, sizeof(String),
785         XtOffset(AppDataPtr, remoteShell), XtRString, REMOTE_SHELL },
786     { "remoteUser", "remoteUser", XtRString, sizeof(String),
787         XtOffset(AppDataPtr, remoteUser), XtRString, "" },
788     { "timeDelay", "timeDelay", XtRFloat, sizeof(float),
789         XtOffset(AppDataPtr, timeDelay), XtRString,
790         (XtPointer) TIME_DELAY_QUOTE },
791     { "timeControl", "timeControl", XtRString, sizeof(String),
792         XtOffset(AppDataPtr, timeControl), XtRString,
793         (XtPointer) TIME_CONTROL },
794     { "internetChessServerMode", "internetChessServerMode",
795         XtRBoolean, sizeof(Boolean),
796         XtOffset(AppDataPtr, icsActive), XtRImmediate,
797         (XtPointer) False },
798     { "internetChessServerHost", "internetChessServerHost",
799         XtRString, sizeof(String),
800         XtOffset(AppDataPtr, icsHost),
801         XtRString, (XtPointer) ICS_HOST },
802     { "internetChessServerPort", "internetChessServerPort",
803         XtRString, sizeof(String),
804         XtOffset(AppDataPtr, icsPort), XtRString,
805         (XtPointer) ICS_PORT },
806     { "internetChessServerCommPort", "internetChessServerCommPort",
807         XtRString, sizeof(String),
808         XtOffset(AppDataPtr, icsCommPort), XtRString,
809         ICS_COMM_PORT },
810     { "internetChessServerLogonScript", "internetChessServerLogonScript",
811         XtRString, sizeof(String),
812         XtOffset(AppDataPtr, icsLogon), XtRString,
813         ICS_LOGON },
814     { "internetChessServerHelper", "internetChessServerHelper",
815         XtRString, sizeof(String),
816         XtOffset(AppDataPtr, icsHelper), XtRString, "" },
817     { "internetChessServerInputBox", "internetChessServerInputBox",
818         XtRBoolean, sizeof(Boolean),
819         XtOffset(AppDataPtr, icsInputBox), XtRImmediate,
820         (XtPointer) False },
821     { "icsAlarm", "icsAlarm",
822         XtRBoolean, sizeof(Boolean),
823         XtOffset(AppDataPtr, icsAlarm), XtRImmediate,
824         (XtPointer) True },
825     { "icsAlarmTime", "icsAlarmTime",
826         XtRInt, sizeof(int),
827         XtOffset(AppDataPtr, icsAlarmTime), XtRImmediate,
828         (XtPointer) 5000 },
829     { "useTelnet", "useTelnet", XtRBoolean, sizeof(Boolean),
830         XtOffset(AppDataPtr, useTelnet), XtRImmediate,
831         (XtPointer) False },
832     { "telnetProgram", "telnetProgram", XtRString, sizeof(String),
833         XtOffset(AppDataPtr, telnetProgram), XtRString, TELNET_PROGRAM },
834     { "gateway", "gateway", XtRString, sizeof(String),
835         XtOffset(AppDataPtr, gateway), XtRString, "" },
836     { "loadGameFile", "loadGameFile", XtRString, sizeof(String),
837         XtOffset(AppDataPtr, loadGameFile), XtRString, "" },
838     { "loadGameIndex", "loadGameIndex",
839         XtRInt, sizeof(int),
840         XtOffset(AppDataPtr, loadGameIndex), XtRImmediate,
841         (XtPointer) 0 },
842     { "saveGameFile", "saveGameFile", XtRString, sizeof(String),
843         XtOffset(AppDataPtr, saveGameFile), XtRString, "" },
844     { "autoRaiseBoard", "autoRaiseBoard", XtRBoolean,
845         sizeof(Boolean), XtOffset(AppDataPtr, autoRaiseBoard),
846         XtRImmediate, (XtPointer) True },
847     { "autoSaveGames", "autoSaveGames", XtRBoolean,
848         sizeof(Boolean), XtOffset(AppDataPtr, autoSaveGames),
849         XtRImmediate, (XtPointer) False },
850     { "blindfold", "blindfold", XtRBoolean,
851         sizeof(Boolean), XtOffset(AppDataPtr, blindfold),
852         XtRImmediate, (XtPointer) False },
853     { "loadPositionFile", "loadPositionFile", XtRString,
854         sizeof(String), XtOffset(AppDataPtr, loadPositionFile),
855         XtRString, "" },
856     { "loadPositionIndex", "loadPositionIndex",
857         XtRInt, sizeof(int),
858         XtOffset(AppDataPtr, loadPositionIndex), XtRImmediate,
859         (XtPointer) 1 },
860     { "savePositionFile", "savePositionFile", XtRString,
861         sizeof(String), XtOffset(AppDataPtr, savePositionFile),
862         XtRString, "" },
863     { "matchMode", "matchMode", XtRBoolean, sizeof(Boolean),
864         XtOffset(AppDataPtr, matchMode), XtRImmediate, (XtPointer) False },
865     { "matchGames", "matchGames", XtRInt, sizeof(int),
866         XtOffset(AppDataPtr, matchGames), XtRImmediate,
867         (XtPointer) 0 },
868     { "monoMode", "monoMode", XtRBoolean, sizeof(Boolean),
869         XtOffset(AppDataPtr, monoMode), XtRImmediate,
870         (XtPointer) False },
871     { "debugMode", "debugMode", XtRBoolean, sizeof(Boolean),
872         XtOffset(AppDataPtr, debugMode), XtRImmediate,
873         (XtPointer) False },
874     { "clockMode", "clockMode", XtRBoolean, sizeof(Boolean),
875         XtOffset(AppDataPtr, clockMode), XtRImmediate,
876         (XtPointer) True },
877     { "boardSize", "boardSize", XtRString, sizeof(String),
878         XtOffset(AppDataPtr, boardSize), XtRString, "" },
879     { "searchTime", "searchTime", XtRString, sizeof(String),
880         XtOffset(AppDataPtr, searchTime), XtRString,
881         (XtPointer) "" },
882     { "searchDepth", "searchDepth", XtRInt, sizeof(int),
883         XtOffset(AppDataPtr, searchDepth), XtRImmediate, 
884         (XtPointer) 0 },
885     { "showCoords", "showCoords", XtRBoolean, sizeof(Boolean),
886         XtOffset(AppDataPtr, showCoords), XtRImmediate,
887         (XtPointer) False },
888     { "showJail", "showJail", XtRInt, sizeof(int),
889         XtOffset(AppDataPtr, showJail), XtRImmediate,
890         (XtPointer) 0 },
891     { "showThinking", "showThinking", XtRBoolean, sizeof(Boolean),
892         XtOffset(AppDataPtr, showThinking), XtRImmediate,
893         (XtPointer) False },
894     { "ponderNextMove", "ponderNextMove", XtRBoolean, sizeof(Boolean),
895         XtOffset(AppDataPtr, ponderNextMove), XtRImmediate,
896         (XtPointer) True },
897     { "periodicUpdates", "periodicUpdates", XtRBoolean, sizeof(Boolean),
898         XtOffset(AppDataPtr, periodicUpdates), XtRImmediate,
899         (XtPointer) True },
900     { "clockFont", "clockFont", XtRString, sizeof(String),
901         XtOffset(AppDataPtr, clockFont), XtRString, CLOCK_FONT },
902     { "coordFont", "coordFont", XtRString, sizeof(String),
903         XtOffset(AppDataPtr, coordFont), XtRString, COORD_FONT },
904     { "font", "font", XtRString, sizeof(String),
905         XtOffset(AppDataPtr, font), XtRString, DEFAULT_FONT },
906     { "ringBellAfterMoves", "ringBellAfterMoves",
907         XtRBoolean, sizeof(Boolean),
908         XtOffset(AppDataPtr, ringBellAfterMoves),
909         XtRImmediate, (XtPointer) False },
910     { "autoCallFlag", "autoCallFlag", XtRBoolean,
911         sizeof(Boolean), XtOffset(AppDataPtr, autoCallFlag),
912         XtRImmediate, (XtPointer) False },
913     { "autoFlipView", "autoFlipView", XtRBoolean,
914         sizeof(Boolean), XtOffset(AppDataPtr, autoFlipView),
915         XtRImmediate, (XtPointer) True },
916     { "autoObserve", "autoObserve", XtRBoolean,
917         sizeof(Boolean), XtOffset(AppDataPtr, autoObserve),
918         XtRImmediate, (XtPointer) False },
919     { "autoComment", "autoComment", XtRBoolean,
920         sizeof(Boolean), XtOffset(AppDataPtr, autoComment),
921         XtRImmediate, (XtPointer) False },
922     { "getMoveList", "getMoveList", XtRBoolean,
923         sizeof(Boolean), XtOffset(AppDataPtr, getMoveList),
924         XtRImmediate, (XtPointer) True },
925 #if HIGHDRAG
926     { "highlightDragging", "highlightDragging", XtRBoolean,
927         sizeof(Boolean), XtOffset(AppDataPtr, highlightDragging),
928         XtRImmediate, (XtPointer) False },
929 #endif
930     { "highlightLastMove", "highlightLastMove", XtRBoolean,
931         sizeof(Boolean), XtOffset(AppDataPtr, highlightLastMove),
932         XtRImmediate, (XtPointer) False },
933     { "premove", "premove", XtRBoolean,
934         sizeof(Boolean), XtOffset(AppDataPtr, premove),
935         XtRImmediate, (XtPointer) True },
936     { "testLegality", "testLegality", XtRBoolean,
937         sizeof(Boolean), XtOffset(AppDataPtr, testLegality),
938         XtRImmediate, (XtPointer) True },
939     { "flipView", "flipView", XtRBoolean,
940         sizeof(Boolean), XtOffset(AppDataPtr, flipView),
941         XtRImmediate, (XtPointer) False },
942     { "cmail", "cmailGameName", XtRString, sizeof(String),
943         XtOffset(AppDataPtr, cmailGameName), XtRString, "" },
944     { "alwaysPromoteToQueen", "alwaysPromoteToQueen", XtRBoolean,
945         sizeof(Boolean), XtOffset(AppDataPtr, alwaysPromoteToQueen),
946         XtRImmediate, (XtPointer) False },
947     { "oldSaveStyle", "oldSaveStyle", XtRBoolean,
948         sizeof(Boolean), XtOffset(AppDataPtr, oldSaveStyle),
949         XtRImmediate, (XtPointer) False },
950     { "quietPlay", "quietPlay", XtRBoolean,
951         sizeof(Boolean), XtOffset(AppDataPtr, quietPlay),
952         XtRImmediate, (XtPointer) False },
953     { "titleInWindow", "titleInWindow", XtRBoolean,
954         sizeof(Boolean), XtOffset(AppDataPtr, titleInWindow),
955         XtRImmediate, (XtPointer) False },
956     { "localLineEditing", "localLineEditing", XtRBoolean,
957         sizeof(Boolean), XtOffset(AppDataPtr, localLineEditing),
958         XtRImmediate, (XtPointer) True }, /* not implemented, must be True */
959 #ifdef ZIPPY
960     { "zippyTalk", "zippyTalk", XtRBoolean,
961         sizeof(Boolean), XtOffset(AppDataPtr, zippyTalk),
962         XtRImmediate, (XtPointer) ZIPPY_TALK },
963     { "zippyPlay", "zippyPlay", XtRBoolean,
964         sizeof(Boolean), XtOffset(AppDataPtr, zippyPlay),
965         XtRImmediate, (XtPointer) ZIPPY_PLAY },
966     { "zippyLines", "zippyLines", XtRString, sizeof(String),
967         XtOffset(AppDataPtr, zippyLines), XtRString, ZIPPY_LINES },
968     { "zippyPinhead", "zippyPinhead", XtRString, sizeof(String),
969         XtOffset(AppDataPtr, zippyPinhead), XtRString, ZIPPY_PINHEAD },
970     { "zippyPassword", "zippyPassword", XtRString, sizeof(String),
971         XtOffset(AppDataPtr, zippyPassword), XtRString, ZIPPY_PASSWORD },
972     { "zippyPassword2", "zippyPassword2", XtRString, sizeof(String),
973         XtOffset(AppDataPtr, zippyPassword2), XtRString, ZIPPY_PASSWORD2 },
974     { "zippyWrongPassword", "zippyWrongPassword", XtRString, sizeof(String),
975         XtOffset(AppDataPtr, zippyWrongPassword), XtRString,
976         ZIPPY_WRONG_PASSWORD },
977     { "zippyAcceptOnly", "zippyAcceptOnly", XtRString, sizeof(String),
978         XtOffset(AppDataPtr, zippyAcceptOnly), XtRString, ZIPPY_ACCEPT_ONLY },
979     { "zippyUseI", "zippyUseI", XtRBoolean,
980         sizeof(Boolean), XtOffset(AppDataPtr, zippyUseI),
981         XtRImmediate, (XtPointer) ZIPPY_USE_I },
982     { "zippyBughouse", "zippyBughouse", XtRInt,
983         sizeof(int), XtOffset(AppDataPtr, zippyBughouse),
984         XtRImmediate, (XtPointer) ZIPPY_BUGHOUSE },
985     { "zippyNoplayCrafty", "zippyNoplayCrafty", XtRBoolean,
986         sizeof(Boolean), XtOffset(AppDataPtr, zippyNoplayCrafty),
987         XtRImmediate, (XtPointer) ZIPPY_NOPLAY_CRAFTY },
988     { "zippyGameEnd", "zippyGameEnd", XtRString, sizeof(String),
989         XtOffset(AppDataPtr, zippyGameEnd), XtRString, ZIPPY_GAME_END },
990     { "zippyGameStart", "zippyGameStart", XtRString, sizeof(String),
991         XtOffset(AppDataPtr, zippyGameStart), XtRString, ZIPPY_GAME_START },
992     { "zippyAdjourn", "zippyAdjourn", XtRBoolean,
993         sizeof(Boolean), XtOffset(AppDataPtr, zippyAdjourn),
994         XtRImmediate, (XtPointer) ZIPPY_ADJOURN },
995     { "zippyAbort", "zippyAbort", XtRBoolean,
996         sizeof(Boolean), XtOffset(AppDataPtr, zippyAbort),
997         XtRImmediate, (XtPointer) ZIPPY_ABORT },
998     { "zippyVariants", "zippyVariants", XtRString, sizeof(String),
999         XtOffset(AppDataPtr, zippyVariants), XtRString, ZIPPY_VARIANTS },
1000     { "zippyMaxGames", "zippyMaxGames", XtRInt, sizeof(int),
1001         XtOffset(AppDataPtr, zippyMaxGames), XtRImmediate,
1002         (XtPointer) ZIPPY_MAX_GAMES },
1003     { "zippyReplayTimeout", "zippyReplayTimeout", XtRInt, sizeof(int),
1004         XtOffset(AppDataPtr, zippyReplayTimeout), XtRImmediate,
1005         (XtPointer) ZIPPY_REPLAY_TIMEOUT },
1006 #endif
1007     { "flashCount", "flashCount", XtRInt, sizeof(int),
1008         XtOffset(AppDataPtr, flashCount), XtRImmediate,
1009         (XtPointer) FLASH_COUNT  },
1010     { "flashRate", "flashRate", XtRInt, sizeof(int),
1011         XtOffset(AppDataPtr, flashRate), XtRImmediate,
1012         (XtPointer) FLASH_RATE },
1013     { "pixmapDirectory", "pixmapDirectory", XtRString,
1014         sizeof(String), XtOffset(AppDataPtr, pixmapDirectory),
1015         XtRString, "" },
1016     { "msLoginDelay", "msLoginDelay", XtRInt, sizeof(int),
1017         XtOffset(AppDataPtr, msLoginDelay), XtRImmediate,
1018         (XtPointer) MS_LOGIN_DELAY },
1019     { "colorizeMessages", "colorizeMessages", XtRBoolean,
1020         sizeof(Boolean), XtOffset(AppDataPtr, colorize),
1021         XtRImmediate, (XtPointer) False },      
1022     { "colorShout", "colorShout", XtRString,
1023         sizeof(String), XtOffset(AppDataPtr, colorShout),
1024         XtRString, COLOR_SHOUT },
1025     { "colorSShout", "colorSShout", XtRString,
1026         sizeof(String), XtOffset(AppDataPtr, colorSShout),
1027         XtRString, COLOR_SSHOUT },
1028     { "colorChannel1", "colorChannel1", XtRString,
1029         sizeof(String), XtOffset(AppDataPtr, colorChannel1),
1030         XtRString, COLOR_CHANNEL1 },
1031     { "colorChannel", "colorChannel", XtRString,
1032         sizeof(String), XtOffset(AppDataPtr, colorChannel),
1033         XtRString, COLOR_CHANNEL },
1034     { "colorKibitz", "colorKibitz", XtRString,
1035         sizeof(String), XtOffset(AppDataPtr, colorKibitz),
1036         XtRString, COLOR_KIBITZ },
1037     { "colorTell", "colorTell", XtRString,
1038         sizeof(String), XtOffset(AppDataPtr, colorTell),
1039         XtRString, COLOR_TELL },
1040     { "colorChallenge", "colorChallenge", XtRString,
1041         sizeof(String), XtOffset(AppDataPtr, colorChallenge),
1042         XtRString, COLOR_CHALLENGE },
1043     { "colorRequest", "colorRequest", XtRString,
1044         sizeof(String), XtOffset(AppDataPtr, colorRequest),
1045         XtRString, COLOR_REQUEST },
1046     { "colorSeek", "colorSeek", XtRString,
1047         sizeof(String), XtOffset(AppDataPtr, colorSeek),
1048         XtRString, COLOR_SEEK },
1049     { "colorNormal", "colorNormal", XtRString,
1050         sizeof(String), XtOffset(AppDataPtr, colorNormal),
1051         XtRString, COLOR_NORMAL },      
1052     { "soundProgram", "soundProgram", XtRString,
1053       sizeof(String), XtOffset(AppDataPtr, soundProgram),
1054       XtRString, "play" },
1055     { "soundShout", "soundShout", XtRString,
1056       sizeof(String), XtOffset(AppDataPtr, soundShout),
1057       XtRString, "" },
1058     { "soundSShout", "soundSShout", XtRString,
1059       sizeof(String), XtOffset(AppDataPtr, soundSShout),
1060       XtRString, "" },
1061     { "soundChannel1", "soundChannel1", XtRString,
1062       sizeof(String), XtOffset(AppDataPtr, soundChannel1),
1063       XtRString, "" },
1064     { "soundChannel", "soundChannel", XtRString,
1065       sizeof(String), XtOffset(AppDataPtr, soundChannel),
1066       XtRString, "" },
1067     { "soundKibitz", "soundKibitz", XtRString,
1068       sizeof(String), XtOffset(AppDataPtr, soundKibitz),
1069       XtRString, "" },
1070     { "soundTell", "soundTell", XtRString,
1071       sizeof(String), XtOffset(AppDataPtr, soundTell),
1072       XtRString, "" },
1073     { "soundChallenge", "soundChallenge", XtRString,
1074       sizeof(String), XtOffset(AppDataPtr, soundChallenge),
1075       XtRString, "" },
1076     { "soundRequest", "soundRequest", XtRString,
1077       sizeof(String), XtOffset(AppDataPtr, soundRequest),
1078       XtRString, "" },
1079     { "soundSeek", "soundSeek", XtRString,
1080       sizeof(String), XtOffset(AppDataPtr, soundSeek),
1081       XtRString, "" },
1082     { "soundMove", "soundMove", XtRString,
1083       sizeof(String), XtOffset(AppDataPtr, soundMove),
1084       XtRString, "$" },
1085     { "soundIcsWin", "soundIcsWin", XtRString,
1086       sizeof(String), XtOffset(AppDataPtr, soundIcsWin),
1087       XtRString, "" },
1088     { "soundIcsLoss", "soundIcsLoss", XtRString,
1089       sizeof(String), XtOffset(AppDataPtr, soundIcsLoss),
1090       XtRString, "" },
1091     { "soundIcsDraw", "soundIcsDraw", XtRString,
1092       sizeof(String), XtOffset(AppDataPtr, soundIcsDraw),
1093       XtRString, "" },
1094     { "soundIcsUnfinished", "soundIcsUnfinished", XtRString,
1095       sizeof(String), XtOffset(AppDataPtr, soundIcsUnfinished),
1096       XtRString, "" },
1097     { "soundIcsAlarm", "soundIcsAlarm", XtRString,
1098       sizeof(String), XtOffset(AppDataPtr, soundIcsAlarm),
1099       XtRString, "$" },
1100     { "reuseFirst", "reuseFirst", XtRBoolean,
1101         sizeof(Boolean), XtOffset(AppDataPtr, reuseFirst),
1102         XtRImmediate, (XtPointer) True },
1103     { "reuseSecond", "reuseSecond", XtRBoolean,
1104         sizeof(Boolean), XtOffset(AppDataPtr, reuseSecond),
1105         XtRImmediate, (XtPointer) True },
1106     { "animateDragging", "animateDragging", XtRBoolean,
1107         sizeof(Boolean), XtOffset(AppDataPtr, animateDragging),
1108         XtRImmediate, (XtPointer) True },
1109     { "animateMoving", "animateMoving", XtRBoolean,
1110         sizeof(Boolean), XtOffset(AppDataPtr, animate),
1111         XtRImmediate, (XtPointer) True },
1112     { "animateSpeed", "animateSpeed", XtRInt,
1113         sizeof(int), XtOffset(AppDataPtr, animSpeed),
1114         XtRImmediate, (XtPointer)10 },
1115     { "popupExitMessage", "popupExitMessage", XtRBoolean,
1116         sizeof(Boolean), XtOffset(AppDataPtr, popupExitMessage),
1117         XtRImmediate, (XtPointer) True },
1118     { "popupMoveErrors", "popupMoveErrors", XtRBoolean,
1119         sizeof(Boolean), XtOffset(AppDataPtr, popupMoveErrors),
1120         XtRImmediate, (XtPointer) False },
1121     { "fontSizeTolerance", "fontSizeTolerance", XtRInt,
1122         sizeof(int), XtOffset(AppDataPtr, fontSizeTolerance),
1123         XtRImmediate, (XtPointer)4 },
1124     { "initialMode", "initialMode", XtRString,
1125         sizeof(String), XtOffset(AppDataPtr, initialMode),
1126         XtRImmediate, (XtPointer) "" },
1127     { "variant", "variant", XtRString,
1128         sizeof(String), XtOffset(AppDataPtr, variant),
1129         XtRImmediate, (XtPointer) "normal" },
1130     { "firstProtocolVersion", "firstProtocolVersion", XtRInt,
1131         sizeof(int), XtOffset(AppDataPtr, firstProtocolVersion),
1132         XtRImmediate, (XtPointer)PROTOVER },
1133     { "secondProtocolVersion", "secondProtocolVersion", XtRInt,
1134         sizeof(int), XtOffset(AppDataPtr, secondProtocolVersion),
1135         XtRImmediate, (XtPointer)PROTOVER },
1136     { "showButtonBar", "showButtonBar", XtRBoolean,
1137         sizeof(Boolean), XtOffset(AppDataPtr, showButtonBar),
1138         XtRImmediate, (XtPointer) True },
1139 };
1140
1141 XrmOptionDescRec shellOptions[] = {
1142     { "-whitePieceColor", "whitePieceColor", XrmoptionSepArg, NULL },
1143     { "-blackPieceColor", "blackPieceColor", XrmoptionSepArg, NULL },
1144     { "-lightSquareColor", "lightSquareColor", XrmoptionSepArg, NULL },
1145     { "-darkSquareColor", "darkSquareColor", XrmoptionSepArg, NULL },
1146     { "-highlightSquareColor", "highlightSquareColor", XrmoptionSepArg, NULL },
1147     { "-premoveHighlightColor", "premoveHighlightColor", XrmoptionSepArg,NULL},
1148     { "-movesPerSession", "movesPerSession", XrmoptionSepArg, NULL },
1149     { "-mps", "movesPerSession", XrmoptionSepArg, NULL },
1150     { "-timeIncrement", "timeIncrement", XrmoptionSepArg, NULL },
1151     { "-inc", "timeIncrement", XrmoptionSepArg, NULL },
1152     { "-initString", "initString", XrmoptionSepArg, NULL },
1153     { "-firstInitString", "initString", XrmoptionSepArg, NULL },
1154     { "-secondInitString", "secondInitString", XrmoptionSepArg, NULL },
1155     { "-firstComputerString", "firstComputerString", XrmoptionSepArg, NULL },
1156     { "-secondComputerString", "secondComputerString", XrmoptionSepArg, NULL },
1157     { "-firstChessProgram", "firstChessProgram", XrmoptionSepArg, NULL },
1158     { "-fcp", "firstChessProgram", XrmoptionSepArg, NULL },
1159     { "-secondChessProgram", "secondChessProgram", XrmoptionSepArg, NULL },
1160     { "-scp", "secondChessProgram", XrmoptionSepArg, NULL },
1161     { "-firstPlaysBlack", "firstPlaysBlack", XrmoptionSepArg, NULL },
1162     { "-fb", "firstPlaysBlack", XrmoptionNoArg, "True" },
1163     { "-xfb", "firstPlaysBlack", XrmoptionNoArg, "False" },
1164     { "-noChessProgram", "noChessProgram", XrmoptionSepArg, NULL },
1165     { "-ncp", "noChessProgram", XrmoptionNoArg, "True" },
1166     { "-xncp", "noChessProgram", XrmoptionNoArg, "False" },
1167     { "-firstHost", "firstHost", XrmoptionSepArg, NULL },
1168     { "-fh", "firstHost", XrmoptionSepArg, NULL },
1169     { "-secondHost", "secondHost", XrmoptionSepArg, NULL },
1170     { "-sh", "secondHost", XrmoptionSepArg, NULL },
1171     { "-firstDirectory", "firstDirectory", XrmoptionSepArg, NULL },
1172     { "-fd", "firstDirectory", XrmoptionSepArg, NULL },
1173     { "-secondDirectory", "secondDirectory", XrmoptionSepArg, NULL },
1174     { "-sd", "secondDirectory", XrmoptionSepArg, NULL },
1175     { "-bitmapDirectory", "bitmapDirectory", XrmoptionSepArg, NULL },
1176     { "-bm", "bitmapDirectory", XrmoptionSepArg, NULL },
1177     { "-remoteShell", "remoteShell", XrmoptionSepArg, NULL },
1178     { "-rsh", "remoteShell", XrmoptionSepArg, NULL },
1179     { "-remoteUser", "remoteUser", XrmoptionSepArg, NULL },
1180     { "-ruser", "remoteUser", XrmoptionSepArg, NULL },
1181     { "-timeDelay", "timeDelay", XrmoptionSepArg, NULL },
1182     { "-td", "timeDelay", XrmoptionSepArg, NULL },
1183     { "-timeControl", "timeControl", XrmoptionSepArg, NULL },
1184     { "-tc", "timeControl", XrmoptionSepArg, NULL },
1185     { "-internetChessServerMode", "internetChessServerMode",
1186         XrmoptionSepArg, NULL },
1187     { "-ics", "internetChessServerMode", XrmoptionNoArg, "True" },
1188     { "-xics", "internetChessServerMode", XrmoptionNoArg, "False" },
1189     { "-internetChessServerHost", "internetChessServerHost",
1190         XrmoptionSepArg, NULL },
1191     { "-icshost", "internetChessServerHost", XrmoptionSepArg, NULL },
1192     { "-internetChessServerPort", "internetChessServerPort",
1193         XrmoptionSepArg, NULL },
1194     { "-icsport", "internetChessServerPort", XrmoptionSepArg, NULL },
1195     { "-internetChessServerCommPort", "internetChessServerCommPort",
1196         XrmoptionSepArg, NULL },
1197     { "-icscomm", "internetChessServerCommPort", XrmoptionSepArg, NULL },
1198     { "-internetChessServerLogonScript", "internetChessServerLogonScript",
1199         XrmoptionSepArg, NULL },
1200     { "-icslogon", "internetChessServerLogonScript", XrmoptionSepArg, NULL },
1201     { "-internetChessServerHelper", "internetChessServerHelper",
1202         XrmoptionSepArg, NULL },
1203     { "-icshelper", "internetChessServerHelper", XrmoptionSepArg, NULL },
1204     { "-internetChessServerInputBox", "internetChessServerInputBox",
1205         XrmoptionSepArg, NULL },
1206     { "-icsinput", "internetChessServerInputBox", XrmoptionNoArg, "True" },
1207     { "-xicsinput", "internetChessServerInputBox", XrmoptionNoArg, "False" },
1208     { "-icsAlarm", "icsAlarm", XrmoptionSepArg, NULL },
1209     { "-alarm", "icsAlarm", XrmoptionNoArg, "True" },
1210     { "-xalarm", "icsAlarm", XrmoptionNoArg, "False" },
1211     { "-icsAlarmTime", "icsAlarmTime", XrmoptionSepArg, NULL },
1212     { "-useTelnet", "useTelnet", XrmoptionSepArg, NULL },
1213     { "-telnet", "useTelnet", XrmoptionNoArg, "True" },
1214     { "-xtelnet", "useTelnet", XrmoptionNoArg, "False" },
1215     { "-telnetProgram", "telnetProgram", XrmoptionSepArg, NULL },
1216     { "-gateway", "gateway", XrmoptionSepArg, NULL },
1217     { "-loadGameFile", "loadGameFile", XrmoptionSepArg, NULL },
1218     { "-lgf", "loadGameFile", XrmoptionSepArg, NULL },
1219     { "-loadGameIndex", "loadGameIndex", XrmoptionSepArg, NULL },
1220     { "-lgi", "loadGameIndex", XrmoptionSepArg, NULL },
1221     { "-saveGameFile", "saveGameFile", XrmoptionSepArg, NULL },
1222     { "-sgf", "saveGameFile", XrmoptionSepArg, NULL },
1223     { "-autoSaveGames", "autoSaveGames", XrmoptionSepArg, NULL },
1224     { "-autosave", "autoSaveGames", XrmoptionNoArg, "True" },
1225     { "-xautosave", "autoSaveGames", XrmoptionNoArg, "False" },
1226     { "-autoRaiseBoard", "autoRaiseBoard", XrmoptionSepArg, NULL },
1227     { "-autoraise", "autoRaiseBoard", XrmoptionNoArg, "True" },
1228     { "-xautoraise", "autoRaiseBoard", XrmoptionNoArg, "False" },
1229     { "-blindfold", "blindfold", XrmoptionSepArg, NULL },
1230     { "-blind", "blindfold", XrmoptionNoArg, "True" },
1231     { "-xblind", "blindfold", XrmoptionNoArg, "False" },
1232     { "-loadPositionFile", "loadPositionFile", XrmoptionSepArg, NULL },
1233     { "-lpf", "loadPositionFile", XrmoptionSepArg, NULL },
1234     { "-loadPositionIndex", "loadPositionIndex", XrmoptionSepArg, NULL },
1235     { "-lpi", "loadPositionIndex", XrmoptionSepArg, NULL },
1236     { "-savePositionFile", "savePositionFile", XrmoptionSepArg, NULL },
1237     { "-spf", "savePositionFile", XrmoptionSepArg, NULL },
1238     { "-matchMode", "matchMode", XrmoptionSepArg, NULL },
1239     { "-mm", "matchMode", XrmoptionNoArg, "True" },
1240     { "-xmm", "matchMode", XrmoptionNoArg, "False" },
1241     { "-matchGames", "matchGames", XrmoptionSepArg, NULL },
1242     { "-mg", "matchGames", XrmoptionSepArg, NULL },
1243     { "-monoMode", "monoMode", XrmoptionSepArg, NULL },
1244     { "-mono", "monoMode", XrmoptionNoArg, "True" },
1245     { "-xmono", "monoMode", XrmoptionNoArg, "False" },
1246     { "-debugMode", "debugMode", XrmoptionSepArg, NULL },
1247     { "-debug", "debugMode", XrmoptionNoArg, "True" },
1248     { "-xdebug", "debugMode", XrmoptionNoArg, "False" },
1249     { "-clockMode", "clockMode", XrmoptionSepArg, NULL },
1250     { "-clock", "clockMode", XrmoptionNoArg, "True" },
1251     { "-xclock", "clockMode", XrmoptionNoArg, "False" },
1252     { "-boardSize", "boardSize", XrmoptionSepArg, NULL },
1253     { "-size", "boardSize", XrmoptionSepArg, NULL },
1254     { "-searchTime", "searchTime", XrmoptionSepArg, NULL },
1255     { "-st", "searchTime", XrmoptionSepArg, NULL },
1256     { "-searchDepth", "searchDepth", XrmoptionSepArg, NULL },
1257     { "-depth", "searchDepth", XrmoptionSepArg, NULL },
1258     { "-showCoords", "showCoords", XrmoptionSepArg, NULL },
1259     { "-coords", "showCoords", XrmoptionNoArg, "True" },
1260     { "-xcoords", "showCoords", XrmoptionNoArg, "False" },
1261 #if JAIL
1262     { "-showJail", "showJail", XrmoptionSepArg, NULL },
1263     { "-jail", "showJail", XrmoptionNoArg, "1" },
1264     { "-sidejail", "showJail", XrmoptionNoArg, "2" },
1265     { "-xjail", "showJail", XrmoptionNoArg, "0" },
1266 #endif
1267     { "-showThinking", "showThinking", XrmoptionSepArg, NULL },
1268     { "-thinking", "showThinking", XrmoptionNoArg, "True" },
1269     { "-xthinking", "showThinking", XrmoptionNoArg, "False" },
1270     { "-ponderNextMove", "ponderNextMove", XrmoptionSepArg, NULL },
1271     { "-ponder", "ponderNextMove", XrmoptionNoArg, "True" },
1272     { "-xponder", "ponderNextMove", XrmoptionNoArg, "False" },
1273     { "-periodicUpdates", "periodicUpdates", XrmoptionSepArg, NULL },
1274     { "-periodic", "periodicUpdates", XrmoptionNoArg, "True" },
1275     { "-xperiodic", "periodicUpdates", XrmoptionNoArg, "False" },
1276     { "-clockFont", "clockFont", XrmoptionSepArg, NULL },
1277     { "-coordFont", "coordFont", XrmoptionSepArg, NULL },
1278     { "-font", "font", XrmoptionSepArg, NULL },
1279     { "-ringBellAfterMoves", "ringBellAfterMoves", XrmoptionSepArg, NULL },
1280     { "-bell", "ringBellAfterMoves", XrmoptionNoArg, "True" },
1281     { "-xbell", "ringBellAfterMoves", XrmoptionNoArg, "False" },
1282     { "-movesound", "ringBellAfterMoves", XrmoptionNoArg, "True" },
1283     { "-xmovesound", "ringBellAfterMoves", XrmoptionNoArg, "False" },
1284     { "-autoCallFlag", "autoCallFlag", XrmoptionSepArg, NULL },
1285     { "-autoflag", "autoCallFlag", XrmoptionNoArg, "True" },
1286     { "-xautoflag", "autoCallFlag", XrmoptionNoArg, "False" },
1287     { "-autoFlipView", "autoFlipView", XrmoptionSepArg, NULL },
1288     { "-autoflip", "autoFlipView", XrmoptionNoArg, "True" },
1289     { "-xautoflip", "autoFlipView", XrmoptionNoArg, "False" },
1290     { "-autoObserve", "autoObserve", XrmoptionSepArg, NULL },
1291     { "-autobs", "autoObserve", XrmoptionNoArg, "True" },
1292     { "-xautobs", "autoObserve", XrmoptionNoArg, "False" },
1293     { "-autoComment", "autoComment", XrmoptionSepArg, NULL },
1294     { "-autocomm", "autoComment", XrmoptionNoArg, "True" },
1295     { "-xautocomm", "autoComment", XrmoptionNoArg, "False" },
1296     { "-getMoveList", "getMoveList", XrmoptionSepArg, NULL },
1297     { "-moves", "getMoveList", XrmoptionNoArg, "True" },
1298     { "-xmoves", "getMoveList", XrmoptionNoArg, "False" },
1299 #if HIGHDRAG
1300     { "-highlightDragging", "highlightDragging", XrmoptionSepArg, NULL },
1301     { "-highdrag", "highlightDragging", XrmoptionNoArg, "True" },
1302     { "-xhighdrag", "highlightDragging", XrmoptionNoArg, "False" },
1303 #endif
1304     { "-highlightLastMove", "highlightLastMove", XrmoptionSepArg, NULL },
1305     { "-highlight", "highlightLastMove", XrmoptionNoArg, "True" },
1306     { "-xhighlight", "highlightLastMove", XrmoptionNoArg, "False" },
1307     { "-premove", "premove", XrmoptionSepArg, NULL },
1308     { "-pre", "premove", XrmoptionNoArg, "True" },
1309     { "-xpre", "premove", XrmoptionNoArg, "False" },
1310     { "-testLegality", "testLegality", XrmoptionSepArg, NULL },
1311     { "-legal", "testLegality", XrmoptionNoArg, "True" },
1312     { "-xlegal", "testLegality", XrmoptionNoArg, "False" },
1313     { "-flipView", "flipView", XrmoptionSepArg, NULL },
1314     { "-flip", "flipView", XrmoptionNoArg, "True" },
1315     { "-xflip", "flipView", XrmoptionNoArg, "False" },
1316     { "-cmail", "cmailGameName", XrmoptionSepArg, NULL },
1317     { "-alwaysPromoteToQueen", "alwaysPromoteToQueen",
1318         XrmoptionSepArg, NULL },
1319     { "-queen", "alwaysPromoteToQueen", XrmoptionNoArg, "True" },
1320     { "-xqueen", "alwaysPromoteToQueen", XrmoptionNoArg, "False" },
1321     { "-oldSaveStyle", "oldSaveStyle", XrmoptionSepArg, NULL },
1322     { "-oldsave", "oldSaveStyle", XrmoptionNoArg, "True" },
1323     { "-xoldsave", "oldSaveStyle", XrmoptionNoArg, "False" },
1324     { "-quietPlay", "quietPlay", XrmoptionSepArg, NULL },
1325     { "-quiet", "quietPlay", XrmoptionNoArg, "True" },
1326     { "-xquiet", "quietPlay", XrmoptionNoArg, "False" },
1327     { "-titleInWindow", "titleInWindow", XrmoptionSepArg, NULL },
1328     { "-title", "titleInWindow", XrmoptionNoArg, "True" },
1329     { "-xtitle", "titleInWindow", XrmoptionNoArg, "False" },
1330 #ifdef ZIPPY
1331     { "-zippyTalk", "zippyTalk", XrmoptionSepArg, NULL },
1332     { "-zt", "zippyTalk", XrmoptionNoArg, "True" },
1333     { "-xzt", "zippyTalk", XrmoptionNoArg, "False" },
1334     { "-zippyPlay", "zippyPlay", XrmoptionSepArg, NULL },
1335     { "-zp", "zippyPlay", XrmoptionNoArg, "True" },
1336     { "-xzp", "zippyPlay", XrmoptionNoArg, "False" },
1337     { "-zippyLines", "zippyLines", XrmoptionSepArg, NULL },
1338     { "-zippyPinhead", "zippyPinhead", XrmoptionSepArg, NULL },
1339     { "-zippyPassword", "zippyPassword", XrmoptionSepArg, NULL },
1340     { "-zippyPassword2", "zippyPassword2", XrmoptionSepArg, NULL },
1341     { "-zippyWrongPassword", "zippyWrongPassword", XrmoptionSepArg, NULL },
1342     { "-zippyAcceptOnly", "zippyAcceptOnly", XrmoptionSepArg, NULL },
1343     { "-zippyUseI", "zippyUseI", XrmoptionSepArg, NULL },
1344     { "-zui", "zippyUseI", XrmoptionNoArg, "True" },
1345     { "-xzui", "zippyUseI", XrmoptionNoArg, "False" },
1346     { "-zippyBughouse", "zippyBughouse", XrmoptionSepArg, NULL },
1347     { "-zippyNoplayCrafty", "zippyNoplayCrafty", XrmoptionSepArg, NULL },
1348     { "-znc", "zippyNoplayCrafty", XrmoptionNoArg, "True" },
1349     { "-xznc", "zippyNoplayCrafty", XrmoptionNoArg, "False" },
1350     { "-zippyGameEnd", "zippyGameEnd", XrmoptionSepArg, NULL },
1351     { "-zippyGameStart", "zippyGameStart", XrmoptionSepArg, NULL },
1352     { "-zippyAdjourn", "zippyAdjourn", XrmoptionSepArg, NULL },
1353     { "-zadj", "zippyAdjourn", XrmoptionNoArg, "True" },
1354     { "-xzadj", "zippyAdjourn", XrmoptionNoArg, "False" },
1355     { "-zippyAbort", "zippyAbort", XrmoptionSepArg, NULL },
1356     { "-zab", "zippyAbort", XrmoptionNoArg, "True" },
1357     { "-xzab", "zippyAbort", XrmoptionNoArg, "False" },
1358     { "-zippyVariants", "zippyVariants", XrmoptionSepArg, NULL },
1359     { "-zippyMaxGames", "zippyMaxGames", XrmoptionSepArg, NULL },
1360     { "-zippyReplayTimeout", "zippyReplayTimeout", XrmoptionSepArg, NULL },
1361 #endif
1362     { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
1363     { "-flash", "flashCount", XrmoptionNoArg, "3" },
1364     { "-xflash", "flashCount", XrmoptionNoArg, "0" },
1365     { "-flashRate", "flashRate", XrmoptionSepArg, NULL },
1366     { "-pixmapDirectory", "pixmapDirectory", XrmoptionSepArg, NULL },
1367     { "-msLoginDelay", "msLoginDelay", XrmoptionSepArg, NULL },
1368     { "-pixmap", "pixmapDirectory", XrmoptionSepArg, NULL },
1369     { "-colorizeMessages", "colorizeMessages", XrmoptionSepArg, NULL },
1370     { "-colorize", "colorizeMessages", XrmoptionNoArg, "True" },
1371     { "-xcolorize", "colorizeMessages", XrmoptionNoArg, "False" },
1372     { "-colorShout", "colorShout", XrmoptionSepArg, NULL },
1373     { "-colorSShout", "colorSShout", XrmoptionSepArg, NULL },
1374     { "-colorCShout", "colorSShout", XrmoptionSepArg, NULL }, /*FICS name*/
1375     { "-colorChannel1", "colorChannel1", XrmoptionSepArg, NULL },
1376     { "-colorChannel", "colorChannel", XrmoptionSepArg, NULL },
1377     { "-colorKibitz", "colorKibitz", XrmoptionSepArg, NULL },
1378     { "-colorTell", "colorTell", XrmoptionSepArg, NULL },
1379     { "-colorChallenge", "colorChallenge", XrmoptionSepArg, NULL },
1380     { "-colorRequest", "colorRequest", XrmoptionSepArg, NULL },
1381     { "-colorSeek", "colorSeek", XrmoptionSepArg, NULL },
1382     { "-colorNormal", "colorNormal", XrmoptionSepArg, NULL },
1383     { "-soundProgram", "soundProgram", XrmoptionSepArg, NULL },
1384     { "-soundShout", "soundShout", XrmoptionSepArg, NULL },
1385     { "-soundSShout", "soundSShout", XrmoptionSepArg, NULL },
1386     { "-soundCShout", "soundSShout", XrmoptionSepArg, NULL }, /*FICS name*/
1387     { "-soundChannel1", "soundChannel1", XrmoptionSepArg, NULL },
1388     { "-soundChannel", "soundChannel", XrmoptionSepArg, NULL },
1389     { "-soundKibitz", "soundKibitz", XrmoptionSepArg, NULL },
1390     { "-soundTell", "soundTell", XrmoptionSepArg, NULL },
1391     { "-soundChallenge", "soundChallenge", XrmoptionSepArg, NULL },
1392     { "-soundRequest", "soundRequest", XrmoptionSepArg, NULL },
1393     { "-soundSeek", "soundSeek", XrmoptionSepArg, NULL },
1394     { "-soundMove", "soundMove", XrmoptionSepArg, NULL },
1395     { "-soundIcsWin", "soundIcsWin", XrmoptionSepArg, NULL },
1396     { "-soundIcsLoss", "soundIcsLoss", XrmoptionSepArg, NULL },
1397     { "-soundIcsDraw", "soundIcsDraw", XrmoptionSepArg, NULL },
1398     { "-soundIcsUnfinished", "soundIcsUnfinished", XrmoptionSepArg, NULL },
1399     { "-soundIcsAlarm", "soundIcsAlarm", XrmoptionSepArg, NULL },
1400     { "-reuseFirst", "reuseFirst", XrmoptionSepArg, NULL },
1401     { "-reuseChessPrograms", "reuseFirst", XrmoptionSepArg, NULL }, /*compat*/
1402     { "-reuse", "reuseFirst", XrmoptionNoArg, "True" },
1403     { "-xreuse", "reuseFirst", XrmoptionNoArg, "False" },
1404     { "-reuseSecond", "reuseSecond", XrmoptionSepArg, NULL },
1405     { "-reuse2", "reuseSecond", XrmoptionNoArg, "True" },
1406     { "-xreuse2", "reuseSecond", XrmoptionNoArg, "False" },
1407     { "-animateMoving", "animateMoving", XrmoptionSepArg, NULL },
1408     { "-animate", "animateMoving", XrmoptionNoArg, "True" },
1409     { "-xanimate", "animateMoving", XrmoptionNoArg, "False" },
1410     { "-animateDragging", "animateDragging", XrmoptionSepArg, NULL },
1411     { "-drag", "animateDragging", XrmoptionNoArg, "True" },
1412     { "-xdrag", "animateDragging", XrmoptionNoArg, "False" },
1413     { "-animateSpeed", "animateSpeed", XrmoptionSepArg, NULL },
1414     { "-popupExitMessage", "popupExitMessage", XrmoptionSepArg, NULL },
1415     { "-exit", "popupExitMessage", XrmoptionNoArg, "True" },
1416     { "-xexit", "popupExitMessage", XrmoptionNoArg, "False" },
1417     { "-popupMoveErrors", "popupMoveErrors", XrmoptionSepArg, NULL },
1418     { "-popup", "popupMoveErrors", XrmoptionNoArg, "True" },
1419     { "-xpopup", "popupMoveErrors", XrmoptionNoArg, "False" },
1420     { "-fontSizeTolerance", "fontSizeTolerance", XrmoptionSepArg, NULL },
1421     { "-initialMode", "initialMode", XrmoptionSepArg, NULL },
1422     { "-mode", "initialMode", XrmoptionSepArg, NULL },
1423     { "-variant", "variant", XrmoptionSepArg, NULL },
1424     { "-firstProtocolVersion", "firstProtocolVersion", XrmoptionSepArg, NULL },
1425     { "-secondProtocolVersion","secondProtocolVersion",XrmoptionSepArg, NULL },
1426     { "-showButtonBar", "showButtonBar", XrmoptionSepArg, NULL },
1427     { "-buttons", "showButtonBar", XrmoptionNoArg, "True" },
1428     { "-xbuttons", "showButtonBar", XrmoptionNoArg, "False" },
1429 };
1430
1431
1432 XtActionsRec boardActions[] = {
1433     { "DrawPosition", DrawPositionProc },
1434     { "HandleUserMove", HandleUserMove },
1435     { "AnimateUserMove", AnimateUserMove },
1436     { "FileNameAction", FileNameAction },
1437     { "AskQuestionProc", AskQuestionProc },
1438     { "AskQuestionReplyAction", AskQuestionReplyAction },
1439     { "PieceMenuPopup", PieceMenuPopup },
1440     { "WhiteClock", WhiteClock },
1441     { "BlackClock", BlackClock },
1442     { "Iconify", Iconify },
1443     { "ResetProc", ResetProc },
1444     { "LoadGameProc", LoadGameProc },
1445     { "LoadNextGameProc", LoadNextGameProc },
1446     { "LoadPrevGameProc", LoadPrevGameProc },
1447     { "LoadSelectedProc", LoadSelectedProc },
1448     { "ReloadGameProc", ReloadGameProc },
1449     { "LoadPositionProc", LoadPositionProc },
1450     { "LoadNextPositionProc", LoadNextPositionProc },
1451     { "LoadPrevPositionProc", LoadPrevPositionProc },
1452     { "ReloadPositionProc", ReloadPositionProc },
1453     { "CopyPositionProc", CopyPositionProc },
1454     { "PastePositionProc", PastePositionProc },
1455     { "CopyGameProc", CopyGameProc },
1456     { "PasteGameProc", PasteGameProc },
1457     { "SaveGameProc", SaveGameProc },
1458     { "SavePositionProc", SavePositionProc },
1459     { "MailMoveProc", MailMoveProc },
1460     { "ReloadCmailMsgProc", ReloadCmailMsgProc },
1461     { "QuitProc", QuitProc },
1462     { "MachineWhiteProc", MachineWhiteProc },
1463     { "MachineBlackProc", MachineBlackProc },
1464     { "AnalysisModeProc", AnalyzeModeProc },
1465     { "AnalyzeFileProc", AnalyzeFileProc },
1466     { "TwoMachinesProc", TwoMachinesProc },
1467     { "IcsClientProc", IcsClientProc },
1468     { "EditGameProc", EditGameProc },
1469     { "EditPositionProc", EditPositionProc },
1470     { "TrainingProc", EditPositionProc },
1471     { "ShowGameListProc", ShowGameListProc },
1472     { "ShowMoveListProc", HistoryShowProc},
1473     { "EditTagsProc", EditCommentProc },
1474     { "EditCommentProc", EditCommentProc },
1475     { "IcsAlarmProc", IcsAlarmProc },
1476     { "IcsInputBoxProc", IcsInputBoxProc },
1477     { "PauseProc", PauseProc },
1478     { "AcceptProc", AcceptProc },
1479     { "DeclineProc", DeclineProc },
1480     { "RematchProc", RematchProc },
1481     { "CallFlagProc", CallFlagProc },
1482     { "DrawProc", DrawProc },
1483     { "AdjournProc", AdjournProc },
1484     { "AbortProc", AbortProc },
1485     { "ResignProc", ResignProc },
1486     { "EnterKeyProc", EnterKeyProc },
1487     { "StopObservingProc", StopObservingProc },
1488     { "StopExaminingProc", StopExaminingProc },
1489     { "BackwardProc", BackwardProc },
1490     { "ForwardProc", ForwardProc },
1491     { "ToStartProc", ToStartProc },
1492     { "ToEndProc", ToEndProc },
1493     { "RevertProc", RevertProc },
1494     { "TruncateGameProc", TruncateGameProc },
1495     { "MoveNowProc", MoveNowProc },
1496     { "RetractMoveProc", RetractMoveProc },
1497     { "AlwaysQueenProc", AlwaysQueenProc },
1498     { "AnimateDraggingProc", AnimateDraggingProc },
1499     { "AnimateMovingProc", AnimateMovingProc },
1500     { "AutoflagProc", AutoflagProc },
1501     { "AutoflipProc", AutoflipProc },
1502     { "AutobsProc", AutobsProc },
1503     { "AutoraiseProc", AutoraiseProc },
1504     { "AutosaveProc", AutosaveProc },
1505     { "BlindfoldProc", BlindfoldProc },
1506     { "FlashMovesProc", FlashMovesProc },
1507     { "FlipViewProc", FlipViewProc },
1508     { "GetMoveListProc", GetMoveListProc },
1509 #if HIGHDRAG
1510     { "HighlightDraggingProc", HighlightDraggingProc },
1511 #endif
1512     { "HighlightLastMoveProc", HighlightLastMoveProc },
1513     { "IcsAlarmProc", IcsAlarmProc },
1514     { "MoveSoundProc", MoveSoundProc },
1515     { "OldSaveStyleProc", OldSaveStyleProc },
1516     { "PeriodicUpdatesProc", PeriodicUpdatesProc },     
1517     { "PonderNextMoveProc", PonderNextMoveProc },
1518     { "PopupExitMessageProc", PopupExitMessageProc },   
1519     { "PopupMoveErrorsProc", PopupMoveErrorsProc },     
1520     { "PremoveProc", PremoveProc },
1521     { "QuietPlayProc", QuietPlayProc },
1522     { "ShowCoordsProc", ShowCoordsProc },
1523     { "ShowThinkingProc", ShowThinkingProc },
1524     { "TestLegalityProc", TestLegalityProc },
1525     { "InfoProc", InfoProc },
1526     { "ManProc", ManProc },
1527     { "HintProc", HintProc },
1528     { "BookProc", BookProc },
1529     { "AboutGameProc", AboutGameProc },
1530     { "AboutProc", AboutProc },
1531     { "DebugProc", DebugProc },
1532     { "NothingProc", NothingProc },
1533     { "CommentPopDown", (XtActionProc) CommentPopDown },
1534     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
1535     { "TagsPopDown", (XtActionProc) TagsPopDown },
1536     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
1537     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
1538     { "AnalysisPopDown", (XtActionProc) AnalysisPopDown },
1539     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
1540     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
1541     { "GameListPopDown", (XtActionProc) GameListPopDown },
1542     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
1543     { "HistoryPopDown", (XtActionProc) HistoryPopDown },
1544 };
1545      
1546 char globalTranslations[] =
1547   ":<Key>R: ResignProc() \n \
1548    :<Key>r: ResetProc() \n \
1549    :<Key>g: LoadGameProc() \n \
1550    :<Key>N: LoadNextGameProc() \n \
1551    :<Key>P: LoadPrevGameProc() \n \
1552    :<Key>Q: QuitProc() \n \
1553    :<Key>F: ToEndProc() \n \
1554    :<Key>f: ForwardProc() \n \
1555    :<Key>B: ToStartProc() \n \
1556    :<Key>b: BackwardProc() \n \
1557    :<Key>p: PauseProc() \n \
1558    :<Key>d: DrawProc() \n \
1559    :<Key>t: CallFlagProc() \n \
1560    :<Key>i: Iconify() \n \
1561    :<Key>c: Iconify() \n \
1562    :<Key>v: FlipViewProc() \n \
1563    <KeyDown>Control_L: BackwardProc() \n \
1564    <KeyUp>Control_L: ForwardProc() \n \
1565    <KeyDown>Control_R: BackwardProc() \n \
1566    <KeyUp>Control_R: ForwardProc() \n \
1567    Shift<Key>1: AskQuestionProc(\"Direct command\",\
1568                                 \"Send to chess program:\",,1) \n \
1569    Shift<Key>2: AskQuestionProc(\"Direct command\",\
1570                                 \"Send to second chess program:\",,2) \n";
1571
1572 char boardTranslations[] =
1573    "<Btn1Down>: HandleUserMove() \n \
1574    <Btn1Up>: HandleUserMove() \n \
1575    <Btn1Motion>: AnimateUserMove() \n \
1576    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1577                  PieceMenuPopup(menuB) \n \
1578    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1579                  PieceMenuPopup(menuW) \n \
1580    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1581                  PieceMenuPopup(menuW) \n \
1582    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1583                  PieceMenuPopup(menuB) \n";
1584      
1585 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1586 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1587      
1588 char ICSInputTranslations[] =
1589     "<Key>Return: EnterKeyProc() \n";
1590
1591 String xboardResources[] = {
1592     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1593     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1594     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1595     NULL
1596   };
1597      
1598
1599 /* Max possible square size */
1600 #define MAXSQSIZE 256
1601
1602 static int xpm_avail[MAXSQSIZE];
1603
1604 #ifdef HAVE_DIR_STRUCT
1605
1606 /* Extract piece size from filename */
1607 static int
1608 xpm_getsize(name, len, ext)
1609      char *name;
1610      int len;
1611      char *ext;
1612 {
1613     char *p, *d;
1614     char buf[10];
1615   
1616     if (len < 4)
1617       return 0;
1618
1619     if ((p=strchr(name, '.')) == NULL ||
1620         StrCaseCmp(p+1, ext) != 0)
1621       return 0;
1622   
1623     p = name + 3;
1624     d = buf;
1625
1626     while (*p && isdigit(*p))
1627       *(d++) = *(p++);
1628
1629     *d = 0;
1630     return atoi(buf);
1631 }
1632
1633 /* Setup xpm_avail */
1634 static int
1635 xpm_getavail(dirname, ext)
1636      char *dirname;
1637      char *ext;
1638 {
1639     DIR *dir;
1640     struct dirent *ent;
1641     int  i;
1642
1643     for (i=0; i<MAXSQSIZE; ++i)
1644       xpm_avail[i] = 0;
1645
1646     if (appData.debugMode)
1647       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1648   
1649     dir = opendir(dirname);
1650     if (!dir)
1651       {
1652           fprintf(stderr, "%s: Can't access XPM directory %s\n", 
1653                   programName, dirname);
1654           exit(1);
1655       }
1656   
1657     while ((ent=readdir(dir)) != NULL) {
1658         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1659         if (i > 0 && i < MAXSQSIZE)
1660           xpm_avail[i] = 1;
1661     }
1662
1663     closedir(dir);
1664
1665     return 0;
1666 }
1667
1668 void
1669 xpm_print_avail(fp, ext)
1670      FILE *fp;
1671      char *ext;
1672 {
1673     int i;
1674
1675     fprintf(fp, "Available `%s' sizes:\n", ext);
1676     for (i=1; i<MAXSQSIZE; ++i) {
1677         if (xpm_avail[i])
1678           printf("%d\n", i);
1679     }
1680 }
1681
1682 /* Return XPM piecesize closest to size */
1683 int
1684 xpm_closest_to(dirname, size, ext)
1685      char *dirname;
1686      int size;
1687      char *ext;
1688 {
1689     int i;
1690     int sm_diff = MAXSQSIZE;
1691     int sm_index = 0;
1692     int diff;
1693   
1694     xpm_getavail(dirname, ext);
1695
1696     if (appData.debugMode)
1697       xpm_print_avail(stderr, ext);
1698   
1699     for (i=1; i<MAXSQSIZE; ++i) {
1700         if (xpm_avail[i]) {
1701             diff = size - i;
1702             diff = (diff<0) ? -diff : diff;
1703             if (diff < sm_diff) {
1704                 sm_diff = diff;
1705                 sm_index = i;
1706             }
1707         }
1708     }
1709
1710     if (!sm_index) {
1711         fprintf(stderr, "Error: No `%s' files!\n", ext);
1712         exit(1);
1713     }
1714
1715     return sm_index;
1716 }
1717 #else   /* !HAVE_DIR_STRUCT */
1718 /* If we are on a system without a DIR struct, we can't
1719    read the directory, so we can't collect a list of
1720    filenames, etc., so we can't do any size-fitting. */
1721 int
1722 xpm_closest_to(dirname, size, ext)
1723      char *dirname;
1724      int size;
1725      char *ext;
1726 {
1727     fprintf(stderr, "Warning: No DIR structure found on this system --\n");
1728     fprintf(stderr, "         Unable to autosize for XPM/XIM pieces.\n");
1729     fprintf(stderr, "   Please report this error to frankm@hiwaay.net.\n");
1730     fprintf(stderr, "   Include system type & operating system in message.\n");
1731     return size;
1732 }
1733 #endif /* HAVE_DIR_STRUCT */
1734
1735 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1736                              "magenta", "cyan", "white" };
1737 typedef struct {
1738     int attr, bg, fg;
1739 } TextColors;
1740 TextColors textColors[(int)NColorClasses];
1741
1742 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1743 static int
1744 parse_color(str, which)
1745      char *str;
1746      int which;
1747 {
1748     char *p, buf[100], *d;
1749     int i;
1750   
1751     if (strlen(str) > 99)       /* watch bounds on buf */
1752       return -1;
1753
1754     p = str;
1755     d = buf;
1756     for (i=0; i<which; ++i) {
1757         p = strchr(p, ',');
1758         if (!p)
1759           return -1;
1760         ++p;
1761     }
1762
1763     /* Could be looking at something like:
1764        black, , 1
1765        .. in which case we want to stop on a comma also */
1766     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1767       ++p;
1768
1769     if (*p == ',') {
1770         return -1;              /* Use default for empty field */
1771     }
1772     
1773     if (which == 2 || isdigit(*p))
1774       return atoi(p);
1775  
1776     while (*p && isalpha(*p))
1777       *(d++) = *(p++);
1778
1779     *d = 0;
1780
1781     for (i=0; i<8; ++i) {
1782         if (!StrCaseCmp(buf, cnames[i]))
1783           return which? (i+40) : (i+30);
1784     }
1785     if (!StrCaseCmp(buf, "default")) return -1;
1786
1787     fprintf(stderr, "%s: unrecognized color %s\n", programName, buf);
1788     return -2;
1789 }
1790
1791 static int
1792 parse_cpair(cc, str)
1793      ColorClass cc;
1794      char *str;
1795 {
1796     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1797         fprintf(stderr, "%s: can't parse foreground color in `%s'\n",
1798                 programName, str);
1799         return -1;
1800     }
1801
1802     /* bg and attr are optional */
1803     textColors[(int)cc].bg = parse_color(str, 1);
1804     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1805         textColors[(int)cc].attr = 0;
1806     }
1807     return 0;
1808 }
1809
1810
1811 /* Arrange to catch delete-window events */
1812 Atom wm_delete_window;
1813 void
1814 CatchDeleteWindow(Widget w, String procname)
1815 {
1816   char buf[MSG_SIZ];
1817   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1818   sprintf(buf, "<Message>WM_PROTOCOLS: %s() \n", procname);
1819   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1820 }
1821
1822 void
1823 BoardToTop()
1824 {
1825   Arg args[16];
1826   XtSetArg(args[0], XtNiconic, False);
1827   XtSetValues(shellWidget, args, 1);
1828
1829   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1830 }
1831
1832 int
1833 main(argc, argv)
1834      int argc;
1835      char **argv;
1836 {
1837     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1838     XSetWindowAttributes window_attributes;
1839     Arg args[16];
1840     Dimension timerWidth, boardWidth, w, h, sep, bor, wr, hr;
1841     XrmValue vFrom, vTo;
1842     XtGeometryResult gres;
1843     char *p;
1844     XrmDatabase xdb;
1845     int forceMono = False;
1846
1847     setbuf(stdout, NULL);
1848     setbuf(stderr, NULL);
1849     debugFP = stderr;
1850     
1851     programName = strrchr(argv[0], '/');
1852     if (programName == NULL)
1853       programName = argv[0];
1854     else
1855       programName++;
1856
1857     shellWidget =
1858       XtAppInitialize(&appContext, "XBoard", shellOptions,
1859                       XtNumber(shellOptions),
1860                       &argc, argv, xboardResources, NULL, 0);
1861     if (argc > 1) {
1862         fprintf(stderr, "%s: unrecognized argument %s\n",
1863                 programName, argv[1]);
1864         exit(2);
1865     }
1866     
1867     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1868         chessDir = ".";
1869     } else {
1870         if (chdir(chessDir) != 0) {
1871             fprintf(stderr, "%s: can't cd to CHESSDIR: ", programName);
1872             perror(chessDir);
1873             exit(1);
1874         }
1875     }
1876     
1877     p = getenv("HOME");
1878     if (p == NULL) p = "/tmp";
1879     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1880     gameCopyFilename = (char*) malloc(i);
1881     gamePasteFilename = (char*) malloc(i);
1882     sprintf(gameCopyFilename, "%s/.xboard%05uc.pgn", p, getpid());
1883     sprintf(gamePasteFilename, "%s/.xboard%05up.pgn", p, getpid());
1884
1885     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1886                               clientResources, XtNumber(clientResources),
1887                               NULL, 0);
1888
1889 #if !HIGHDRAG
1890     /* This feature does not work; animation needs a rewrite */
1891     appData.highlightDragging = FALSE;
1892 #endif
1893     InitBackEnd1();
1894
1895     xDisplay = XtDisplay(shellWidget);
1896     xScreen = DefaultScreen(xDisplay);
1897     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1898
1899     /*
1900      * Determine boardSize
1901      */
1902     if (isdigit(appData.boardSize[0])) {
1903         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1904                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1905                    &fontPxlSize, &smallLayout, &tinyLayout);
1906         if (i == 0) {
1907             fprintf(stderr, "%s: bad boardSize syntax %s\n",
1908                     programName, appData.boardSize);
1909             exit(2);
1910         }
1911         if (i < 7) {
1912             /* Find some defaults; use the nearest known size */
1913             SizeDefaults *szd, *nearest;
1914             int distance = 99999;
1915             nearest = szd = sizeDefaults;
1916             while (szd->name != NULL) {
1917                 if (abs(szd->squareSize - squareSize) < distance) {
1918                     nearest = szd;
1919                     distance = abs(szd->squareSize - squareSize);
1920                     if (distance == 0) break;
1921                 }
1922                 szd++;
1923             }
1924             if (i < 2) lineGap = nearest->lineGap;
1925             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1926             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1927             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1928             if (i < 6) smallLayout = nearest->smallLayout;
1929             if (i < 7) tinyLayout = nearest->tinyLayout;
1930         }
1931     } else {
1932         SizeDefaults *szd = sizeDefaults;
1933         if (*appData.boardSize == NULLCHAR) {
1934             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1935                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1936               szd++;
1937             }
1938             if (szd->name == NULL) szd--;
1939         } else {
1940             while (szd->name != NULL &&
1941                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1942             if (szd->name == NULL) {
1943                 fprintf(stderr, "%s: unrecognized boardSize name %s\n",
1944                         programName, appData.boardSize);
1945                 exit(2);
1946             }
1947         }
1948         squareSize = szd->squareSize;
1949         lineGap = szd->lineGap;
1950         clockFontPxlSize = szd->clockFontPxlSize;
1951         coordFontPxlSize = szd->coordFontPxlSize;
1952         fontPxlSize = szd->fontPxlSize;
1953         smallLayout = szd->smallLayout;
1954         tinyLayout = szd->tinyLayout;
1955     }
1956
1957     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1958     if (strlen(appData.pixmapDirectory) > 0) {
1959         p = ExpandPathName(appData.pixmapDirectory);
1960         if (!p) {
1961             fprintf(stderr, "Error expanding path name \"%s\"\n",
1962                    appData.pixmapDirectory);
1963             exit(1);
1964         }
1965         if (appData.debugMode) {
1966             fprintf(stderr, "XBoard square size (hint): %d\n", squareSize);
1967             fprintf(stderr, "%s fulldir:%s:\n", IMAGE_EXT, p);
1968         }
1969         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1970         if (appData.debugMode) {
1971             fprintf(stderr, "Closest %s size: %d\n", IMAGE_EXT, squareSize);
1972         }
1973     }
1974                 
1975     boardWidth = lineGap + BOARD_SIZE * (squareSize + lineGap);
1976     if (appData.showJail == 1) {
1977         /* Jail on top and bottom */
1978         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1979         XtSetArg(boardArgs[2], XtNheight,
1980                  boardWidth + 2*(lineGap + squareSize));
1981     } else if (appData.showJail == 2) {
1982         /* Jail on sides */
1983         XtSetArg(boardArgs[1], XtNwidth,
1984                  boardWidth + 2*(lineGap + squareSize));
1985         XtSetArg(boardArgs[2], XtNheight, boardWidth);
1986     } else {
1987         /* No jail */
1988         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1989         XtSetArg(boardArgs[2], XtNheight, boardWidth);
1990     }
1991
1992     /*
1993      * Determine what fonts to use.
1994      */
1995     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1996     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1997     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1998     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1999     coordFontID = XLoadFont(xDisplay, appData.coordFont);
2000     coordFontStruct = XQueryFont(xDisplay, coordFontID);
2001     appData.font = FindFont(appData.font, fontPxlSize);
2002
2003     xdb = XtDatabase(xDisplay);
2004     XrmPutStringResource(&xdb, "*font", appData.font);
2005
2006     /*
2007      * Detect if there are not enough colors available and adapt.
2008      */
2009     if (DefaultDepth(xDisplay, xScreen) <= 2) {
2010       appData.monoMode = True;
2011     }
2012
2013     if (!appData.monoMode) {
2014         vFrom.addr = (caddr_t) appData.lightSquareColor;
2015         vFrom.size = strlen(appData.lightSquareColor);
2016         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2017         if (vTo.addr == NULL) {
2018           appData.monoMode = True;
2019           forceMono = True;
2020         } else {
2021           lightSquareColor = *(Pixel *) vTo.addr;
2022         }
2023     }
2024     if (!appData.monoMode) {
2025         vFrom.addr = (caddr_t) appData.darkSquareColor;
2026         vFrom.size = strlen(appData.darkSquareColor);
2027         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2028         if (vTo.addr == NULL) {
2029           appData.monoMode = True;
2030           forceMono = True;
2031         } else {
2032           darkSquareColor = *(Pixel *) vTo.addr;
2033         }
2034     }
2035     if (!appData.monoMode) {
2036         vFrom.addr = (caddr_t) appData.whitePieceColor;
2037         vFrom.size = strlen(appData.whitePieceColor);
2038         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2039         if (vTo.addr == NULL) {
2040           appData.monoMode = True;
2041           forceMono = True;
2042         } else {
2043           whitePieceColor = *(Pixel *) vTo.addr;
2044         }
2045     }
2046     if (!appData.monoMode) {
2047         vFrom.addr = (caddr_t) appData.blackPieceColor;
2048         vFrom.size = strlen(appData.blackPieceColor);
2049         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2050         if (vTo.addr == NULL) {
2051           appData.monoMode = True;
2052           forceMono = True;
2053         } else {
2054           blackPieceColor = *(Pixel *) vTo.addr;
2055         }
2056     }
2057
2058     if (!appData.monoMode) {
2059         vFrom.addr = (caddr_t) appData.highlightSquareColor;
2060         vFrom.size = strlen(appData.highlightSquareColor);
2061         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2062         if (vTo.addr == NULL) {
2063           appData.monoMode = True;
2064           forceMono = True;
2065         } else {
2066           highlightSquareColor = *(Pixel *) vTo.addr;
2067         }
2068     }
2069
2070     if (!appData.monoMode) {
2071         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
2072         vFrom.size = strlen(appData.premoveHighlightColor);
2073         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2074         if (vTo.addr == NULL) {
2075           appData.monoMode = True;
2076           forceMono = True;
2077         } else {
2078           premoveHighlightColor = *(Pixel *) vTo.addr;
2079         }
2080     }
2081
2082     if (forceMono) {
2083       fprintf(stderr, "%s: too few colors available; trying monochrome mode\n",
2084               programName);
2085     }
2086
2087     if (appData.monoMode && appData.debugMode) {
2088         fprintf(stderr, "white pixel = 0x%lx, black pixel = 0x%lx\n",
2089                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2090                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2091     }
2092     
2093     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2094         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2095         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2096         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2097         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2098         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2099         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2100         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2101         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2102         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2103       {
2104           if (appData.colorize) {
2105               fprintf(stderr,
2106                       "%s: can't parse color names; disabling colorization\n",
2107                       programName);
2108           }
2109           appData.colorize = FALSE;
2110       }
2111     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2112     textColors[ColorNone].attr = 0;
2113     
2114     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2115     
2116     /*
2117      * widget hierarchy
2118      */
2119     if (tinyLayout) {
2120         layoutName = "tinyLayout";
2121     } else if (smallLayout) {
2122         layoutName = "smallLayout";
2123     } else {
2124         layoutName = "normalLayout";
2125     }
2126     /* Outer layoutWidget is there only to provide a name for use in
2127        resources that depend on the layout style */
2128     layoutWidget =
2129       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2130                             layoutArgs, XtNumber(layoutArgs));
2131     formWidget =
2132       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2133                             formArgs, XtNumber(formArgs));
2134     XtSetArg(args[0], XtNdefaultDistance, &sep);
2135     XtGetValues(formWidget, args, 1);
2136     
2137     j = 0;
2138     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2139
2140     widgetList[j++] = whiteTimerWidget =
2141       XtCreateWidget("whiteTime", labelWidgetClass,
2142                      formWidget, timerArgs, XtNumber(timerArgs));
2143     XtSetArg(args[0], XtNfont, clockFontStruct);
2144     XtSetValues(whiteTimerWidget, args, 1);
2145     
2146     widgetList[j++] = blackTimerWidget =
2147       XtCreateWidget("blackTime", labelWidgetClass,
2148                      formWidget, timerArgs, XtNumber(timerArgs));
2149     XtSetArg(args[0], XtNfont, clockFontStruct);
2150     XtSetValues(blackTimerWidget, args, 1);
2151     
2152     if (appData.titleInWindow) {
2153         widgetList[j++] = titleWidget = 
2154           XtCreateWidget("title", labelWidgetClass, formWidget,
2155                          titleArgs, XtNumber(titleArgs));
2156     }
2157
2158     if (appData.showButtonBar) {
2159       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2160     }
2161
2162     widgetList[j++] = messageWidget =
2163       XtCreateWidget("message", labelWidgetClass, formWidget,
2164                      messageArgs, XtNumber(messageArgs));
2165     
2166     widgetList[j++] = boardWidget =
2167       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2168                      XtNumber(boardArgs));
2169
2170     XtManageChildren(widgetList, j);
2171     
2172     timerWidth = (boardWidth - sep) / 2;
2173     XtSetArg(args[0], XtNwidth, timerWidth);
2174     XtSetValues(whiteTimerWidget, args, 1);
2175     XtSetValues(blackTimerWidget, args, 1);
2176     
2177     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2178     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2179     XtGetValues(whiteTimerWidget, args, 2);
2180     
2181     if (appData.showButtonBar) {
2182       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2183       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2184       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2185     }
2186
2187     /*
2188      * formWidget uses these constraints but they are stored
2189      * in the children.
2190      */
2191     i = 0;
2192     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2193     XtSetValues(menuBarWidget, args, i);
2194     if (appData.titleInWindow) {
2195         if (smallLayout) {
2196             i = 0;
2197             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2198             XtSetValues(whiteTimerWidget, args, i);
2199             i = 0;
2200             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2201             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2202             XtSetValues(blackTimerWidget, args, i);
2203             i = 0;
2204             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2205             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2206             XtSetValues(titleWidget, args, i);
2207             i = 0;
2208             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2209             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2210             XtSetValues(messageWidget, args, i);
2211             if (appData.showButtonBar) {
2212               i = 0;
2213               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2214               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2215               XtSetValues(buttonBarWidget, args, i);
2216             }
2217         } else {
2218             i = 0;
2219             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2220             XtSetValues(whiteTimerWidget, args, i);
2221             i = 0;
2222             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2223             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2224             XtSetValues(blackTimerWidget, args, i);
2225             i = 0;
2226             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2227             XtSetValues(titleWidget, args, i);
2228             i = 0;
2229             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2230             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2231             XtSetValues(messageWidget, args, i);
2232             if (appData.showButtonBar) {
2233               i = 0;
2234               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2235               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2236               XtSetValues(buttonBarWidget, args, i);
2237             }
2238         }
2239     } else {
2240         i = 0;
2241         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2242         XtSetValues(whiteTimerWidget, args, i);
2243         i = 0;
2244         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2245         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2246         XtSetValues(blackTimerWidget, args, i);
2247         i = 0;
2248         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2249         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2250         XtSetValues(messageWidget, args, i);
2251         if (appData.showButtonBar) {
2252           i = 0;
2253           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2254           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2255           XtSetValues(buttonBarWidget, args, i);
2256         }
2257     }
2258     i = 0;
2259     XtSetArg(args[0], XtNfromVert, messageWidget);
2260     XtSetValues(boardWidget, args, 1);
2261
2262     XtRealizeWidget(shellWidget);
2263
2264     /*
2265      * Correct the width of the message and title widgets.
2266      * It is not known why some systems need the extra fudge term.
2267      * The value "2" is probably larger than needed.
2268      */
2269     XawFormDoLayout(formWidget, False);
2270 #define WIDTH_FUDGE 2
2271     i = 0;
2272     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2273     XtSetArg(args[i], XtNheight, &h);  i++;
2274     XtGetValues(messageWidget, args, i);
2275     if (appData.showButtonBar) {
2276       i = 0;
2277       XtSetArg(args[i], XtNwidth, &w);  i++;
2278       XtGetValues(buttonBarWidget, args, i);
2279       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2280     } else {
2281       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2282     }
2283
2284     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2285     if (gres != XtGeometryYes && appData.debugMode) {
2286       fprintf(stderr, "%s: messageWidget geometry error %d %d %d %d %d\n",
2287               programName, gres, w, h, wr, hr);
2288     }
2289     
2290     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2291     /* The size used for the child widget in layout lags one resize behind
2292        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2293     w--;
2294     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2295     if (gres != XtGeometryYes && appData.debugMode) {
2296       fprintf(stderr, "%s: messageWidget geometry error %d %d %d %d %d\n",
2297               programName, gres, w, h, wr, hr);
2298     }
2299     /* !! end hack */
2300
2301     if (appData.titleInWindow) {
2302         i = 0;
2303         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2304         XtSetArg(args[i], XtNheight, &h);  i++;
2305         XtGetValues(titleWidget, args, i);
2306         if (smallLayout) {
2307             w = boardWidth - 2*bor;
2308         } else {
2309             XtSetArg(args[0], XtNwidth, &w);
2310             XtGetValues(menuBarWidget, args, 1);
2311             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2312         }
2313
2314         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2315         if (gres != XtGeometryYes && appData.debugMode) {
2316             fprintf(stderr,
2317                     "%s: titleWidget geometry error %d %d %d %d %d\n",
2318                     programName, gres, w, h, wr, hr);
2319         }
2320     }
2321     XawFormDoLayout(formWidget, True);
2322
2323     xBoardWindow = XtWindow(boardWidget);
2324     
2325     /* 
2326      * Create X checkmark bitmap and initialize option menu checks.
2327      */
2328     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2329                checkmark_bits, checkmark_width, checkmark_height);
2330     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2331     if (appData.alwaysPromoteToQueen) {
2332         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2333                     args, 1);
2334     }
2335     if (appData.animateDragging) {
2336         XtSetValues(XtNameToWidget(menuBarWidget,
2337                                    "menuOptions.Animate Dragging"),
2338                     args, 1);
2339     }
2340     if (appData.animate) {
2341         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2342                     args, 1);
2343     }
2344     if (appData.autoComment) {
2345         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2346                     args, 1);
2347     }
2348     if (appData.autoCallFlag) {
2349         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2350                     args, 1);
2351     }
2352     if (appData.autoFlipView) {
2353         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2354                     args, 1);
2355     }
2356     if (appData.autoObserve) {
2357         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2358                     args, 1);
2359     }
2360     if (appData.autoRaiseBoard) {
2361         XtSetValues(XtNameToWidget(menuBarWidget,
2362                                    "menuOptions.Auto Raise Board"), args, 1);
2363     }
2364     if (appData.autoSaveGames) {
2365         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2366                     args, 1);
2367     }
2368     if (appData.saveGameFile[0] != NULLCHAR) {
2369         /* Can't turn this off from menu */
2370         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2371                     args, 1);
2372         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2373                        False);
2374
2375     }
2376     if (appData.blindfold) {
2377         XtSetValues(XtNameToWidget(menuBarWidget,
2378                                    "menuOptions.Blindfold"), args, 1);
2379     }
2380     if (appData.flashCount > 0) {
2381         XtSetValues(XtNameToWidget(menuBarWidget,
2382                                    "menuOptions.Flash Moves"),
2383                     args, 1);
2384     }
2385     if (appData.getMoveList) {
2386         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2387                     args, 1);
2388     }
2389 #if HIGHDRAG
2390     if (appData.highlightDragging) {
2391         XtSetValues(XtNameToWidget(menuBarWidget,
2392                                    "menuOptions.Highlight Dragging"),
2393                     args, 1);
2394     }
2395 #endif
2396     if (appData.highlightLastMove) {
2397         XtSetValues(XtNameToWidget(menuBarWidget,
2398                                    "menuOptions.Highlight Last Move"),
2399                     args, 1);
2400     }
2401     if (appData.icsAlarm) {
2402         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2403                     args, 1);
2404     }
2405     if (appData.ringBellAfterMoves) {
2406         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2407                     args, 1);
2408     }
2409     if (appData.oldSaveStyle) {
2410         XtSetValues(XtNameToWidget(menuBarWidget,
2411                                    "menuOptions.Old Save Style"), args, 1);
2412     }
2413     if (appData.periodicUpdates) {
2414         XtSetValues(XtNameToWidget(menuBarWidget,
2415                                    "menuOptions.Periodic Updates"), args, 1);
2416     }   
2417     if (appData.ponderNextMove) {
2418         XtSetValues(XtNameToWidget(menuBarWidget,
2419                                    "menuOptions.Ponder Next Move"), args, 1);
2420     }   
2421     if (appData.popupExitMessage) {
2422         XtSetValues(XtNameToWidget(menuBarWidget,
2423                                    "menuOptions.Popup Exit Message"), args, 1);
2424     }   
2425     if (appData.popupMoveErrors) {
2426         XtSetValues(XtNameToWidget(menuBarWidget,
2427                                    "menuOptions.Popup Move Errors"), args, 1);
2428     }   
2429     if (appData.premove) {
2430         XtSetValues(XtNameToWidget(menuBarWidget,
2431                                    "menuOptions.Premove"), args, 1);
2432     }
2433     if (appData.quietPlay) {
2434         XtSetValues(XtNameToWidget(menuBarWidget,
2435                                    "menuOptions.Quiet Play"), args, 1);
2436     }
2437     if (appData.showCoords) {
2438         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2439                     args, 1);
2440     }
2441     if (appData.showThinking) {
2442         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Thinking"),
2443                     args, 1);
2444     }
2445     if (appData.testLegality) {
2446         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2447                     args, 1);
2448     }
2449
2450     /*
2451      * Create an icon.
2452      */
2453     ReadBitmap(&wIconPixmap, "icon_white.bm",
2454                icon_white_bits, icon_white_width, icon_white_height);
2455     ReadBitmap(&bIconPixmap, "icon_black.bm",
2456                icon_black_bits, icon_black_width, icon_black_height);
2457     iconPixmap = wIconPixmap;
2458     i = 0;
2459     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2460     XtSetValues(shellWidget, args, i);
2461     
2462     /*
2463      * Create a cursor for the board widget.
2464      */
2465     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2466     XChangeWindowAttributes(xDisplay, xBoardWindow,
2467                             CWCursor, &window_attributes);
2468     
2469     /*
2470      * Inhibit shell resizing.
2471      */
2472     shellArgs[0].value = (XtArgVal) &w;
2473     shellArgs[1].value = (XtArgVal) &h;
2474     XtGetValues(shellWidget, shellArgs, 2);
2475     shellArgs[4].value = shellArgs[2].value = w;
2476     shellArgs[5].value = shellArgs[3].value = h;
2477     XtSetValues(shellWidget, &shellArgs[2], 4);
2478     
2479     CatchDeleteWindow(shellWidget, "QuitProc");
2480
2481     CreateGCs();
2482     CreateGrid();
2483 #if HAVE_LIBXPM
2484     if (appData.bitmapDirectory[0] != NULLCHAR) {
2485       CreatePieces();
2486     } else {
2487       CreateXPMPieces();
2488     }
2489 #else
2490     CreateXIMPieces();
2491     /* Create regular pieces */
2492     if (!useImages) CreatePieces();
2493 #endif  
2494
2495     CreatePieceMenus();
2496
2497     if (appData.animate || appData.animateDragging)
2498       CreateAnimVars();
2499     
2500     XtAugmentTranslations(formWidget,
2501                           XtParseTranslationTable(globalTranslations));
2502     XtAugmentTranslations(boardWidget,
2503                           XtParseTranslationTable(boardTranslations));
2504     XtAugmentTranslations(whiteTimerWidget,
2505                           XtParseTranslationTable(whiteTranslations));
2506     XtAugmentTranslations(blackTimerWidget,
2507                           XtParseTranslationTable(blackTranslations));
2508
2509     /* Why is the following needed on some versions of X instead
2510      * of a translation? */
2511     XtAddEventHandler(boardWidget, ExposureMask, False,
2512                       (XtEventHandler) EventProc, NULL);
2513     /* end why */
2514
2515     InitBackEnd2();
2516     
2517     if (errorExitStatus == -1) {
2518         if (appData.icsActive) {
2519             /* We now wait until we see "login:" from the ICS before
2520                sending the logon script (problems with timestamp otherwise) */
2521             /*ICSInitScript();*/
2522             if (appData.icsInputBox) ICSInputBoxPopUp();
2523         }
2524
2525         signal(SIGINT, IntSigHandler);
2526         signal(SIGTERM, IntSigHandler);
2527         if (*appData.cmailGameName != NULLCHAR) {
2528             signal(SIGUSR1, CmailSigHandler);
2529         }
2530     }
2531
2532     XtAppMainLoop(appContext);
2533     return 0;
2534 }
2535
2536 void
2537 ShutDownFrontEnd()
2538 {
2539     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2540         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2541     }
2542     unlink(gameCopyFilename);
2543     unlink(gamePasteFilename);
2544 }
2545
2546 RETSIGTYPE
2547 IntSigHandler(sig)
2548      int sig;
2549 {
2550     ExitEvent(sig);
2551 }
2552
2553 RETSIGTYPE
2554 CmailSigHandler(sig)
2555      int sig;
2556 {
2557     int dummy = 0;
2558     int error;
2559
2560     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2561
2562     /* Activate call-back function CmailSigHandlerCallBack()             */
2563     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2564
2565     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2566 }
2567
2568 void
2569 CmailSigHandlerCallBack(isr, closure, message, count, error)
2570      InputSourceRef isr;
2571      VOIDSTAR closure;
2572      char *message;
2573      int count;
2574      int error;
2575 {
2576     BoardToTop();
2577     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2578 }
2579 /**** end signal code ****/
2580
2581
2582 void
2583 ICSInitScript()
2584 {
2585     FILE *f;
2586     char buf[MSG_SIZ];
2587     char *p;
2588
2589     f = fopen(appData.icsLogon, "r");
2590     if (f == NULL) {
2591         p = getenv("HOME");
2592         if (p != NULL) {
2593             strcpy(buf, p);
2594             strcat(buf, "/");
2595             strcat(buf, appData.icsLogon);
2596             f = fopen(buf, "r");
2597         }
2598     }
2599     if (f != NULL)
2600       ProcessICSInitScript(f);
2601 }
2602
2603 void
2604 ResetFrontEnd()
2605 {
2606     CommentPopDown();
2607     EditCommentPopDown();
2608     TagsPopDown();
2609     return;
2610 }
2611
2612 typedef struct {
2613     char *name;
2614     Boolean value;
2615 } Enables;
2616
2617 void
2618 SetMenuEnables(enab)
2619      Enables *enab;
2620 {
2621   Widget w;
2622   if (!menuBarWidget) return;
2623   while (enab->name != NULL) {
2624     w = XtNameToWidget(menuBarWidget, enab->name);
2625     if (w == NULL) {
2626       DisplayError(enab->name, 0);
2627     } else {
2628       XtSetSensitive(w, enab->value);
2629     }
2630     enab++;
2631   }
2632 }
2633
2634 Enables icsEnables[] = {
2635     { "menuFile.Mail Move", False },
2636     { "menuFile.Reload CMail Message", False },
2637     { "menuMode.Machine Black", False },
2638     { "menuMode.Machine White", False },
2639     { "menuMode.Analysis Mode", False },
2640     { "menuMode.Analyze File", False },
2641     { "menuMode.Two Machines", False },
2642 #ifndef ZIPPY
2643     { "menuHelp.Hint", False },
2644     { "menuHelp.Book", False },
2645     { "menuStep.Move Now", False },
2646     { "menuOptions.Periodic Updates", False },  
2647     { "menuOptions.Show Thinking", False },
2648     { "menuOptions.Ponder Next Move", False },
2649 #endif
2650     { NULL, False }
2651 };
2652
2653 Enables ncpEnables[] = {    
2654     { "menuFile.Mail Move", False },
2655     { "menuFile.Reload CMail Message", False },
2656     { "menuMode.Machine White", False },
2657     { "menuMode.Machine Black", False },
2658     { "menuMode.Analysis Mode", False },
2659     { "menuMode.Analyze File", False },
2660     { "menuMode.Two Machines", False },
2661     { "menuMode.ICS Client", False },
2662     { "menuMode.ICS Input Box", False },
2663     { "Action", False },
2664     { "menuStep.Revert", False },
2665     { "menuStep.Move Now", False },
2666     { "menuStep.Retract Move", False },
2667     { "menuOptions.Auto Comment", False },
2668     { "menuOptions.Auto Flag", False },
2669     { "menuOptions.Auto Flip View", False },
2670     { "menuOptions.Auto Observe", False },
2671     { "menuOptions.Auto Raise Board", False },
2672     { "menuOptions.Get Move List", False },
2673     { "menuOptions.ICS Alarm", False },
2674     { "menuOptions.Move Sound", False },
2675     { "menuOptions.Quiet Play", False },
2676     { "menuOptions.Show Thinking", False },
2677     { "menuOptions.Periodic Updates", False },  
2678     { "menuOptions.Ponder Next Move", False },
2679     { "menuHelp.Hint", False },
2680     { "menuHelp.Book", False },
2681     { NULL, False }
2682 };
2683
2684 Enables gnuEnables[] = {    
2685     { "menuMode.ICS Client", False },
2686     { "menuMode.ICS Input Box", False },
2687     { "menuAction.Accept", False },
2688     { "menuAction.Decline", False },
2689     { "menuAction.Rematch", False },
2690     { "menuAction.Adjourn", False },
2691     { "menuAction.Stop Examining", False },
2692     { "menuAction.Stop Observing", False },
2693     { "menuStep.Revert", False },
2694     { "menuOptions.Auto Comment", False },
2695     { "menuOptions.Auto Observe", False },
2696     { "menuOptions.Auto Raise Board", False },
2697     { "menuOptions.Get Move List", False },
2698     { "menuOptions.Premove", False },
2699     { "menuOptions.Quiet Play", False },
2700
2701     /* The next two options rely on SetCmailMode being called *after*    */
2702     /* SetGNUMode so that when GNU is being used to give hints these     */
2703     /* menu options are still available                                  */
2704
2705     { "menuFile.Mail Move", False },
2706     { "menuFile.Reload CMail Message", False },
2707     { NULL, False }
2708 };
2709
2710 Enables cmailEnables[] = {    
2711     { "Action", True },
2712     { "menuAction.Call Flag", False },
2713     { "menuAction.Draw", True },
2714     { "menuAction.Adjourn", False },
2715     { "menuAction.Abort", False },
2716     { "menuAction.Stop Observing", False },
2717     { "menuAction.Stop Examining", False },
2718     { "menuFile.Mail Move", True },
2719     { "menuFile.Reload CMail Message", True },
2720     { NULL, False }
2721 };
2722
2723 Enables trainingOnEnables[] = {    
2724   { "menuMode.Edit Comment", False },
2725   { "menuMode.Pause", False },
2726   { "menuStep.Forward", False },
2727   { "menuStep.Backward", False },
2728   { "menuStep.Forward to End", False },
2729   { "menuStep.Back to Start", False },
2730   { "menuStep.Move Now", False },
2731   { "menuStep.Truncate Game", False },
2732   { NULL, False }
2733 };
2734
2735 Enables trainingOffEnables[] = {    
2736   { "menuMode.Edit Comment", True },
2737   { "menuMode.Pause", True },
2738   { "menuStep.Forward", True },
2739   { "menuStep.Backward", True },
2740   { "menuStep.Forward to End", True },
2741   { "menuStep.Back to Start", True },
2742   { "menuStep.Move Now", True },
2743   { "menuStep.Truncate Game", True },
2744   { NULL, False }
2745 };
2746
2747 Enables machineThinkingEnables[] = {
2748   { "menuFile.Load Game", False },
2749   { "menuFile.Load Next Game", False },
2750   { "menuFile.Load Previous Game", False },
2751   { "menuFile.Reload Same Game", False },
2752   { "menuFile.Paste Game", False },
2753   { "menuFile.Load Position", False },
2754   { "menuFile.Load Next Position", False },
2755   { "menuFile.Load Previous Position", False },
2756   { "menuFile.Reload Same Position", False },
2757   { "menuFile.Paste Position", False },
2758   { "menuMode.Machine White", False },
2759   { "menuMode.Machine Black", False },
2760   { "menuMode.Two Machines", False },
2761   { "menuStep.Retract Move", False },
2762   { NULL, False }
2763 };
2764
2765 Enables userThinkingEnables[] = {
2766   { "menuFile.Load Game", True },
2767   { "menuFile.Load Next Game", True },
2768   { "menuFile.Load Previous Game", True },
2769   { "menuFile.Reload Same Game", True },
2770   { "menuFile.Paste Game", True },
2771   { "menuFile.Load Position", True },
2772   { "menuFile.Load Next Position", True },
2773   { "menuFile.Load Previous Position", True },
2774   { "menuFile.Reload Same Position", True },
2775   { "menuFile.Paste Position", True },
2776   { "menuMode.Machine White", True },
2777   { "menuMode.Machine Black", True },
2778   { "menuMode.Two Machines", True },
2779   { "menuStep.Retract Move", True },
2780   { NULL, False }
2781 };
2782
2783 void SetICSMode()
2784 {
2785   SetMenuEnables(icsEnables);
2786 }
2787
2788 void
2789 SetNCPMode()
2790 {
2791   SetMenuEnables(ncpEnables);
2792 }
2793
2794 void
2795 SetGNUMode()
2796 {
2797   SetMenuEnables(gnuEnables);
2798 }
2799
2800 void
2801 SetCmailMode()
2802 {
2803   SetMenuEnables(cmailEnables);
2804 }
2805
2806 void
2807 SetTrainingModeOn()
2808 {
2809   SetMenuEnables(trainingOnEnables);
2810   if (appData.showButtonBar) {
2811     XtSetSensitive(buttonBarWidget, False);
2812   }
2813   CommentPopDown();
2814 }
2815
2816 void
2817 SetTrainingModeOff()
2818 {
2819   SetMenuEnables(trainingOffEnables);
2820   if (appData.showButtonBar) {
2821     XtSetSensitive(buttonBarWidget, True);
2822   }
2823 }
2824
2825 void
2826 SetUserThinkingEnables()
2827 {
2828   if (appData.noChessProgram) return;
2829   SetMenuEnables(userThinkingEnables);
2830 }
2831
2832 void
2833 SetMachineThinkingEnables()
2834 {
2835   if (appData.noChessProgram) return;
2836   SetMenuEnables(machineThinkingEnables);
2837   switch (gameMode) {
2838   case MachinePlaysBlack:
2839   case MachinePlaysWhite:
2840   case TwoMachinesPlay:
2841     XtSetSensitive(XtNameToWidget(menuBarWidget,
2842                                   ModeToWidgetName(gameMode)), True);
2843     break;
2844   default:
2845     break;
2846   }
2847 }
2848
2849 #define Abs(n) ((n)<0 ? -(n) : (n))
2850
2851 /*
2852  * Find a font that matches "pattern" that is as close as
2853  * possible to the targetPxlSize.  Prefer fonts that are k
2854  * pixels smaller to fonts that are k pixels larger.  The
2855  * pattern must be in the X Consortium standard format, 
2856  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
2857  * The return value should be freed with XtFree when no
2858  * longer needed.
2859  */
2860 char *FindFont(pattern, targetPxlSize)
2861      char *pattern;
2862      int targetPxlSize;
2863 {
2864     char **fonts, *p, *best, *scalable, *scalableTail;
2865     int i, j, nfonts, minerr, err, pxlSize;
2866
2867     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
2868     if (nfonts < 1) {
2869         fprintf(stderr, "%s: no fonts match pattern %s\n",
2870                 programName, pattern);
2871         exit(2);
2872     }
2873     best = fonts[0];
2874     scalable = NULL;
2875     minerr = 999999;
2876     for (i=0; i<nfonts; i++) {
2877         j = 0;
2878         p = fonts[i];
2879         if (*p != '-') continue;
2880         while (j < 7) {
2881             if (*p == NULLCHAR) break;
2882             if (*p++ == '-') j++;
2883         }
2884         if (j < 7) continue;
2885         pxlSize = atoi(p);
2886         if (pxlSize == 0) {
2887             scalable = fonts[i];
2888             scalableTail = p;
2889         } else {
2890             err = pxlSize - targetPxlSize;
2891             if (Abs(err) < Abs(minerr) ||
2892                 (minerr > 0 && err < 0 && -err == minerr)) {
2893                 best = fonts[i];
2894                 minerr = err;
2895             }
2896         }
2897     }
2898     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
2899         /* If the error is too big and there is a scalable font,
2900            use the scalable font. */
2901         int headlen = scalableTail - scalable;
2902         p = (char *) XtMalloc(strlen(scalable) + 10);
2903         while (isdigit(*scalableTail)) scalableTail++;
2904         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
2905     } else {
2906         p = (char *) XtMalloc(strlen(best) + 1);
2907         strcpy(p, best);
2908     }
2909     if (appData.debugMode) {
2910         fprintf(debugFP, "resolved %s at pixel size %d\n  to %s\n",
2911                 pattern, targetPxlSize, p);
2912     }
2913     XFreeFontNames(fonts);
2914     return p;
2915 }
2916
2917 void CreateGCs()
2918 {
2919     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
2920       | GCBackground | GCFunction | GCPlaneMask;
2921     XGCValues gc_values;
2922     GC copyInvertedGC;
2923     
2924     gc_values.plane_mask = AllPlanes;
2925     gc_values.line_width = lineGap;
2926     gc_values.line_style = LineSolid;
2927     gc_values.function = GXcopy;
2928     
2929     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2930     gc_values.background = XBlackPixel(xDisplay, xScreen);
2931     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
2932     
2933     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2934     gc_values.background = XWhitePixel(xDisplay, xScreen);
2935     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
2936     XSetFont(xDisplay, coordGC, coordFontID);
2937     
2938     if (appData.monoMode) {
2939         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2940         gc_values.background = XWhitePixel(xDisplay, xScreen);
2941         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);      
2942
2943         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
2944         gc_values.background = XBlackPixel(xDisplay, xScreen);
2945         lightSquareGC = wbPieceGC 
2946           = XtGetGC(shellWidget, value_mask, &gc_values);
2947
2948         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
2949         gc_values.background = XWhitePixel(xDisplay, xScreen);
2950         darkSquareGC = bwPieceGC
2951           = XtGetGC(shellWidget, value_mask, &gc_values);
2952
2953         if (DefaultDepth(xDisplay, xScreen) == 1) {
2954             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
2955             gc_values.function = GXcopyInverted;
2956             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
2957             gc_values.function = GXcopy;
2958             if (XBlackPixel(xDisplay, xScreen) == 1) {
2959                 bwPieceGC = darkSquareGC;
2960                 wbPieceGC = copyInvertedGC;
2961             } else {
2962                 bwPieceGC = copyInvertedGC;
2963                 wbPieceGC = lightSquareGC;
2964             }
2965         }
2966     } else {
2967         gc_values.foreground = highlightSquareColor;
2968         gc_values.background = highlightSquareColor;
2969         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);      
2970
2971         gc_values.foreground = premoveHighlightColor;
2972         gc_values.background = premoveHighlightColor;
2973         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);       
2974
2975         gc_values.foreground = lightSquareColor;
2976         gc_values.background = darkSquareColor;
2977         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2978         
2979         gc_values.foreground = darkSquareColor;
2980         gc_values.background = lightSquareColor;
2981         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2982
2983         gc_values.foreground = jailSquareColor;
2984         gc_values.background = jailSquareColor;
2985         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
2986
2987         gc_values.foreground = whitePieceColor;
2988         gc_values.background = darkSquareColor;
2989         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2990         
2991         gc_values.foreground = whitePieceColor;
2992         gc_values.background = lightSquareColor;
2993         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2994         
2995         gc_values.foreground = whitePieceColor;
2996         gc_values.background = jailSquareColor;
2997         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
2998         
2999         gc_values.foreground = blackPieceColor;
3000         gc_values.background = darkSquareColor;
3001         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3002         
3003         gc_values.foreground = blackPieceColor;
3004         gc_values.background = lightSquareColor;
3005         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3006
3007         gc_values.foreground = blackPieceColor;
3008         gc_values.background = jailSquareColor;
3009         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3010     }
3011 }
3012
3013 void loadXIM(xim, xmask, filename, dest, mask)
3014      XImage *xim;
3015      XImage *xmask;
3016      char *filename;
3017      Pixmap *dest;
3018      Pixmap *mask;
3019 {
3020     int x, y, w, h, p;
3021     FILE *fp;
3022     Pixmap temp;
3023     XGCValues   values;
3024     GC maskGC;
3025
3026     fp = fopen(filename, "rb");
3027     if (!fp) {
3028         fprintf(stderr, "%s: error loading XIM!\n", programName);
3029         exit(1);
3030     }
3031           
3032     w = fgetc(fp);
3033     h = fgetc(fp);
3034   
3035     for (y=0; y<h; ++y) {
3036         for (x=0; x<h; ++x) {
3037             p = fgetc(fp);
3038
3039             switch (p) {
3040               case 0:   
3041                 XPutPixel(xim, x, y, blackPieceColor); 
3042                 if (xmask)
3043                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3044                 break;
3045               case 1:   
3046                 XPutPixel(xim, x, y, darkSquareColor); 
3047                 if (xmask)
3048                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3049                 break;
3050               case 2:   
3051                 XPutPixel(xim, x, y, whitePieceColor); 
3052                 if (xmask)
3053                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3054                 break;
3055               case 3:   
3056                 XPutPixel(xim, x, y, lightSquareColor);
3057                 if (xmask)
3058                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3059                 break;
3060             }
3061         }
3062     }
3063
3064     /* create Pixmap of piece */
3065     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3066                           w, h, xim->depth);
3067     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3068               0, 0, 0, 0, w, h);  
3069
3070     /* create Pixmap of clipmask 
3071        Note: We assume the white/black pieces have the same
3072              outline, so we make only 6 masks. This is okay
3073              since the XPM clipmask routines do the same. */
3074     if (xmask) {
3075       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3076                             w, h, xim->depth);
3077       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3078               0, 0, 0, 0, w, h);  
3079
3080       /* now create the 1-bit version */
3081       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3082                           w, h, 1);
3083
3084       values.foreground = 1;
3085       values.background = 0;
3086
3087       /* Don't use XtGetGC, not read only */
3088       maskGC = XCreateGC(xDisplay, *mask, 
3089                     GCForeground | GCBackground, &values);
3090       XCopyPlane(xDisplay, temp, *mask, maskGC, 
3091                   0, 0, squareSize, squareSize, 0, 0, 1);
3092       XFreePixmap(xDisplay, temp);
3093     }
3094 }
3095
3096 void CreateXIMPieces()
3097 {
3098     int piece, kind;
3099     char buf[MSG_SIZ];
3100     u_int ss;
3101     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3102     XImage *ximtemp;
3103
3104     ss = squareSize;
3105
3106     /* The XSynchronize calls were copied from CreatePieces.
3107        Not sure if needed, but can't hurt */
3108     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3109                                      buffering bug */
3110   
3111     /* temp needed by loadXIM() */
3112     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3113                  0, 0, ss, ss, AllPlanes, XYPixmap);
3114
3115     if (strlen(appData.pixmapDirectory) == 0) {
3116       useImages = 0;
3117     } else {
3118         useImages = 1;
3119         if (appData.monoMode) {
3120           DisplayFatalError("XIM pieces cannot be used in monochrome mode",
3121                             0, 2);
3122           ExitEvent(2);
3123         }
3124         fprintf(stderr, "\nLoading XIMs...\n");
3125         /* Load pieces */
3126         for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) {
3127             fprintf(stderr, "%d", piece+1);
3128             for (kind=0; kind<4; kind++) {
3129                 fprintf(stderr, ".");
3130                 sprintf(buf, "%s/%c%s%u.xim",
3131                         ExpandPathName(appData.pixmapDirectory),
3132                         ToLower(PieceToChar((ChessSquare)piece)),
3133                         ximkind[kind], ss);
3134                 ximPieceBitmap[kind][piece] =
3135                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3136                             0, 0, ss, ss, AllPlanes, XYPixmap);
3137                 if (appData.debugMode)
3138                   fprintf(stderr, "(File:%s:) ", buf);
3139                 loadXIM(ximPieceBitmap[kind][piece], 
3140                         ximtemp, buf,
3141                         &(xpmPieceBitmap[kind][piece]),
3142                         &(ximMaskPm[piece%6]));
3143             }
3144             fprintf(stderr," ");
3145         }
3146         /* Load light and dark squares */
3147         /* If the LSQ and DSQ pieces don't exist, we will 
3148            draw them with solid squares. */
3149         sprintf(buf, "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3150         if (access(buf, 0) != 0) {
3151             useImageSqs = 0;
3152         } else {
3153             useImageSqs = 1;
3154             fprintf(stderr, "light square ");
3155             ximLightSquare= 
3156               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3157                         0, 0, ss, ss, AllPlanes, XYPixmap);
3158             if (appData.debugMode)
3159               fprintf(stderr, "(File:%s:) ", buf);
3160
3161             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3162             fprintf(stderr, "dark square ");
3163             sprintf(buf, "%s/dsq%u.xim",
3164                     ExpandPathName(appData.pixmapDirectory), ss);
3165             if (appData.debugMode)
3166               fprintf(stderr, "(File:%s:) ", buf);
3167             ximDarkSquare= 
3168               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3169                         0, 0, ss, ss, AllPlanes, XYPixmap);
3170             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3171             xpmJailSquare = xpmLightSquare;
3172         }
3173         fprintf(stderr, "Done.\n");
3174     }
3175     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3176 }
3177
3178 #if HAVE_LIBXPM
3179 void CreateXPMPieces()
3180 {
3181     int piece, kind, r;
3182     char buf[MSG_SIZ];
3183     u_int ss = squareSize;
3184     XpmAttributes attr;
3185     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3186     XpmColorSymbol symbols[4];
3187
3188 #if 0
3189     /* Apparently some versions of Xpm don't define XpmFormat at all --tpm */
3190     if (appData.debugMode) {
3191         fprintf(stderr, "XPM Library Version: %d.%d%c\n", 
3192                 XpmFormat, XpmVersion, (char)('a' + XpmRevision - 1));
3193     }
3194 #endif
3195   
3196     /* The XSynchronize calls were copied from CreatePieces.
3197        Not sure if needed, but can't hurt */
3198     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3199   
3200     /* Setup translations so piece colors match square colors */
3201     symbols[0].name = "light_piece";
3202     symbols[0].value = appData.whitePieceColor;
3203     symbols[1].name = "dark_piece";
3204     symbols[1].value = appData.blackPieceColor;
3205     symbols[2].name = "light_square";
3206     symbols[2].value = appData.lightSquareColor;
3207     symbols[3].name = "dark_square";
3208     symbols[3].value = appData.darkSquareColor;
3209
3210     attr.valuemask = XpmColorSymbols;
3211     attr.colorsymbols = symbols;
3212     attr.numsymbols = 4;
3213
3214     if (appData.monoMode) {
3215       DisplayFatalError("XPM pieces cannot be used in monochrome mode",
3216                         0, 2);
3217       ExitEvent(2);
3218     }
3219     if (strlen(appData.pixmapDirectory) == 0) {
3220         XpmPieces* pieces = builtInXpms;
3221         useImages = 1;
3222         /* Load pieces */
3223         while (pieces->size != squareSize && pieces->size) pieces++;
3224         if (!pieces->size) {
3225           fprintf(stderr, "No builtin XPM pieces of size %d\n", squareSize);
3226           exit(1);
3227         }
3228         for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) {
3229             for (kind=0; kind<4; kind++) {
3230
3231                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3232                                                pieces->xpm[piece][kind],
3233                                                &(xpmPieceBitmap[kind][piece]),
3234                                                NULL, &attr)) != 0) {
3235                   fprintf(stderr, "Error %d loading XPM image \"%s\"\n",
3236                           r, buf);
3237                   exit(1); 
3238                 }       
3239             }   
3240         }
3241         useImageSqs = 0;
3242         xpmJailSquare = xpmLightSquare;
3243     } else {
3244         useImages = 1;
3245         
3246         fprintf(stderr, "\nLoading XPMs...\n");
3247
3248         /* Load pieces */
3249         for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) {
3250             fprintf(stderr, "%d ", piece+1);
3251             for (kind=0; kind<4; kind++) {
3252                 sprintf(buf, "%s/%c%s%u.xpm",
3253                         ExpandPathName(appData.pixmapDirectory),
3254                         ToLower(PieceToChar((ChessSquare)piece)),
3255                         xpmkind[kind], ss);
3256                 if (appData.debugMode) {
3257                     fprintf(stderr, "(File:%s:) ", buf);
3258                 }
3259                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3260                                            &(xpmPieceBitmap[kind][piece]),
3261                                            NULL, &attr)) != 0) {
3262                     fprintf(stderr, "Error %d loading XPM file \"%s\"\n",
3263                             r, buf);
3264                     exit(1); 
3265                 }       
3266             }   
3267         }
3268         /* Load light and dark squares */
3269         /* If the LSQ and DSQ pieces don't exist, we will 
3270            draw them with solid squares. */
3271         fprintf(stderr, "light square ");
3272         sprintf(buf, "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3273         if (access(buf, 0) != 0) {
3274             useImageSqs = 0;
3275         } else {
3276             useImageSqs = 1;
3277             if (appData.debugMode)
3278               fprintf(stderr, "(File:%s:) ", buf);
3279
3280             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3281                                        &xpmLightSquare, NULL, &attr)) != 0) {
3282                 fprintf(stderr, "Error %d loading XPM file \"%s\"\n", r, buf);
3283                 exit(1);
3284             }
3285             fprintf(stderr, "dark square ");
3286             sprintf(buf, "%s/dsq%u.xpm",
3287                     ExpandPathName(appData.pixmapDirectory), ss);
3288             if (appData.debugMode) {
3289                 fprintf(stderr, "(File:%s:) ", buf);
3290             }
3291             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3292                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3293                 fprintf(stderr, "Error %d loading XPM file \"%s\"\n", r, buf);
3294                 exit(1);
3295             }
3296         }
3297         xpmJailSquare = xpmLightSquare;
3298         fprintf(stderr, "Done.\n");
3299     }
3300     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3301                                       buffering bug */  
3302 }
3303 #endif /* HAVE_LIBXPM */
3304
3305 #if HAVE_LIBXPM
3306 /* No built-in bitmaps */
3307 void CreatePieces()
3308 {
3309     int piece, kind;
3310     char buf[MSG_SIZ];
3311     u_int ss = squareSize;
3312         
3313     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3314                                      buffering bug */
3315
3316     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3317         for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) {
3318             sprintf(buf, "%c%u%c.bm", ToLower(PieceToChar((ChessSquare)piece)),
3319                     ss, kind == SOLID ? 's' : 'o');
3320             ReadBitmap(&pieceBitmap[kind][piece], buf, NULL, ss, ss);
3321         }
3322     }
3323     
3324     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3325                                       buffering bug */
3326 }
3327 #else
3328 /* With built-in bitmaps */
3329 void CreatePieces()
3330 {
3331     BuiltInBits* bib = builtInBits;
3332     int piece, kind;
3333     char buf[MSG_SIZ];
3334     u_int ss = squareSize;
3335         
3336     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3337                                      buffering bug */
3338
3339     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3340
3341     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3342         for (piece = (int) WhitePawn; piece <= (int) WhiteKing; piece++) {
3343             sprintf(buf, "%c%u%c.bm", ToLower(PieceToChar((ChessSquare)piece)),
3344                     ss, kind == SOLID ? 's' : 'o');
3345             ReadBitmap(&pieceBitmap[kind][piece], buf,
3346                        bib->bits[kind][piece], ss, ss);
3347         }
3348     }
3349     
3350     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3351                                       buffering bug */
3352 }
3353 #endif
3354
3355 void ReadBitmap(pm, name, bits, wreq, hreq)
3356      Pixmap *pm;
3357      String name;
3358      unsigned char bits[];
3359      u_int wreq, hreq;
3360 {
3361     int x_hot, y_hot;
3362     u_int w, h;
3363     int errcode;
3364     char msg[MSG_SIZ], fullname[MSG_SIZ];
3365     
3366     if (*appData.bitmapDirectory != NULLCHAR) {
3367         strcpy(fullname, appData.bitmapDirectory);
3368         strcat(fullname, "/");
3369         strcat(fullname, name);
3370         errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3371                                   &w, &h, pm, &x_hot, &y_hot);
3372         if (errcode != BitmapSuccess) {
3373             switch (errcode) {
3374               case BitmapOpenFailed:
3375                 sprintf(msg, "Can't open bitmap file %s", fullname);
3376                 break;
3377               case BitmapFileInvalid:
3378                 sprintf(msg, "Invalid bitmap in file %s", fullname);
3379                 break;
3380               case BitmapNoMemory:
3381                 sprintf(msg, "Ran out of memory reading bitmap file %s",
3382                         fullname);
3383                 break;
3384               default:
3385                 sprintf(msg, "Unknown XReadBitmapFile error %d on file %s",
3386                         errcode, fullname);
3387                 break;
3388             }
3389             fprintf(stderr, "%s: %s...using built-in\n",
3390                     programName, msg);
3391         } else if (w != wreq || h != hreq) {
3392             fprintf(stderr,
3393                     "%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n",
3394                     programName, fullname, w, h, wreq, hreq);
3395         } else {
3396             return;
3397         }
3398     }
3399     if (bits == NULL) {
3400         fprintf(stderr, "%s: No built-in bitmap for %s; giving up\n",
3401                 programName, name);
3402         exit(1);
3403     } else {
3404         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3405                                     wreq, hreq);
3406     }
3407 }
3408
3409 void CreateGrid()
3410 {
3411     int i;
3412     
3413     if (lineGap == 0) return;
3414     for (i = 0; i < BOARD_SIZE + 1; i++) {
3415         gridSegments[i].x1 = 0;
3416         gridSegments[i].x2 =
3417           lineGap + BOARD_SIZE * (squareSize + lineGap);
3418         gridSegments[i].y1 = gridSegments[i].y2
3419           = lineGap / 2 + (i * (squareSize + lineGap));
3420
3421         gridSegments[i + BOARD_SIZE + 1].y1 = 0;
3422         gridSegments[i + BOARD_SIZE + 1].y2 =
3423           BOARD_SIZE * (squareSize + lineGap);
3424         gridSegments[i + BOARD_SIZE + 1].x1 =
3425           gridSegments[i + BOARD_SIZE + 1].x2
3426             = lineGap / 2 + (i * (squareSize + lineGap));
3427     }
3428 }
3429
3430 static void MenuBarSelect(w, addr, index)
3431      Widget w;
3432      caddr_t addr;
3433      caddr_t index;
3434 {
3435     XtActionProc proc = (XtActionProc) addr;
3436
3437     (proc)(NULL, NULL, NULL, NULL);
3438 }
3439
3440 void CreateMenuBarPopup(parent, name, mb)
3441      Widget parent;
3442      String name;
3443      Menu *mb;
3444 {
3445     int j;
3446     Widget menu, entry;
3447     MenuItem *mi;
3448     Arg args[16];
3449
3450     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3451                               parent, NULL, 0);
3452     j = 0;
3453     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3454     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3455     mi = mb->mi;
3456     while (mi->string != NULL) {
3457         if (strcmp(mi->string, "----") == 0) {
3458             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3459                                           menu, args, j);
3460         } else {
3461             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3462                                           menu, args, j);
3463             XtAddCallback(entry, XtNcallback,
3464                           (XtCallbackProc) MenuBarSelect,
3465                           (caddr_t) mi->proc);
3466         }
3467         mi++;
3468     }
3469 }       
3470
3471 Widget CreateMenuBar(mb)
3472      Menu *mb;
3473 {
3474     int j;
3475     Widget anchor, menuBar;
3476     Arg args[16];
3477     char menuName[MSG_SIZ];
3478
3479     j = 0;
3480     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3481     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3482     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3483     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3484                              formWidget, args, j);
3485
3486     while (mb->name != NULL) {
3487         strcpy(menuName, "menu");
3488         strcat(menuName, mb->name);
3489         j = 0;
3490         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3491         if (tinyLayout) {
3492             char shortName[2];
3493             shortName[0] = mb->name[0];
3494             shortName[1] = NULLCHAR;
3495             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3496         }
3497         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3498         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3499                                        menuBar, args, j);
3500         CreateMenuBarPopup(menuBar, menuName, mb);
3501         mb++;
3502     }
3503     return menuBar;
3504 }
3505
3506 Widget CreateButtonBar(mi)
3507      MenuItem *mi;
3508 {
3509     int j;
3510     Widget button, buttonBar;
3511     Arg args[16];
3512
3513     j = 0;
3514     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3515     if (tinyLayout) {
3516         XtSetArg(args[j], XtNhSpace, 0); j++;
3517     }
3518     XtSetArg(args[j], XtNborderWidth, 0); j++;
3519     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3520     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3521                                formWidget, args, j);
3522
3523     while (mi->string != NULL) {
3524         j = 0;
3525         if (tinyLayout) {
3526             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3527             XtSetArg(args[j], XtNborderWidth, 0); j++;
3528         }
3529         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3530                                        buttonBar, args, j);
3531         XtAddCallback(button, XtNcallback,
3532                       (XtCallbackProc) MenuBarSelect,
3533                       (caddr_t) mi->proc);
3534         mi++;
3535     }
3536     return buttonBar;
3537 }     
3538
3539 Widget
3540 CreatePieceMenu(name, color)
3541      char *name;
3542      int color;
3543 {
3544     int i;
3545     Widget entry, menu;
3546     Arg args[16];
3547     ChessSquare selection;
3548
3549     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3550                               boardWidget, args, 0);
3551     
3552     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3553         String item = pieceMenuStrings[color][i];
3554         
3555         if (strcmp(item, "----") == 0) {
3556             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3557                                           menu, NULL, 0);
3558         } else {
3559             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3560                                           menu, NULL, 0);
3561             selection = pieceMenuTranslation[color][i];
3562             XtAddCallback(entry, XtNcallback,
3563                           (XtCallbackProc) PieceMenuSelect,
3564                           (caddr_t) selection);
3565             if (selection == WhitePawn || selection == BlackPawn) {
3566                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3567                 XtSetValues(menu, args, 1);
3568             }
3569         }
3570     }
3571     return menu;
3572 }
3573
3574 void
3575 CreatePieceMenus()
3576 {
3577     int i;
3578     Widget entry;
3579     Arg args[16];
3580     ChessSquare selection;
3581
3582     whitePieceMenu = CreatePieceMenu("menuW", 0);
3583     blackPieceMenu = CreatePieceMenu("menuB", 1);
3584     
3585     XtRegisterGrabAction(PieceMenuPopup, True,
3586                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3587                          GrabModeAsync, GrabModeAsync);
3588
3589     XtSetArg(args[0], XtNlabel, "Drop");
3590     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3591                                   boardWidget, args, 1);
3592     for (i = 0; i < DROP_MENU_SIZE; i++) {
3593         String item = dropMenuStrings[i];
3594         
3595         if (strcmp(item, "----") == 0) {
3596             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3597                                           dropMenu, NULL, 0);
3598         } else {
3599             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3600                                           dropMenu, NULL, 0);
3601             selection = dropMenuTranslation[i];
3602             XtAddCallback(entry, XtNcallback,
3603                           (XtCallbackProc) DropMenuSelect,
3604                           (caddr_t) selection);
3605         }
3606     }
3607 }       
3608
3609 void SetupDropMenu()
3610 {
3611     int i, j, count;
3612     char label[32];
3613     Arg args[16];
3614     Widget entry;
3615     char* p;
3616
3617     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3618         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3619         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3620                    dmEnables[i].piece);
3621         XtSetSensitive(entry, p != NULL || !appData.testLegality
3622                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3623                                        && !appData.icsActive));
3624         count = 0;
3625         while (p && *p++ == dmEnables[i].piece) count++;
3626         sprintf(label, "%s  %d", dmEnables[i].widget, count);
3627         j = 0;
3628         XtSetArg(args[j], XtNlabel, label); j++;
3629         XtSetValues(entry, args, j);
3630     }
3631 }
3632
3633 void PieceMenuPopup(w, event, params, num_params)
3634      Widget w;
3635      XEvent *event;
3636      String *params;
3637      Cardinal *num_params;
3638 {
3639     String whichMenu;
3640     if (event->type != ButtonPress) return;
3641     if (errorUp) ErrorPopDown();
3642     switch (gameMode) {
3643       case EditPosition:
3644       case IcsExamining:
3645         whichMenu = params[0];
3646         break;
3647       case IcsPlayingWhite:
3648       case IcsPlayingBlack:
3649       case EditGame:
3650       case MachinePlaysWhite:
3651       case MachinePlaysBlack:
3652         if (appData.testLegality &&
3653             gameInfo.variant != VariantBughouse &&
3654             gameInfo.variant != VariantCrazyhouse) return;
3655         SetupDropMenu();
3656         whichMenu = "menuD";
3657         break;
3658       default:
3659         return;
3660     }
3661     
3662     if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_SIZE)) < 0) ||
3663         ((pmFromY = EventToSquare(event->xbutton.y, BOARD_SIZE)) < 0)) {
3664         pmFromX = pmFromY = -1;
3665         return;
3666     }
3667     if (flipView)
3668       pmFromX = BOARD_SIZE - 1 - pmFromX;
3669     else
3670       pmFromY = BOARD_SIZE - 1 - pmFromY;
3671     
3672     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3673 }
3674
3675 static void PieceMenuSelect(w, piece, junk)
3676      Widget w;
3677      ChessSquare piece;
3678      caddr_t junk;
3679 {
3680     if (pmFromX < 0 || pmFromY < 0) return;
3681     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3682 }
3683
3684 static void DropMenuSelect(w, piece, junk)
3685      Widget w;
3686      ChessSquare piece;
3687      caddr_t junk;
3688 {
3689     if (pmFromX < 0 || pmFromY < 0) return;
3690     DropMenuEvent(piece, pmFromX, pmFromY);
3691 }
3692
3693 void WhiteClock(w, event, prms, nprms)
3694      Widget w;
3695      XEvent *event;
3696      String *prms;
3697      Cardinal *nprms;
3698 {
3699     if (gameMode == EditPosition || gameMode == IcsExamining) {
3700         SetWhiteToPlayEvent();
3701     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3702         CallFlagEvent();
3703     }
3704 }
3705
3706 void BlackClock(w, event, prms, nprms)
3707      Widget w;
3708      XEvent *event;
3709      String *prms;
3710      Cardinal *nprms;
3711 {
3712     if (gameMode == EditPosition || gameMode == IcsExamining) {
3713         SetBlackToPlayEvent();
3714     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3715         CallFlagEvent();
3716     }
3717 }
3718
3719
3720 /*
3721  * If the user selects on a border boundary, return -1; if off the board,
3722  *   return -2.  Otherwise map the event coordinate to the square.
3723  */
3724 int EventToSquare(x, limit)
3725      int x;
3726 {
3727     if (x <= 0) 
3728       return -2;
3729     if (x < lineGap)
3730       return -1;
3731     x -= lineGap;
3732     if ((x % (squareSize + lineGap)) >= squareSize)
3733       return -1;
3734     x /= (squareSize + lineGap);
3735     if (x >= limit)
3736       return -2;
3737     return x;
3738 }
3739
3740 static void do_flash_delay(msec)
3741      unsigned long msec;
3742 {
3743     TimeDelay(msec);
3744 }
3745
3746 static void drawHighlight(file, rank, gc)
3747      int file, rank;
3748      GC gc;
3749 {
3750     int x, y;
3751
3752     if (lineGap == 0 || appData.blindfold) return;
3753     
3754     if (flipView) {
3755         x = lineGap/2 + ((BOARD_SIZE-1)-file) * 
3756           (squareSize + lineGap);
3757         y = lineGap/2 + rank * (squareSize + lineGap);
3758     } else {
3759         x = lineGap/2 + file * (squareSize + lineGap);
3760         y = lineGap/2 + ((BOARD_SIZE-1)-rank) * 
3761           (squareSize + lineGap);
3762     }
3763     
3764     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
3765                    squareSize+lineGap, squareSize+lineGap);
3766 }
3767
3768 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
3769 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
3770
3771 void
3772 SetHighlights(fromX, fromY, toX, toY)
3773      int fromX, fromY, toX, toY;
3774 {
3775     if (hi1X != fromX || hi1Y != fromY) {
3776         if (hi1X >= 0 && hi1Y >= 0) {
3777             drawHighlight(hi1X, hi1Y, lineGC);
3778         }
3779         if (fromX >= 0 && fromY >= 0) {
3780             drawHighlight(fromX, fromY, highlineGC);
3781         }
3782     }
3783     if (hi2X != toX || hi2Y != toY) {
3784         if (hi2X >= 0 && hi2Y >= 0) {
3785             drawHighlight(hi2X, hi2Y, lineGC);
3786         }
3787         if (toX >= 0 && toY >= 0) {
3788             drawHighlight(toX, toY, highlineGC);
3789         }
3790     }
3791     hi1X = fromX;
3792     hi1Y = fromY;
3793     hi2X = toX;
3794     hi2Y = toY;
3795 }
3796
3797 void
3798 ClearHighlights()
3799 {
3800     SetHighlights(-1, -1, -1, -1);
3801 }
3802
3803
3804 void
3805 SetPremoveHighlights(fromX, fromY, toX, toY)
3806      int fromX, fromY, toX, toY;
3807 {
3808     if (pm1X != fromX || pm1Y != fromY) {
3809         if (pm1X >= 0 && pm1Y >= 0) {
3810             drawHighlight(pm1X, pm1Y, lineGC);
3811         }
3812         if (fromX >= 0 && fromY >= 0) {
3813             drawHighlight(fromX, fromY, prelineGC);
3814         }
3815     }
3816     if (pm2X != toX || pm2Y != toY) {
3817         if (pm2X >= 0 && pm2Y >= 0) {
3818             drawHighlight(pm2X, pm2Y, lineGC);
3819         }
3820         if (toX >= 0 && toY >= 0) {
3821             drawHighlight(toX, toY, prelineGC);
3822         }
3823     }
3824     pm1X = fromX;
3825     pm1Y = fromY;
3826     pm2X = toX;
3827     pm2Y = toY;
3828 }
3829
3830 void
3831 ClearPremoveHighlights()
3832 {
3833   SetPremoveHighlights(-1, -1, -1, -1);
3834 }
3835
3836 static void BlankSquare(x, y, color, piece, dest)
3837      int x, y, color;
3838      ChessSquare piece;
3839      Drawable dest;
3840 {
3841     if (useImages && useImageSqs) {
3842         Pixmap pm;
3843         switch (color) {
3844           case 1: /* light */
3845             pm = xpmLightSquare;
3846             break;
3847           case 0: /* dark */
3848             pm = xpmDarkSquare;
3849             break;
3850           case 2: /* neutral */
3851           default:
3852             pm = xpmJailSquare;
3853             break;
3854         }
3855         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
3856                   squareSize, squareSize, x, y);
3857     } else {
3858         GC gc;
3859         switch (color) {
3860           case 1: /* light */
3861             gc = lightSquareGC;
3862             break;
3863           case 0: /* dark */
3864             gc = darkSquareGC;
3865             break;
3866           case 2: /* neutral */
3867           default:
3868             gc = jailSquareGC;
3869             break;
3870         }
3871         XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
3872     }
3873 }
3874
3875 /*
3876    I split out the routines to draw a piece so that I could
3877    make a generic flash routine.
3878 */
3879 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
3880      ChessSquare piece;
3881      int square_color, x, y;
3882      Drawable dest;
3883 {
3884     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3885     switch (square_color) {
3886       case 1: /* light */
3887       case 2: /* neutral */
3888       default:
3889         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3890                   ? *pieceToOutline(piece)
3891                   : *pieceToSolid(piece),
3892                   dest, bwPieceGC, 0, 0,
3893                   squareSize, squareSize, x, y);
3894         break;
3895       case 0: /* dark */
3896         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
3897                   ? *pieceToSolid(piece)
3898                   : *pieceToOutline(piece),
3899                   dest, wbPieceGC, 0, 0,
3900                   squareSize, squareSize, x, y);
3901         break;
3902     }
3903 }
3904
3905 static void monoDrawPiece(piece, square_color, x, y, dest)
3906      ChessSquare piece;
3907      int square_color, x, y;
3908      Drawable dest;
3909 {
3910     switch (square_color) {
3911       case 1: /* light */
3912       case 2: /* neutral */
3913       default:
3914         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3915                    ? *pieceToOutline(piece)
3916                    : *pieceToSolid(piece),
3917                    dest, bwPieceGC, 0, 0,
3918                    squareSize, squareSize, x, y, 1);
3919         break;
3920       case 0: /* dark */
3921         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
3922                    ? *pieceToSolid(piece)
3923                    : *pieceToOutline(piece),
3924                    dest, wbPieceGC, 0, 0,
3925                    squareSize, squareSize, x, y, 1);
3926         break;
3927     }
3928 }
3929
3930 static void colorDrawPiece(piece, square_color, x, y, dest)
3931      ChessSquare piece;
3932      int square_color, x, y;
3933      Drawable dest;
3934 {
3935     switch (square_color) {
3936       case 1: /* light */
3937         XCopyPlane(xDisplay, *pieceToSolid(piece),
3938                    dest, (int) piece < (int) BlackPawn
3939                    ? wlPieceGC : blPieceGC, 0, 0,
3940                    squareSize, squareSize, x, y, 1);
3941         break;
3942       case 0: /* dark */
3943         XCopyPlane(xDisplay, *pieceToSolid(piece),
3944                    dest, (int) piece < (int) BlackPawn
3945                    ? wdPieceGC : bdPieceGC, 0, 0,
3946                    squareSize, squareSize, x, y, 1);
3947         break;
3948       case 2: /* neutral */
3949       default:
3950         XCopyPlane(xDisplay, *pieceToSolid(piece),
3951                    dest, (int) piece < (int) BlackPawn
3952                    ? wjPieceGC : bjPieceGC, 0, 0,
3953                    squareSize, squareSize, x, y, 1);
3954         break;
3955     }
3956 }
3957
3958 static void colorDrawPieceImage(piece, square_color, x, y, dest)
3959      ChessSquare piece;
3960      int square_color, x, y;
3961      Drawable dest;
3962 {
3963     int kind;
3964
3965     switch (square_color) {
3966       case 1: /* light */
3967       case 2: /* neutral */
3968       default:
3969         if ((int)piece < (int) BlackPawn) {
3970             kind = 0;
3971         } else {
3972             kind = 2;
3973             piece -= BlackPawn;
3974         }
3975         break;
3976       case 0: /* dark */
3977         if ((int)piece < (int) BlackPawn) {
3978             kind = 1;
3979         } else {
3980             kind = 3;
3981             piece -= BlackPawn;
3982         }
3983         break;
3984     }
3985     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
3986               dest, wlPieceGC, 0, 0,
3987               squareSize, squareSize, x, y);            
3988 }
3989
3990 typedef void (*DrawFunc)();
3991
3992 DrawFunc ChooseDrawFunc()
3993 {
3994     if (appData.monoMode) {
3995         if (DefaultDepth(xDisplay, xScreen) == 1) {
3996             return monoDrawPiece_1bit;
3997         } else {
3998             return monoDrawPiece;
3999         }
4000     } else {
4001         if (useImages)
4002           return colorDrawPieceImage;
4003         else
4004           return colorDrawPiece;
4005     }
4006 }
4007
4008 void DrawSquare(row, column, piece, do_flash)
4009      int row, column, do_flash;
4010      ChessSquare piece;
4011 {
4012     int square_color, x, y, direction, font_ascent, font_descent;
4013     int i;
4014     char string[2];
4015     XCharStruct overall;
4016     DrawFunc drawfunc;
4017     int flash_delay;
4018
4019     /* Calculate delay in milliseconds (2-delays per complete flash) */
4020     flash_delay = 500 / appData.flashRate;
4021         
4022     if (flipView) {
4023         x = lineGap + ((BOARD_SIZE-1)-column) * 
4024           (squareSize + lineGap);
4025         y = lineGap + row * (squareSize + lineGap);
4026     } else {
4027         x = lineGap + column * (squareSize + lineGap);
4028         y = lineGap + ((BOARD_SIZE-1)-row) * 
4029           (squareSize + lineGap);
4030     }
4031     
4032     square_color = ((column + row) % 2) == 1;
4033     
4034     if (piece == EmptySquare || appData.blindfold) {
4035         BlankSquare(x, y, square_color, piece, xBoardWindow);
4036     } else {
4037         drawfunc = ChooseDrawFunc();
4038         if (do_flash && appData.flashCount > 0) {
4039             for (i=0; i<appData.flashCount; ++i) {
4040
4041                 drawfunc(piece, square_color, x, y, xBoardWindow);
4042                 XSync(xDisplay, False);
4043                 do_flash_delay(flash_delay);
4044
4045                 BlankSquare(x, y, square_color, piece, xBoardWindow);
4046                 XSync(xDisplay, False);
4047                 do_flash_delay(flash_delay);
4048             }
4049         }
4050         drawfunc(piece, square_color, x, y, xBoardWindow);
4051     }
4052         
4053     string[1] = NULLCHAR;
4054     if (appData.showCoords && row == (flipView ? 7 : 0)) {
4055         string[0] = 'a' + column;
4056         XTextExtents(coordFontStruct, string, 1, &direction, 
4057                      &font_ascent, &font_descent, &overall);
4058         if (appData.monoMode) {
4059             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4060                              x + squareSize - overall.width - 2, 
4061                              y + squareSize - font_descent - 1, string, 1);
4062         } else {
4063             XDrawString(xDisplay, xBoardWindow, coordGC,
4064                         x + squareSize - overall.width - 2, 
4065                         y + squareSize - font_descent - 1, string, 1);
4066         }
4067     }
4068     if (appData.showCoords && column == (flipView ? 7 : 0)) {
4069         string[0] = '1' + row;
4070         XTextExtents(coordFontStruct, string, 1, &direction, 
4071                      &font_ascent, &font_descent, &overall);
4072         if (appData.monoMode) {
4073             XDrawImageString(xDisplay, xBoardWindow, coordGC,
4074                              x + 2, y + font_ascent + 1, string, 1);
4075         } else {
4076             XDrawString(xDisplay, xBoardWindow, coordGC,
4077                         x + 2, y + font_ascent + 1, string, 1);
4078         }           
4079     }   
4080 }
4081
4082
4083 /* Why is this needed on some versions of X? */
4084 void EventProc(widget, unused, event)
4085      Widget widget;
4086      caddr_t unused;
4087      XEvent *event;
4088 {
4089     if (!XtIsRealized(widget))
4090       return;
4091
4092     switch (event->type) {
4093       case Expose:
4094         if (event->xexpose.count > 0) return;  /* no clipping is done */
4095         XDrawPosition(widget, True, NULL);
4096         break;
4097       default:
4098         return;
4099     }
4100 }
4101 /* end why */
4102
4103 void DrawPosition(fullRedraw, board)
4104      /*Boolean*/int fullRedraw;
4105      Board board;
4106 {
4107     XDrawPosition(boardWidget, fullRedraw, board);
4108 }
4109
4110 /* Returns 1 if there are "too many" differences between b1 and b2
4111    (i.e. more than 1 move was made) */
4112 static int too_many_diffs(b1, b2)
4113      Board b1, b2;
4114 {
4115     int i, j;
4116     int c = 0;
4117   
4118     for (i=0; i<BOARD_SIZE; ++i) {
4119         for (j=0; j<BOARD_SIZE; ++j) {
4120             if (b1[i][j] != b2[i][j]) {
4121                 if (++c > 4)    /* Castling causes 4 diffs */
4122                   return 1;
4123             }
4124         }
4125     }
4126
4127     return 0;
4128 }
4129
4130 /* Matrix describing castling maneuvers */
4131 /* Row, ColRookFrom, ColKingFrom, ColRookTo, ColKingTo */
4132 static int castling_matrix[4][5] = {
4133     { 0, 0, 4, 3, 2 },          /* 0-0-0, white */
4134     { 0, 7, 4, 5, 6 },          /* 0-0,   white */
4135     { 7, 0, 4, 3, 2 },          /* 0-0-0, black */
4136     { 7, 7, 4, 5, 6 }           /* 0-0,   black */
4137 };
4138
4139 /* Checks whether castling occurred. If it did, *rrow and *rcol
4140    are set to the destination (row,col) of the rook that moved.
4141    
4142    Returns 1 if castling occurred, 0 if not.
4143    
4144    Note: Only handles a max of 1 castling move, so be sure
4145    to call too_many_diffs() first.
4146    */
4147 static int check_castle_draw(newb, oldb, rrow, rcol)
4148      Board newb, oldb;
4149      int *rrow, *rcol;
4150 {
4151     int i, *r, j;
4152     int match;
4153
4154     /* For each type of castling... */
4155     for (i=0; i<4; ++i) {
4156         r = castling_matrix[i];
4157
4158         /* Check the 4 squares involved in the castling move */
4159         match = 0;
4160         for (j=1; j<=4; ++j) {
4161             if (newb[r[0]][r[j]] == oldb[r[0]][r[j]]) {
4162                 match = 1;
4163                 break;
4164             }
4165         }
4166
4167         if (!match) {
4168             /* All 4 changed, so it must be a castling move */
4169             *rrow = r[0];
4170             *rcol = r[3];
4171             return 1;
4172         }
4173     }
4174     return 0;
4175 }
4176
4177 static int damage[BOARD_SIZE][BOARD_SIZE];
4178
4179 /*
4180  * event handler for redrawing the board
4181  */
4182 void XDrawPosition(w, repaint, board)
4183      Widget w;
4184      /*Boolean*/int repaint;
4185      Board board;
4186 {
4187     int i, j, do_flash;
4188     static int lastFlipView = 0;
4189     static int lastBoardValid = 0;
4190     static Board lastBoard;
4191     Arg args[16];
4192     int rrow, rcol;
4193     
4194     if (board == NULL) {
4195         if (!lastBoardValid) return;
4196         board = lastBoard;
4197     }
4198     if (!lastBoardValid || lastFlipView != flipView) {
4199         XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
4200         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
4201                     args, 1);
4202     }
4203
4204     /*
4205      * It would be simpler to clear the window with XClearWindow()
4206      * but this causes a very distracting flicker.
4207      */
4208     
4209     if (!repaint && lastBoardValid && lastFlipView == flipView) {
4210
4211         /* If too much changes (begin observing new game, etc.), don't
4212            do flashing */
4213         do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
4214
4215         /* Special check for castling so we don't flash both the king
4216            and the rook (just flash the king). */
4217         if (do_flash) {
4218             if (check_castle_draw(board, lastBoard, &rrow, &rcol)) {
4219                 /* Draw rook with NO flashing. King will be drawn flashing later */
4220                 DrawSquare(rrow, rcol, board[rrow][rcol], 0);
4221                 lastBoard[rrow][rcol] = board[rrow][rcol];
4222             }
4223         }
4224
4225         /* First pass -- Draw (newly) empty squares and repair damage. 
4226            This prevents you from having a piece show up twice while it 
4227            is flashing on its new square */
4228         for (i = 0; i < BOARD_SIZE; i++)
4229           for (j = 0; j < BOARD_SIZE; j++)
4230             if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
4231                 || damage[i][j]) {
4232                 DrawSquare(i, j, board[i][j], 0);
4233                 damage[i][j] = False;
4234             }
4235
4236         /* Second pass -- Draw piece(s) in new position and flash them */
4237         for (i = 0; i < BOARD_SIZE; i++)
4238           for (j = 0; j < BOARD_SIZE; j++)
4239             if (board[i][j] != lastBoard[i][j]) {
4240                 DrawSquare(i, j, board[i][j], do_flash);          
4241             }
4242     } else {
4243         if (lineGap > 0)
4244           XDrawSegments(xDisplay, xBoardWindow, lineGC,
4245                         gridSegments, (BOARD_SIZE + 1) * 2);
4246         
4247         for (i = 0; i < BOARD_SIZE; i++)
4248           for (j = 0; j < BOARD_SIZE; j++) {
4249               DrawSquare(i, j, board[i][j], 0);
4250               damage[i][j] = False;
4251           }
4252     }
4253
4254     CopyBoard(lastBoard, board);
4255     lastBoardValid = 1;
4256     lastFlipView = flipView;
4257
4258     /* Draw highlights */
4259     if (pm1X >= 0 && pm1Y >= 0) {
4260       drawHighlight(pm1X, pm1Y, prelineGC);
4261     }
4262     if (pm2X >= 0 && pm2Y >= 0) {
4263       drawHighlight(pm2X, pm2Y, prelineGC);
4264     }
4265     if (hi1X >= 0 && hi1Y >= 0) {
4266       drawHighlight(hi1X, hi1Y, highlineGC);
4267     }
4268     if (hi2X >= 0 && hi2Y >= 0) {
4269       drawHighlight(hi2X, hi2Y, highlineGC);
4270     }
4271
4272     /* If piece being dragged around board, must redraw that too */
4273     DrawDragPiece();
4274
4275     XSync(xDisplay, False);
4276 }
4277
4278
4279 /*
4280  * event handler for redrawing the board
4281  */
4282 void DrawPositionProc(w, event, prms, nprms)
4283      Widget w;
4284      XEvent *event;
4285      String *prms;
4286      Cardinal *nprms;
4287 {
4288     XDrawPosition(w, True, NULL);
4289 }
4290
4291
4292 /*
4293  * event handler for parsing user moves
4294  */
4295 void HandleUserMove(w, event, prms, nprms)
4296      Widget w;
4297      XEvent *event;
4298      String *prms;
4299      Cardinal *nprms;
4300 {
4301     int x, y;
4302     Boolean saveAnimate;
4303     static int second = 0;
4304         
4305     if (w != boardWidget || errorExitStatus != -1) return;
4306
4307     if (event->type == ButtonPress) ErrorPopDown();
4308
4309     if (promotionUp) {
4310         if (event->type == ButtonPress) {
4311             XtPopdown(promotionShell);
4312             XtDestroyWidget(promotionShell);
4313             promotionUp = False;
4314             ClearHighlights();
4315             fromX = fromY = -1;
4316         } else {
4317             return;
4318         }
4319     }
4320     
4321     x = EventToSquare(event->xbutton.x, BOARD_SIZE);
4322     y = EventToSquare(event->xbutton.y, BOARD_SIZE);
4323     if (!flipView && y >= 0) {
4324         y = BOARD_SIZE - 1 - y;
4325     }
4326     if (flipView && x >= 0) {
4327         x = BOARD_SIZE - 1 - x;
4328     }
4329
4330     if (fromX == -1) {
4331         if (event->type == ButtonPress) {
4332             /* First square */ 
4333             if (OKToStartUserMove(x, y)) {
4334                 fromX = x;
4335                 fromY = y;
4336                 second = 0;
4337                 DragPieceBegin(event->xbutton.x, event->xbutton.y);
4338                 if (appData.highlightDragging) {
4339                     SetHighlights(x, y, -1, -1);
4340                 }
4341             } 
4342         }
4343         return;
4344     }
4345
4346     /* fromX != -1 */
4347     if (event->type == ButtonPress && gameMode != EditPosition &&
4348         x >= 0 && y >= 0) {
4349         ChessSquare fromP;
4350         ChessSquare toP;
4351         /* Check if clicking again on the same color piece */
4352         fromP = boards[currentMove][fromY][fromX];
4353         toP = boards[currentMove][y][x];
4354         if ((WhitePawn <= fromP && fromP <= WhiteKing &&
4355              WhitePawn <= toP && toP <= WhiteKing) ||
4356             (BlackPawn <= fromP && fromP <= BlackKing &&
4357              BlackPawn <= toP && toP <= BlackKing)) {
4358             /* Clicked again on same color piece -- changed his mind */
4359             second = (x == fromX && y == fromY);
4360             if (appData.highlightDragging) {
4361                 SetHighlights(x, y, -1, -1);
4362             } else {
4363                 ClearHighlights();
4364             }
4365             if (OKToStartUserMove(x, y)) {
4366                 fromX = x;
4367                 fromY = y;
4368                 DragPieceBegin(event->xbutton.x, event->xbutton.y);
4369             }
4370             return;
4371         }
4372     }
4373
4374     if (event->type == ButtonRelease && x == fromX && y == fromY) {
4375         DragPieceEnd(event->xbutton.x, event->xbutton.y);
4376         if (appData.animateDragging) {
4377             /* Undo animation damage if any */
4378             DrawPosition(FALSE, NULL);
4379         }
4380         if (second) {
4381             /* Second up/down in same square; just abort move */
4382             second = 0;
4383             fromX = fromY = -1;
4384             ClearHighlights();
4385             gotPremove = 0;
4386             ClearPremoveHighlights();
4387         } else {
4388             /* First upclick in same square; start click-click mode */
4389             SetHighlights(x, y, -1, -1);
4390         }
4391         return;
4392     }   
4393
4394     /* Completed move */
4395     toX = x;
4396     toY = y;
4397     saveAnimate = appData.animate;
4398     if (event->type == ButtonPress) {
4399         /* Finish clickclick move */
4400         if (appData.animate || appData.highlightLastMove) {
4401             SetHighlights(fromX, fromY, toX, toY);
4402         } else {
4403             ClearHighlights();
4404         }
4405     } else {
4406         /* Finish drag move */
4407         if (appData.highlightLastMove) {
4408             SetHighlights(fromX, fromY, toX, toY);
4409         } else {
4410             ClearHighlights();
4411         }
4412         DragPieceEnd(event->xbutton.x, event->xbutton.y);
4413         /* Don't animate move and drag both */
4414         appData.animate = FALSE;
4415     }
4416     if (IsPromotion(fromX, fromY, toX, toY)) {
4417         if (appData.alwaysPromoteToQueen) {
4418             UserMoveEvent(fromX, fromY, toX, toY, 'q');
4419             if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4420             if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4421             fromX = fromY = -1;
4422         } else {
4423             SetHighlights(fromX, fromY, toX, toY);
4424             PromotionPopUp();
4425         }
4426     } else {
4427         UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
4428         if (!appData.highlightLastMove || gotPremove) ClearHighlights();
4429         if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
4430         fromX = fromY = -1;
4431     }
4432     appData.animate = saveAnimate;
4433     if (appData.animate || appData.animateDragging) {
4434         /* Undo animation damage if needed */
4435         DrawPosition(FALSE, NULL);
4436     }
4437 }
4438
4439 void AnimateUserMove (Widget w, XEvent * event,
4440                       String * params, Cardinal * nParams)
4441 {
4442     DragPieceMove(event->xmotion.x, event->xmotion.y);
4443 }
4444
4445 Widget CommentCreate(name, text, mutable, callback, lines)
4446      char *name, *text;
4447      int /*Boolean*/ mutable;
4448      XtCallbackProc callback;
4449      int lines;
4450 {
4451     Arg args[16];
4452     Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
4453     Dimension bw_width;
4454     int j;
4455
4456     j = 0;
4457     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4458     XtGetValues(boardWidget, args, j);
4459
4460     j = 0;
4461     XtSetArg(args[j], XtNresizable, True);  j++;
4462 #if TOPLEVEL
4463     shell =
4464       XtCreatePopupShell(name, topLevelShellWidgetClass,
4465                          shellWidget, args, j);
4466 #else
4467     shell =
4468       XtCreatePopupShell(name, transientShellWidgetClass,
4469                          shellWidget, args, j);
4470 #endif
4471     layout =
4472       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4473                             layoutArgs, XtNumber(layoutArgs));
4474     form =
4475       XtCreateManagedWidget("form", formWidgetClass, layout,
4476                             formArgs, XtNumber(formArgs));
4477
4478     j = 0;
4479     if (mutable) {
4480         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4481         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4482     }
4483     XtSetArg(args[j], XtNstring, text);  j++;
4484     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4485     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4486     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4487     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4488     XtSetArg(args[j], XtNresizable, True);  j++;
4489     XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
4490 #if 0
4491     XtSetArg(args[j], XtNscrollVertical, XawtextScrollWhenNeeded);  j++;
4492 #else
4493     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4494     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4495 #endif
4496     XtSetArg(args[j], XtNautoFill, True);  j++;
4497     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4498     edit =
4499       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4500
4501     if (mutable) {
4502         j = 0;
4503         XtSetArg(args[j], XtNfromVert, edit);  j++;
4504         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4505         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4506         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4507         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4508         b_ok =
4509           XtCreateManagedWidget("ok", commandWidgetClass, form, args, j);
4510         XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
4511
4512         j = 0;
4513         XtSetArg(args[j], XtNfromVert, edit);  j++;
4514         XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
4515         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4516         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4517         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4518         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4519         b_cancel =
4520           XtCreateManagedWidget("cancel", commandWidgetClass, form, args, j);
4521         XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
4522
4523         j = 0;
4524         XtSetArg(args[j], XtNfromVert, edit);  j++;
4525         XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
4526         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4527         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4528         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4529         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4530         b_clear =
4531           XtCreateManagedWidget("clear", commandWidgetClass, form, args, j);
4532         XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
4533     } else {
4534         j = 0;
4535         XtSetArg(args[j], XtNfromVert, edit);  j++;
4536         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4537         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4538         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4539         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4540         b_close =
4541           XtCreateManagedWidget("close", commandWidgetClass, form, args, j);
4542         XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
4543
4544         j = 0;
4545         XtSetArg(args[j], XtNfromVert, edit);  j++;
4546         XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
4547         XtSetArg(args[j], XtNtop, XtChainBottom); j++;
4548         XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
4549         XtSetArg(args[j], XtNleft, XtChainLeft); j++;
4550         XtSetArg(args[j], XtNright, XtChainLeft); j++;
4551         b_edit =
4552           XtCreateManagedWidget("edit", commandWidgetClass, form, args, j);
4553         XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
4554     }
4555
4556     XtRealizeWidget(shell);
4557
4558     if (commentX == -1) {
4559         int xx, yy;
4560         Window junk;
4561         Dimension pw_height;
4562         Dimension ew_height;
4563
4564         j = 0;
4565         XtSetArg(args[j], XtNheight, &ew_height);  j++;
4566         XtGetValues(edit, args, j);
4567
4568         j = 0;
4569         XtSetArg(args[j], XtNheight, &pw_height);  j++;
4570         XtGetValues(shell, args, j);
4571         commentH = pw_height + (lines - 1) * ew_height;
4572         commentW = bw_width - 16;
4573
4574         XSync(xDisplay, False);
4575 #ifdef NOTDEF
4576         /* This code seems to tickle an X bug if it is executed too soon
4577            after xboard starts up.  The coordinates get transformed as if
4578            the main window was positioned at (0, 0).
4579            */
4580         XtTranslateCoords(shellWidget,
4581                           (bw_width - commentW) / 2, 0 - commentH / 2,
4582                           &commentX, &commentY);
4583 #else  /*!NOTDEF*/
4584         XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4585                               RootWindowOfScreen(XtScreen(shellWidget)),
4586                               (bw_width - commentW) / 2, 0 - commentH / 2,
4587                               &xx, &yy, &junk);
4588         commentX = xx;
4589         commentY = yy;
4590 #endif /*!NOTDEF*/
4591         if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
4592     }
4593     j = 0;
4594     XtSetArg(args[j], XtNheight, commentH);  j++;
4595     XtSetArg(args[j], XtNwidth, commentW);  j++;
4596     XtSetArg(args[j], XtNx, commentX);  j++;
4597     XtSetArg(args[j], XtNy, commentY);  j++;
4598     XtSetValues(shell, args, j);
4599     XtSetKeyboardFocus(shell, edit);
4600
4601     return shell;
4602 }
4603
4604 /* Used for analysis window and ICS input window */
4605 Widget MiscCreate(name, text, mutable, callback, lines)
4606      char *name, *text;
4607      int /*Boolean*/ mutable;
4608      XtCallbackProc callback;
4609      int lines;
4610 {
4611     Arg args[16];
4612     Widget shell, layout, form, edit;
4613     Position x, y;
4614     Dimension bw_width, pw_height, ew_height, w, h;
4615     int j;
4616     int xx, yy;
4617     Window junk;
4618
4619     j = 0;
4620     XtSetArg(args[j], XtNresizable, True);  j++;
4621 #if TOPLEVEL
4622     shell =
4623       XtCreatePopupShell(name, topLevelShellWidgetClass,
4624                          shellWidget, args, j);
4625 #else
4626     shell =
4627       XtCreatePopupShell(name, transientShellWidgetClass,
4628                          shellWidget, args, j);
4629 #endif
4630     layout =
4631       XtCreateManagedWidget(layoutName, formWidgetClass, shell,
4632                             layoutArgs, XtNumber(layoutArgs));
4633     form =
4634       XtCreateManagedWidget("form", formWidgetClass, layout,
4635                             formArgs, XtNumber(formArgs));
4636
4637     j = 0;
4638     if (mutable) {
4639         XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
4640         XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
4641     }
4642     XtSetArg(args[j], XtNstring, text);  j++;
4643     XtSetArg(args[j], XtNtop, XtChainTop);  j++;
4644     XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
4645     XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
4646     XtSetArg(args[j], XtNright, XtChainRight);  j++;
4647     XtSetArg(args[j], XtNresizable, True);  j++;
4648 #if 0
4649     XtSetArg(args[j], XtNscrollVertical, XawtextScrollWhenNeeded);  j++;
4650 #else
4651     /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
4652     XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
4653 #endif
4654     XtSetArg(args[j], XtNautoFill, True);  j++;
4655     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
4656     edit =
4657       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
4658
4659     XtRealizeWidget(shell);
4660
4661     j = 0;
4662     XtSetArg(args[j], XtNwidth, &bw_width);  j++;
4663     XtGetValues(boardWidget, args, j);
4664
4665     j = 0;
4666     XtSetArg(args[j], XtNheight, &ew_height);  j++;
4667     XtGetValues(edit, args, j);
4668
4669     j = 0;
4670     XtSetArg(args[j], XtNheight, &pw_height);  j++;
4671     XtGetValues(shell, args, j);
4672     h = pw_height + (lines - 1) * ew_height;
4673     w = bw_width - 16;
4674
4675     XSync(xDisplay, False);
4676 #ifdef NOTDEF
4677     /* This code seems to tickle an X bug if it is executed too soon
4678        after xboard starts up.  The coordinates get transformed as if
4679        the main window was positioned at (0, 0).
4680     */
4681     XtTranslateCoords(shellWidget, (bw_width - w) / 2, 0 - h / 2, &x, &y);
4682 #else  /*!NOTDEF*/
4683     XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
4684                           RootWindowOfScreen(XtScreen(shellWidget)),
4685                           (bw_width - w) / 2, 0 - h / 2, &xx, &yy, &junk);
4686 #endif /*!NOTDEF*/
4687     x = xx;
4688     y = yy;
4689     if (y < 0) y = 0; /*avoid positioning top offscreen*/
4690
4691     j = 0;
4692     XtSetArg(args[j], XtNheight, h);  j++;
4693     XtSetArg(args[j], XtNwidth, w);  j++;
4694     XtSetArg(args[j], XtNx, x);  j++;
4695     XtSetArg(args[j], XtNy, y);  j++;
4696     XtSetValues(shell, args, j);
4697
4698     return shell;
4699 }
4700
4701
4702 static int savedIndex;  /* gross that this is global */
4703
4704 void EditCommentPopUp(index, title, text)
4705      int index;
4706      char *title, *text;
4707 {
4708     Widget edit;
4709     Arg args[16];
4710     int j;
4711
4712     savedIndex = index;
4713     if (text == NULL) text = "";
4714
4715     if (editShell == NULL) {
4716         editShell =
4717           CommentCreate(title, text, True, EditCommentCallback, 4); 
4718         XtRealizeWidget(editShell);
4719         CatchDeleteWindow(editShell, "EditCommentPopDown");
4720     } else {
4721         edit = XtNameToWidget(editShell, "*form.text");
4722         j = 0;
4723         XtSetArg(args[j], XtNstring, text); j++;
4724         XtSetValues(edit, args, j);
4725         j = 0;
4726         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4727         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4728         XtSetValues(editShell, args, j);
4729     }
4730
4731     XtPopup(editShell, XtGrabNone);
4732
4733     editUp = True;
4734     j = 0;
4735     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4736     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4737                 args, j);
4738 }
4739
4740 void EditCommentCallback(w, client_data, call_data)
4741      Widget w;
4742      XtPointer client_data, call_data;
4743 {
4744     String name, val;
4745     Arg args[16];
4746     int j;
4747     Widget edit;
4748
4749     j = 0;
4750     XtSetArg(args[j], XtNlabel, &name);  j++;
4751     XtGetValues(w, args, j);
4752
4753     if (strcmp(name, "ok") == 0) {
4754         edit = XtNameToWidget(editShell, "*form.text");
4755         j = 0;
4756         XtSetArg(args[j], XtNstring, &val); j++;
4757         XtGetValues(edit, args, j);
4758         ReplaceComment(savedIndex, val);
4759         EditCommentPopDown();
4760     } else if (strcmp(name, "cancel") == 0) {
4761         EditCommentPopDown();
4762     } else if (strcmp(name, "clear") == 0) {
4763         edit = XtNameToWidget(editShell, "*form.text");
4764         XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4765         XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4766     }
4767 }
4768
4769 void EditCommentPopDown()
4770 {
4771     Arg args[16];
4772     int j;
4773
4774     if (!editUp) return;
4775     j = 0;
4776     XtSetArg(args[j], XtNx, &commentX); j++;
4777     XtSetArg(args[j], XtNy, &commentY); j++;
4778     XtSetArg(args[j], XtNheight, &commentH); j++;
4779     XtSetArg(args[j], XtNwidth, &commentW); j++;
4780     XtGetValues(editShell, args, j);
4781     XtPopdown(editShell);
4782     editUp = False;
4783     j = 0;
4784     XtSetArg(args[j], XtNleftBitmap, None); j++;
4785     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
4786                 args, j);
4787 }
4788
4789 void ICSInputBoxPopUp()
4790 {
4791     Widget edit;
4792     Arg args[16];
4793     int j;
4794     char *title = "ICS Input";
4795     XtTranslations tr;
4796         
4797     if (ICSInputShell == NULL) {
4798         ICSInputShell = MiscCreate(title, "", True, NULL, 1);
4799         tr = XtParseTranslationTable(ICSInputTranslations);
4800         edit = XtNameToWidget(ICSInputShell, "*form.text");
4801         XtOverrideTranslations(edit, tr);
4802         XtRealizeWidget(ICSInputShell);
4803         CatchDeleteWindow(ICSInputShell, "ICSInputBoxPopDown");
4804         
4805     } else {
4806         edit = XtNameToWidget(ICSInputShell, "*form.text");
4807         j = 0;
4808         XtSetArg(args[j], XtNstring, ""); j++;
4809         XtSetValues(edit, args, j);
4810         j = 0;
4811         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4812         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4813         XtSetValues(ICSInputShell, args, j);
4814     }
4815
4816     XtPopup(ICSInputShell, XtGrabNone);
4817     XtSetKeyboardFocus(ICSInputShell, edit);
4818
4819     ICSInputBoxUp = True;
4820     j = 0;
4821     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
4822     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4823                 args, j);
4824 }
4825
4826 void ICSInputSendText()
4827 {
4828     Widget edit;
4829     int j;
4830     Arg args[16];
4831     String val;
4832   
4833     edit = XtNameToWidget(ICSInputShell, "*form.text");
4834     j = 0;
4835     XtSetArg(args[j], XtNstring, &val); j++;
4836     XtGetValues(edit, args, j);
4837     SendMultiLineToICS(val);
4838     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
4839     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
4840 }
4841
4842 void ICSInputBoxPopDown()
4843 {
4844     Arg args[16];
4845     int j;
4846
4847     if (!ICSInputBoxUp) return;
4848     j = 0;
4849     XtPopdown(ICSInputShell);
4850     ICSInputBoxUp = False;
4851     j = 0;
4852     XtSetArg(args[j], XtNleftBitmap, None); j++;
4853     XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
4854                 args, j);
4855 }
4856
4857 void CommentPopUp(title, text)
4858      char *title, *text;
4859 {
4860     Arg args[16];
4861     int j;
4862     Widget edit;
4863
4864     if (commentShell == NULL) {
4865         commentShell =
4866           CommentCreate(title, text, False, CommentCallback, 4);
4867         XtRealizeWidget(commentShell);
4868         CatchDeleteWindow(commentShell, "CommentPopDown");
4869     } else {
4870         edit = XtNameToWidget(commentShell, "*form.text");
4871         j = 0;
4872         XtSetArg(args[j], XtNstring, text); j++;
4873         XtSetValues(edit, args, j);
4874         j = 0;
4875         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4876         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4877         XtSetValues(commentShell, args, j);
4878     }
4879
4880     XtPopup(commentShell, XtGrabNone);
4881     XSync(xDisplay, False);
4882
4883     commentUp = True;
4884 }
4885
4886 void AnalysisPopUp(title, text)
4887      char *title, *text;
4888 {
4889     Arg args[16];
4890     int j;
4891     Widget edit;
4892
4893     if (analysisShell == NULL) {
4894         analysisShell = MiscCreate(title, text, False, NULL, 4);
4895         XtRealizeWidget(analysisShell);
4896         CatchDeleteWindow(analysisShell, "AnalysisPopDown");
4897
4898     } else {
4899         edit = XtNameToWidget(analysisShell, "*form.text");
4900         j = 0;
4901         XtSetArg(args[j], XtNstring, text); j++;
4902         XtSetValues(edit, args, j);
4903         j = 0;
4904         XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
4905         XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
4906         XtSetValues(analysisShell, args, j);
4907     }
4908
4909     if (!analysisUp) {
4910         XtPopup(analysisShell, XtGrabNone);
4911     }
4912     XSync(xDisplay, False);
4913
4914     analysisUp = True;
4915 }
4916
4917 void CommentCallback(w, client_data, call_data)
4918      Widget w;
4919      XtPointer client_data, call_data;
4920 {
4921     String name;
4922     Arg args[16];
4923     int j;
4924
4925     j = 0;
4926     XtSetArg(args[j], XtNlabel, &name);  j++;
4927     XtGetValues(w, args, j);
4928
4929     if (strcmp(name, "close") == 0) {
4930         CommentPopDown();
4931     } else if (strcmp(name, "edit") == 0) {
4932         CommentPopDown();
4933         EditCommentEvent();
4934     }
4935 }
4936
4937
4938 void CommentPopDown()
4939 {
4940     Arg args[16];
4941     int j;
4942
4943     if (!commentUp) return;
4944     j = 0;
4945     XtSetArg(args[j], XtNx, &commentX); j++;
4946     XtSetArg(args[j], XtNy, &commentY); j++;
4947     XtSetArg(args[j], XtNwidth, &commentW); j++;
4948     XtSetArg(args[j], XtNheight, &commentH); j++;
4949     XtGetValues(commentShell, args, j);
4950     XtPopdown(commentShell);
4951     XSync(xDisplay, False);
4952     commentUp = False;
4953 }
4954
4955 void AnalysisPopDown()
4956 {
4957     if (!analysisUp) return;
4958     XtPopdown(analysisShell);
4959     XSync(xDisplay, False);
4960     analysisUp = False;
4961 }
4962
4963
4964 void FileNamePopUp(label, def, proc, openMode)
4965      char *label;
4966      char *def;
4967      FileProc proc;
4968      char *openMode;
4969 {
4970     Arg args[16];
4971     Widget popup, layout, dialog, edit;
4972     Window root, child;
4973     int x, y, i;
4974     int win_x, win_y;
4975     unsigned int mask;
4976     
4977     fileProc = proc;            /* I can't see a way not */
4978     fileOpenMode = openMode;    /*   to use globals here */
4979     
4980     i = 0;
4981     XtSetArg(args[i], XtNresizable, True); i++;
4982     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
4983     fileNameShell = popup =
4984       XtCreatePopupShell("File name prompt", transientShellWidgetClass,
4985                          shellWidget, args, i);
4986     
4987     layout =
4988       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
4989                             layoutArgs, XtNumber(layoutArgs));
4990   
4991     i = 0;
4992     XtSetArg(args[i], XtNlabel, label); i++;
4993     XtSetArg(args[i], XtNvalue, def); i++;
4994     XtSetArg(args[i], XtNborderWidth, 0); i++;
4995     dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
4996                                    layout, args, i);
4997     
4998     XawDialogAddButton(dialog, "ok", FileNameCallback, (XtPointer) dialog);
4999     XawDialogAddButton(dialog, "cancel", FileNameCallback,
5000                        (XtPointer) dialog);
5001     
5002     XtRealizeWidget(popup);
5003     CatchDeleteWindow(popup, "FileNamePopDown");
5004     
5005     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
5006                   &x, &y, &win_x, &win_y, &mask);
5007     
5008     XtSetArg(args[0], XtNx, x - 10);
5009     XtSetArg(args[1], XtNy, y - 30);
5010     XtSetValues(popup, args, 2);
5011     
5012     XtPopup(popup, XtGrabExclusive);
5013     filenameUp = True;
5014     
5015     edit = XtNameToWidget(dialog, "*value");
5016     XtSetKeyboardFocus(popup, edit);
5017 }
5018
5019 void FileNamePopDown()
5020 {
5021     if (!filenameUp) return;
5022     XtPopdown(fileNameShell);
5023     XtDestroyWidget(fileNameShell);
5024     filenameUp = False;
5025     ModeHighlight();
5026 }
5027
5028 void FileNameCallback(w, client_data, call_data)
5029      Widget w;
5030      XtPointer client_data, call_data;
5031 {
5032     String name;
5033     Arg args[16];
5034     
5035     XtSetArg(args[0], XtNlabel, &name);
5036     XtGetValues(w, args, 1);
5037     
5038     if (strcmp(name, "cancel") == 0) {
5039         FileNamePopDown();
5040         return;
5041     }
5042     
5043     FileNameAction(w, NULL, NULL, NULL);
5044 }
5045
5046 void FileNameAction(w, event, prms, nprms)
5047      Widget w;
5048      XEvent *event;
5049      String *prms;
5050      Cardinal *nprms;
5051 {
5052     char buf[MSG_SIZ];
5053     String name;
5054     FILE *f;
5055     char *p, *fullname;
5056     int index;
5057
5058     name = XawDialogGetValueString(w = XtParent(w));
5059     
5060     if ((name != NULL) && (*name != NULLCHAR)) {
5061         strcpy(buf, name);
5062         XtPopdown(w = XtParent(XtParent(w)));
5063         XtDestroyWidget(w);
5064         filenameUp = False;
5065
5066         p = strrchr(buf, ' ');
5067         if (p == NULL) {
5068             index = 0;
5069         } else {
5070             *p++ = NULLCHAR;
5071             index = atoi(p);
5072         }
5073         fullname = ExpandPathName(buf);
5074         if (!fullname) {
5075             ErrorPopUp("Error", "Can't open file", FALSE);
5076         }
5077         else {
5078             f = fopen(fullname, fileOpenMode);
5079             if (f == NULL) {
5080                 DisplayError("Failed to open file", errno);
5081             } else {
5082                 (void) (*fileProc)(f, index, buf);
5083             }
5084         }
5085         ModeHighlight();
5086         return;
5087     }
5088     
5089     XtPopdown(w = XtParent(XtParent(w)));
5090     XtDestroyWidget(w);
5091     filenameUp = False;
5092     ModeHighlight();
5093 }
5094
5095 void PromotionPopUp()
5096 {
5097     Arg args[16];
5098     Widget dialog, layout;
5099     Position x, y;
5100     Dimension bw_width, pw_width;
5101     int j;
5102
5103     j = 0;
5104     XtSetArg(args[j], XtNwidth, &bw_width); j++;
5105     XtGetValues(boardWidget, args, j);
5106     
5107     j = 0;
5108     XtSetArg(args[j], XtNresizable, True); j++;
5109     promotionShell =
5110       XtCreatePopupShell("Promotion", transientShellWidgetClass,
5111                          shellWidget, args, j);
5112     layout =
5113       XtCreateManagedWidget(layoutName, formWidgetClass, promotionShell,
5114                             layoutArgs, XtNumber(layoutArgs));
5115     
5116     j = 0;
5117     XtSetArg(args[j], XtNlabel, "Promote pawn to what?"); j++;
5118     XtSetArg(args[j], XtNborderWidth, 0); j++;
5119     dialog = XtCreateManagedWidget("promotion", dialogWidgetClass,
5120                                    layout, args, j);
5121     
5122     XawDialogAddButton(dialog, "Queen", PromotionCallback, 
5123                        (XtPointer) dialog);
5124     XawDialogAddButton(dialog, "Rook", PromotionCallback, 
5125                        (XtPointer) dialog);
5126     XawDialogAddButton(dialog, "Bishop", PromotionCallback, 
5127                        (XtPointer) dialog);
5128     XawDialogAddButton(dialog, "Knight", PromotionCallback, 
5129                        (XtPointer) dialog);
5130     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
5131         gameInfo.variant == VariantGiveaway) {
5132       XawDialogAddButton(dialog, "King", PromotionCallback, 
5133                          (XtPointer) dialog);
5134     }
5135     XawDialogAddButton(dialog, "cancel", PromotionCallback, 
5136                        (XtPointer) dialog);
5137     
5138     XtRealizeWidget(promotionShell);
5139     CatchDeleteWindow(promotionShell, "PromotionPopDown");
5140     
5141     j = 0;
5142     XtSetArg(args[j], XtNwidth, &pw_width); j++;
5143     XtGetValues(promotionShell, args, j);
5144     
5145     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5146                       lineGap + squareSize/3 +
5147                       ((toY == 7) ^ (flipView) ?
5148                        0 : 6*(squareSize + lineGap)), &x, &y);
5149     
5150     j = 0;
5151     XtSetArg(args[j], XtNx, x); j++;
5152     XtSetArg(args[j], XtNy, y); j++;
5153     XtSetValues(promotionShell, args, j);
5154     
5155     XtPopup(promotionShell, XtGrabNone);
5156     
5157     promotionUp = True;
5158 }
5159
5160 void PromotionPopDown()
5161 {
5162     if (!promotionUp) return;
5163     XtPopdown(promotionShell);
5164     XtDestroyWidget(promotionShell);
5165     promotionUp = False;
5166 }
5167
5168 void PromotionCallback(w, client_data, call_data)
5169      Widget w;
5170      XtPointer client_data, call_data;
5171 {
5172     String name;
5173     Arg args[16];
5174     int promoChar;
5175     
5176     XtSetArg(args[0], XtNlabel, &name);
5177     XtGetValues(w, args, 1);
5178     
5179     PromotionPopDown();
5180     
5181     if (fromX == -1) return;
5182     
5183     if (strcmp(name, "cancel") == 0) {
5184         fromX = fromY = -1;
5185         ClearHighlights();
5186         return;
5187     } else if (strcmp(name, "Knight") == 0) {
5188         promoChar = 'n';
5189     } else {
5190         promoChar = ToLower(name[0]);
5191     }
5192
5193     UserMoveEvent(fromX, fromY, toX, toY, promoChar);
5194
5195     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
5196     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
5197     fromX = fromY = -1;
5198 }
5199
5200
5201 void ErrorCallback(w, client_data, call_data)
5202      Widget w;
5203      XtPointer client_data, call_data;
5204 {
5205     errorUp = False;
5206     XtPopdown(w = XtParent(XtParent(XtParent(w))));
5207     XtDestroyWidget(w);
5208     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5209 }
5210
5211
5212 void ErrorPopDown()
5213 {
5214     if (!errorUp) return;
5215     errorUp = False;
5216     XtPopdown(errorShell);
5217     XtDestroyWidget(errorShell);
5218     if (errorExitStatus != -1) ExitEvent(errorExitStatus);
5219 }
5220
5221 void ErrorPopUp(title, label, modal)
5222      char *title, *label;
5223      int modal;
5224 {
5225     Arg args[16];
5226     Widget dialog, layout;
5227     Position x, y;
5228     int xx, yy;
5229     Window junk;
5230     Dimension bw_width, pw_width;
5231     Dimension pw_height;
5232     int i;
5233     
5234     i = 0;
5235     XtSetArg(args[i], XtNresizable, True);  i++;
5236     XtSetArg(args[i], XtNtitle, title); i++;
5237     errorShell = 
5238       XtCreatePopupShell("errorpopup", transientShellWidgetClass,
5239                          shellWidget, args, i);
5240     layout =
5241       XtCreateManagedWidget(layoutName, formWidgetClass, errorShell,
5242                             layoutArgs, XtNumber(layoutArgs));
5243     
5244     i = 0;
5245     XtSetArg(args[i], XtNlabel, label); i++;
5246     XtSetArg(args[i], XtNborderWidth, 0); i++;
5247     dialog = XtCreateManagedWidget("dialog", dialogWidgetClass,
5248                                    layout, args, i);
5249     
5250     XawDialogAddButton(dialog, "ok", ErrorCallback, (XtPointer) dialog);
5251     
5252     XtRealizeWidget(errorShell);
5253     CatchDeleteWindow(errorShell, "ErrorPopDown");
5254     
5255     i = 0;
5256     XtSetArg(args[i], XtNwidth, &bw_width);  i++;
5257     XtGetValues(boardWidget, args, i);
5258     i = 0;
5259     XtSetArg(args[i], XtNwidth, &pw_width);  i++;
5260     XtSetArg(args[i], XtNheight, &pw_height);  i++;
5261     XtGetValues(errorShell, args, i);
5262
5263 #ifdef NOTDEF
5264     /* This code seems to tickle an X bug if it is executed too soon
5265        after xboard starts up.  The coordinates get transformed as if
5266        the main window was positioned at (0, 0).
5267        */
5268     XtTranslateCoords(boardWidget, (bw_width - pw_width) / 2,
5269                       0 - pw_height + squareSize / 3, &x, &y);
5270 #else
5271     XTranslateCoordinates(xDisplay, XtWindow(boardWidget),
5272                           RootWindowOfScreen(XtScreen(boardWidget)),
5273                           (bw_width - pw_width) / 2,
5274                           0 - pw_height + squareSize / 3, &xx, &yy, &junk);
5275     x = xx;
5276     y = yy;
5277 #endif
5278     if (y < 0) y = 0; /*avoid positioning top offscreen*/
5279
5280     i = 0;
5281     XtSetArg(args[i], XtNx, x);  i++;
5282     XtSetArg(args[i], XtNy, y);  i++;
5283     XtSetValues(errorShell, args, i);
5284
5285     errorUp = True;
5286     XtPopup(errorShell, modal ? XtGrabExclusive : XtGrabNone);
5287 }
5288
5289 /* Disable all user input other than deleting the window */
5290 static int frozen = 0;
5291 void FreezeUI()
5292 {
5293   if (frozen) return;
5294   /* Grab by a widget that doesn't accept input */
5295   XtAddGrab(messageWidget, TRUE, FALSE);
5296   frozen = 1;
5297 }
5298
5299 /* Undo a FreezeUI */
5300 void ThawUI()
5301 {
5302   if (!frozen) return;
5303   XtRemoveGrab(messageWidget);
5304   frozen = 0;
5305 }
5306
5307 char *ModeToWidgetName(mode)
5308      GameMode mode;
5309 {
5310     switch (mode) {
5311       case BeginningOfGame:
5312         if (appData.icsActive)
5313           return "menuMode.ICS Client";
5314         else if (appData.noChessProgram ||
5315                  *appData.cmailGameName != NULLCHAR)
5316           return "menuMode.Edit Game";
5317         else
5318           return "menuMode.Machine Black";
5319       case MachinePlaysBlack:
5320         return "menuMode.Machine Black";
5321       case MachinePlaysWhite:
5322         return "menuMode.Machine White";
5323       case AnalyzeMode:
5324         return "menuMode.Analysis Mode";
5325       case AnalyzeFile:
5326         return "menuMode.Analyze File";
5327       case TwoMachinesPlay:
5328         return "menuMode.Two Machines";
5329       case EditGame:
5330         return "menuMode.Edit Game";
5331       case PlayFromGameFile:
5332         return "menuFile.Load Game";
5333       case EditPosition:
5334         return "menuMode.Edit Position";
5335       case Training:
5336         return "menuMode.Training";
5337       case IcsPlayingWhite:
5338       case IcsPlayingBlack:
5339       case IcsObserving:
5340       case IcsIdle:
5341       case IcsExamining:
5342         return "menuMode.ICS Client";
5343       default:
5344       case EndOfGame:
5345         return NULL;
5346     }
5347 }     
5348
5349 void ModeHighlight()
5350 {
5351     Arg args[16];
5352     static int oldPausing = FALSE;
5353     static GameMode oldmode = (GameMode) -1;
5354     char *wname;
5355     
5356     if (!boardWidget || !XtIsRealized(boardWidget)) return;
5357
5358     if (pausing != oldPausing) {
5359         oldPausing = pausing;
5360         if (pausing) {
5361             XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5362         } else {
5363             XtSetArg(args[0], XtNleftBitmap, None);
5364         }
5365         XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Pause"),
5366                     args, 1);
5367
5368         if (appData.showButtonBar) {
5369 #if 0
5370           if (pausing) {
5371             XtSetArg(args[0], XtNbackground, buttonForegroundPixel);
5372             XtSetArg(args[1], XtNforeground, buttonBackgroundPixel);
5373           } else {
5374             XtSetArg(args[0], XtNbackground, buttonBackgroundPixel);
5375             XtSetArg(args[1], XtNforeground, buttonForegroundPixel);
5376           }
5377 #else
5378           /* Always toggle, don't set.  Previous code messes up when
5379              invoked while the button is pressed, as releasing it
5380              toggles the state again. */
5381           {
5382             Pixel oldbg, oldfg;
5383             XtSetArg(args[0], XtNbackground, &oldbg);
5384             XtSetArg(args[1], XtNforeground, &oldfg);
5385             XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON),
5386                         args, 2);
5387             XtSetArg(args[0], XtNbackground, oldfg);
5388             XtSetArg(args[1], XtNforeground, oldbg);
5389           }
5390 #endif
5391           XtSetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
5392         }
5393     }
5394
5395     wname = ModeToWidgetName(oldmode);
5396     if (wname != NULL) {
5397         XtSetArg(args[0], XtNleftBitmap, None);
5398         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5399     }
5400     wname = ModeToWidgetName(gameMode);
5401     if (wname != NULL) {
5402         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
5403         XtSetValues(XtNameToWidget(menuBarWidget, wname), args, 1);
5404     }
5405     oldmode = gameMode;
5406
5407     /* Maybe all the enables should be handled here, not just this one */
5408     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Training"),
5409                    gameMode == Training || gameMode == PlayFromGameFile);
5410 }
5411
5412
5413 /*
5414  * Button/menu procedures
5415  */
5416 void ResetProc(w, event, prms, nprms)
5417      Widget w;
5418      XEvent *event;
5419      String *prms;
5420      Cardinal *nprms;
5421 {
5422     ResetGameEvent();
5423     AnalysisPopDown();
5424 }
5425
5426 int LoadGamePopUp(f, gameNumber, title)
5427      FILE *f;
5428      int gameNumber;
5429      char *title;
5430 {
5431     cmailMsgLoaded = FALSE;
5432     if (gameNumber == 0) {
5433         int error = GameListBuild(f);
5434         if (error) {
5435             DisplayError("Cannot build game list", error);
5436         } else if (!ListEmpty(&gameList) &&
5437                    ((ListGame *) gameList.tailPred)->number > 1) {
5438             GameListPopUp(f, title);
5439             return TRUE;
5440         }
5441         GameListDestroy();
5442         gameNumber = 1;
5443     }
5444     return LoadGame(f, gameNumber, title, FALSE);
5445 }
5446
5447 void LoadGameProc(w, event, prms, nprms)
5448      Widget w;
5449      XEvent *event;
5450      String *prms;
5451      Cardinal *nprms;
5452 {
5453     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5454         Reset(FALSE, TRUE);
5455     }
5456     FileNamePopUp("Load game file name?", "", LoadGamePopUp, "rb");
5457 }
5458
5459 void LoadNextGameProc(w, event, prms, nprms)
5460      Widget w;
5461      XEvent *event;
5462      String *prms;
5463      Cardinal *nprms;
5464 {
5465     ReloadGame(1);
5466 }
5467
5468 void LoadPrevGameProc(w, event, prms, nprms)
5469      Widget w;
5470      XEvent *event;
5471      String *prms;
5472      Cardinal *nprms;
5473 {
5474     ReloadGame(-1);
5475 }
5476
5477 void ReloadGameProc(w, event, prms, nprms)
5478      Widget w;
5479      XEvent *event;
5480      String *prms;
5481      Cardinal *nprms;
5482 {
5483     ReloadGame(0);
5484 }
5485
5486 void LoadNextPositionProc(w, event, prms, nprms)
5487      Widget w;
5488      XEvent *event;
5489      String *prms;
5490      Cardinal *nprms;
5491 {
5492     ReloadPosition(1);
5493 }
5494
5495 void LoadPrevPositionProc(w, event, prms, nprms)
5496      Widget w;
5497      XEvent *event;
5498      String *prms;
5499      Cardinal *nprms;
5500 {
5501     ReloadPosition(-1);
5502 }
5503
5504 void ReloadPositionProc(w, event, prms, nprms)
5505      Widget w;
5506      XEvent *event;
5507      String *prms;
5508      Cardinal *nprms;
5509 {
5510     ReloadPosition(0);
5511 }
5512
5513 void LoadPositionProc(w, event, prms, nprms)
5514      Widget w;
5515      XEvent *event;
5516      String *prms;
5517      Cardinal *nprms;
5518 {
5519     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
5520         Reset(FALSE, TRUE);
5521     }
5522     FileNamePopUp("Load position file name?", "", LoadPosition, "rb");
5523 }
5524
5525 void SaveGameProc(w, event, prms, nprms)
5526      Widget w;
5527      XEvent *event;
5528      String *prms;
5529      Cardinal *nprms;
5530 {
5531     FileNamePopUp("Save game file name?",
5532                   DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
5533                   SaveGame, "a");
5534 }
5535
5536 void SavePositionProc(w, event, prms, nprms)
5537      Widget w;
5538      XEvent *event;
5539      String *prms;
5540      Cardinal *nprms;
5541 {
5542     FileNamePopUp("Save position file name?",
5543                   DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
5544                   SavePosition, "a");
5545 }
5546
5547 void ReloadCmailMsgProc(w, event, prms, nprms)
5548      Widget w;
5549      XEvent *event;
5550      String *prms;
5551      Cardinal *nprms;
5552 {
5553     ReloadCmailMsgEvent(FALSE);
5554 }
5555
5556 void MailMoveProc(w, event, prms, nprms)
5557      Widget w;
5558      XEvent *event;
5559      String *prms;
5560      Cardinal *nprms;
5561 {
5562     MailMoveEvent();
5563 }
5564
5565 /* this variable is shared between CopyPositionProc and SendPositionSelection */
5566 static char *selected_fen_position=NULL;
5567
5568 static Boolean
5569 SendPositionSelection(Widget w, Atom *selection, Atom *target,
5570                  Atom *type_return, XtPointer *value_return,
5571                  unsigned long *length_return, int *format_return)
5572 {
5573   char *selection_tmp;
5574
5575   if (!selected_fen_position) return False; /* should never happen */
5576   if (*target == XA_STRING){
5577     /* note: since no XtSelectionDoneProc was registered, Xt will
5578      * automatically call XtFree on the value returned.  So have to
5579      * make a copy of it allocated with XtMalloc */
5580     selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
5581     strcpy(selection_tmp, selected_fen_position);
5582
5583     *value_return=selection_tmp;
5584     *length_return=strlen(selection_tmp);
5585     *type_return=XA_STRING;
5586     *format_return = 8; /* bits per byte */
5587     return True;
5588   } else {
5589     return False;
5590   } 
5591 }
5592
5593 /* note: when called from menu all parameters are NULL, so no clue what the
5594  * Widget which was clicked on was, or what the click event was
5595  */
5596 void CopyPositionProc(w, event, prms, nprms)
5597   Widget w;
5598   XEvent *event;
5599   String *prms;
5600   Cardinal *nprms;
5601   {
5602     int ret;
5603
5604     if (selected_fen_position) free(selected_fen_position);
5605     selected_fen_position = (char *)PositionToFEN(currentMove);
5606     if (!selected_fen_position) return;
5607     ret = XtOwnSelection(menuBarWidget, XA_PRIMARY,
5608                          CurrentTime,
5609                          SendPositionSelection,
5610                          NULL/* lose_ownership_proc */ ,
5611                          NULL/* transfer_done_proc */);
5612     if (!ret) {
5613       free(selected_fen_position);
5614       selected_fen_position=NULL;
5615     }
5616   }
5617
5618 /* function called when the data to Paste is ready */
5619 static void
5620 PastePositionCB(Widget w, XtPointer client_data, Atom *selection,
5621            Atom *type, XtPointer value, unsigned long *len, int *format)
5622 {
5623   char *fenstr=value;
5624   if (value==NULL || *len==0) return; /* nothing had been selected to copy */
5625   fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
5626   EditPositionPasteFEN(fenstr);
5627   XtFree(value);
5628 }
5629
5630 /* called when Paste Position button is pressed,
5631  * all parameters will be NULL */
5632 void PastePositionProc(w, event, prms, nprms)
5633   Widget w;
5634   XEvent *event;
5635   String *prms;
5636   Cardinal *nprms;
5637 {
5638     XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING,
5639       /* (XtSelectionCallbackProc) */ PastePositionCB,
5640       NULL, /* client_data passed to PastePositionCB */
5641
5642       /* better to use the time field from the event that triggered the
5643        * call to this function, but that isn't trivial to get
5644        */
5645       CurrentTime
5646     );
5647     return;
5648 }
5649
5650 static Boolean
5651 SendGameSelection(Widget w, Atom *selection, Atom *target,
5652                   Atom *type_return, XtPointer *value_return,
5653                   unsigned long *length_return, int *format_return)
5654 {
5655   char *selection_tmp;
5656
5657   if (*target == XA_STRING){
5658     FILE* f = fopen(gameCopyFilename, "r");
5659     long len;
5660     size_t count;
5661     if (f == NULL) return False;
5662     fseek(f, 0, 2);
5663     len = ftell(f);
5664     rewind(f);
5665     selection_tmp = XtMalloc(len + 1);
5666     count = fread(selection_tmp, 1, len, f);
5667     if (len != count) {
5668       XtFree(selection_tmp);
5669       return False;
5670     }
5671     selection_tmp[len] = NULLCHAR;
5672     *value_return = selection_tmp;
5673     *length_return = len;
5674     *type_return = XA_STRING;
5675     *format_return = 8; /* bits per byte */
5676     return True;
5677   } else {
5678     return False;
5679   } 
5680 }
5681
5682 /* note: when called from menu all parameters are NULL, so no clue what the
5683  * Widget which was clicked on was, or what the click event was
5684  */
5685 void CopyGameProc(w, event, prms, nprms)
5686   Widget w;
5687   XEvent *event;
5688   String *prms;
5689   Cardinal *nprms;
5690 {
5691   int ret;
5692
5693   ret = SaveGameToFile(gameCopyFilename, FALSE);
5694   if (!ret) return;
5695
5696   ret = XtOwnSelection(menuBarWidget, XA_PRIMARY,
5697                        CurrentTime,
5698                        SendGameSelection,
5699                        NULL/* lose_ownership_proc */ ,
5700                        NULL/* transfer_done_proc */);
5701 }
5702
5703 /* function called when the data to Paste is ready */
5704 static void
5705 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
5706             Atom *type, XtPointer value, unsigned long *len, int *format)
5707 {
5708   FILE* f;
5709   if (value == NULL || *len == 0) {
5710     return; /* nothing had been selected to copy */
5711   }
5712   f = fopen(gamePasteFilename, "w");
5713   if (f == NULL) {
5714     DisplayError("Can't open temp file", errno);
5715     return;
5716   }
5717   fwrite(value, 1, *len, f);
5718   fclose(f);
5719   XtFree(value);
5720   LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
5721 }
5722
5723 /* called when Paste Game button is pressed,
5724  * all parameters will be NULL */
5725 void PasteGameProc(w, event, prms, nprms)
5726   Widget w;
5727   XEvent *event;
5728   String *prms;
5729   Cardinal *nprms;
5730 {
5731     XtGetSelectionValue(menuBarWidget, XA_PRIMARY, XA_STRING,
5732       /* (XtSelectionCallbackProc) */ PasteGameCB,
5733       NULL, /* client_data passed to PasteGameCB */
5734
5735       /* better to use the time field from the event that triggered the
5736        * call to this function, but that isn't trivial to get
5737        */
5738       CurrentTime
5739     );
5740     return;
5741 }
5742
5743
5744 void AutoSaveGame()
5745 {
5746     SaveGameProc(NULL, NULL, NULL, NULL);
5747 }
5748
5749
5750 void QuitProc(w, event, prms, nprms)
5751      Widget w;
5752      XEvent *event;
5753      String *prms;
5754      Cardinal *nprms;
5755 {
5756     ExitEvent(0);
5757 }
5758
5759 void PauseProc(w, event, prms, nprms)
5760      Widget w;
5761      XEvent *event;
5762      String *prms;
5763      Cardinal *nprms;
5764 {
5765     PauseEvent();
5766 }
5767
5768
5769 void MachineBlackProc(w, event, prms, nprms)
5770      Widget w;
5771      XEvent *event;
5772      String *prms;
5773      Cardinal *nprms;
5774 {
5775     MachineBlackEvent();
5776 }
5777
5778 void MachineWhiteProc(w, event, prms, nprms)
5779      Widget w;
5780      XEvent *event;
5781      String *prms;
5782      Cardinal *nprms;
5783 {
5784     MachineWhiteEvent();
5785 }
5786
5787 void AnalyzeModeProc(w, event, prms, nprms)
5788      Widget w;
5789      XEvent *event;
5790      String *prms;
5791      Cardinal *nprms;
5792 {
5793     if (!first.analysisSupport) {
5794       char buf[MSG_SIZ];
5795       sprintf(buf, "%s does not support analysis", first.tidy);
5796       DisplayError(buf, 0);
5797       return;
5798     }
5799     if (!appData.showThinking)
5800       ShowThinkingProc(w,event,prms,nprms);
5801
5802     AnalyzeModeEvent();
5803 }
5804
5805 void AnalyzeFileProc(w, event, prms, nprms)
5806      Widget w;
5807      XEvent *event;
5808      String *prms;
5809      Cardinal *nprms;
5810 {
5811     if (!first.analysisSupport) {
5812       char buf[MSG_SIZ];
5813       sprintf(buf, "%s does not support analysis", first.tidy);
5814       DisplayError(buf, 0);
5815       return;
5816     }
5817     Reset(FALSE, TRUE);
5818
5819     if (!appData.showThinking)
5820       ShowThinkingProc(w,event,prms,nprms);
5821
5822     AnalyzeFileEvent();
5823     FileNamePopUp("File to analyze", "", LoadGamePopUp, "rb");
5824     AnalysisPeriodicEvent(1);
5825 }
5826
5827 void TwoMachinesProc(w, event, prms, nprms)
5828      Widget w;
5829      XEvent *event;
5830      String *prms;
5831      Cardinal *nprms;
5832 {
5833     TwoMachinesEvent();
5834 }
5835
5836 void IcsClientProc(w, event, prms, nprms)
5837      Widget w;
5838      XEvent *event;
5839      String *prms;
5840      Cardinal *nprms;
5841 {
5842     IcsClientEvent();
5843 }
5844
5845 void EditGameProc(w, event, prms, nprms)
5846      Widget w;
5847      XEvent *event;
5848      String *prms;
5849      Cardinal *nprms;
5850 {
5851     EditGameEvent();
5852 }
5853
5854 void EditPositionProc(w, event, prms, nprms)
5855      Widget w;
5856      XEvent *event;
5857      String *prms;
5858      Cardinal *nprms;
5859 {
5860     EditPositionEvent();
5861 }
5862
5863 void TrainingProc(w, event, prms, nprms)
5864      Widget w;
5865      XEvent *event;
5866      String *prms;
5867      Cardinal *nprms;
5868 {
5869     TrainingEvent();
5870 }
5871
5872 void EditCommentProc(w, event, prms, nprms)
5873      Widget w;
5874      XEvent *event;
5875      String *prms;
5876      Cardinal *nprms;
5877 {
5878     if (editUp) {
5879         EditCommentPopDown();
5880     } else {
5881         EditCommentEvent();
5882     }
5883 }
5884
5885 void IcsInputBoxProc(w, event, prms, nprms)
5886      Widget w;
5887      XEvent *event;
5888      String *prms;
5889      Cardinal *nprms;
5890 {
5891     if (ICSInputBoxUp) {
5892         ICSInputBoxPopDown();
5893     } else {
5894         ICSInputBoxPopUp();
5895     }
5896 }
5897
5898 void AcceptProc(w, event, prms, nprms)
5899      Widget w;
5900      XEvent *event;
5901      String *prms;
5902      Cardinal *nprms;
5903 {
5904     AcceptEvent();
5905 }
5906
5907 void DeclineProc(w, event, prms, nprms)
5908      Widget w;
5909      XEvent *event;
5910      String *prms;
5911      Cardinal *nprms;
5912 {
5913     DeclineEvent();
5914 }
5915
5916 void RematchProc(w, event, prms, nprms)
5917      Widget w;
5918      XEvent *event;
5919      String *prms;
5920      Cardinal *nprms;
5921 {
5922     RematchEvent();
5923 }
5924
5925 void CallFlagProc(w, event, prms, nprms)
5926      Widget w;
5927      XEvent *event;
5928      String *prms;
5929      Cardinal *nprms;
5930 {
5931     CallFlagEvent();
5932 }
5933
5934 void DrawProc(w, event, prms, nprms)
5935      Widget w;
5936      XEvent *event;
5937      String *prms;
5938      Cardinal *nprms;
5939 {
5940     DrawEvent();
5941 }
5942
5943 void AbortProc(w, event, prms, nprms)
5944      Widget w;
5945      XEvent *event;
5946      String *prms;
5947      Cardinal *nprms;
5948 {
5949     AbortEvent();
5950 }
5951
5952 void AdjournProc(w, event, prms, nprms)
5953      Widget w;
5954      XEvent *event;
5955      String *prms;
5956      Cardinal *nprms;
5957 {
5958     AdjournEvent();
5959 }
5960
5961 void ResignProc(w, event, prms, nprms)
5962      Widget w;
5963      XEvent *event;
5964      String *prms;
5965      Cardinal *nprms;
5966 {
5967     ResignEvent();
5968 }
5969
5970 void EnterKeyProc(w, event, prms, nprms)
5971      Widget w;
5972      XEvent *event;
5973      String *prms;
5974      Cardinal *nprms;
5975 {
5976     if (ICSInputBoxUp == True)
5977       ICSInputSendText();
5978 }
5979
5980 void StopObservingProc(w, event, prms, nprms)
5981      Widget w;
5982      XEvent *event;
5983      String *prms;
5984      Cardinal *nprms;
5985 {
5986     StopObservingEvent();
5987 }
5988
5989 void StopExaminingProc(w, event, prms, nprms)
5990      Widget w;
5991      XEvent *event;
5992      String *prms;
5993      Cardinal *nprms;
5994 {
5995     StopExaminingEvent();
5996 }
5997
5998
5999 void ForwardProc(w, event, prms, nprms)
6000      Widget w;
6001      XEvent *event;
6002      String *prms;
6003      Cardinal *nprms;
6004 {
6005     ForwardEvent();
6006 }
6007
6008
6009 void BackwardProc(w, event, prms, nprms)
6010      Widget w;
6011      XEvent *event;
6012      String *prms;
6013      Cardinal *nprms;
6014 {
6015     BackwardEvent();
6016 }
6017
6018 void ToStartProc(w, event, prms, nprms)
6019      Widget w;
6020      XEvent *event;
6021      String *prms;
6022      Cardinal *nprms;
6023 {
6024     ToStartEvent();
6025 }
6026
6027 void ToEndProc(w, event, prms, nprms)
6028      Widget w;
6029      XEvent *event;
6030      String *prms;
6031      Cardinal *nprms;
6032 {
6033     ToEndEvent();
6034 }
6035
6036 void RevertProc(w, event, prms, nprms)
6037      Widget w;
6038      XEvent *event;
6039      String *prms;
6040      Cardinal *nprms;
6041 {
6042     RevertEvent();
6043 }
6044
6045 void TruncateGameProc(w, event, prms, nprms)
6046      Widget w;
6047      XEvent *event;
6048      String *prms;
6049      Cardinal *nprms;
6050 {
6051     TruncateGameEvent();
6052 }
6053 void RetractMoveProc(w, event, prms, nprms)
6054      Widget w;
6055      XEvent *event;
6056      String *prms;
6057      Cardinal *nprms;
6058 {
6059     RetractMoveEvent();
6060 }
6061
6062 void MoveNowProc(w, event, prms, nprms)
6063      Widget w;
6064      XEvent *event;
6065      String *prms;
6066      Cardinal *nprms;
6067 {
6068     MoveNowEvent();
6069 }
6070
6071
6072 void AlwaysQueenProc(w, event, prms, nprms)
6073      Widget w;
6074      XEvent *event;
6075      String *prms;
6076      Cardinal *nprms;
6077 {
6078     Arg args[16];
6079
6080     appData.alwaysPromoteToQueen = !appData.alwaysPromoteToQueen;
6081
6082     if (appData.alwaysPromoteToQueen) {
6083         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6084     } else {
6085         XtSetArg(args[0], XtNleftBitmap, None);
6086     }
6087     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
6088                 args, 1);
6089 }
6090
6091 void AnimateDraggingProc(w, event, prms, nprms)
6092      Widget w;
6093      XEvent *event;
6094      String *prms;
6095      Cardinal *nprms;
6096 {
6097     Arg args[16];
6098
6099     appData.animateDragging = !appData.animateDragging;
6100
6101     if (appData.animateDragging) {
6102         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6103         CreateAnimVars();
6104     } else {
6105         XtSetArg(args[0], XtNleftBitmap, None);
6106     }
6107     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Dragging"),
6108                 args, 1);
6109 }
6110
6111 void AnimateMovingProc(w, event, prms, nprms)
6112      Widget w;
6113      XEvent *event;
6114      String *prms;
6115      Cardinal *nprms;
6116 {
6117     Arg args[16];
6118
6119     appData.animate = !appData.animate;
6120
6121     if (appData.animate) {
6122         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6123         CreateAnimVars();
6124     } else {
6125         XtSetArg(args[0], XtNleftBitmap, None);
6126     }
6127     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
6128                 args, 1);
6129 }
6130
6131 void AutocommProc(w, event, prms, nprms)
6132      Widget w;
6133      XEvent *event;
6134      String *prms;
6135      Cardinal *nprms;
6136 {
6137     Arg args[16];
6138
6139     appData.autoComment = !appData.autoComment;
6140
6141     if (appData.autoComment) {
6142         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6143     } else {
6144         XtSetArg(args[0], XtNleftBitmap, None);
6145     }
6146     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
6147                 args, 1);
6148 }
6149
6150
6151 void AutoflagProc(w, event, prms, nprms)
6152      Widget w;
6153      XEvent *event;
6154      String *prms;
6155      Cardinal *nprms;
6156 {
6157     Arg args[16];
6158
6159     appData.autoCallFlag = !appData.autoCallFlag;
6160
6161     if (appData.autoCallFlag) {
6162         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6163     } else {
6164         XtSetArg(args[0], XtNleftBitmap, None);
6165     }
6166     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
6167                 args, 1);
6168 }
6169
6170 void AutoflipProc(w, event, prms, nprms)
6171      Widget w;
6172      XEvent *event;
6173      String *prms;
6174      Cardinal *nprms;
6175 {
6176     Arg args[16];
6177
6178     appData.autoFlipView = !appData.autoFlipView;
6179
6180     if (appData.autoFlipView) {
6181         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6182     } else {
6183         XtSetArg(args[0], XtNleftBitmap, None);
6184     }
6185     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flip View"),
6186                 args, 1);
6187 }
6188
6189 void AutobsProc(w, event, prms, nprms)
6190      Widget w;
6191      XEvent *event;
6192      String *prms;
6193      Cardinal *nprms;
6194 {
6195     Arg args[16];
6196
6197     appData.autoObserve = !appData.autoObserve;
6198
6199     if (appData.autoObserve) {
6200         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6201     } else {
6202         XtSetArg(args[0], XtNleftBitmap, None);
6203     }
6204     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
6205                 args, 1);
6206 }
6207
6208 void AutoraiseProc(w, event, prms, nprms)
6209      Widget w;
6210      XEvent *event;
6211      String *prms;
6212      Cardinal *nprms;
6213 {
6214     Arg args[16];
6215
6216     appData.autoRaiseBoard = !appData.autoRaiseBoard;
6217
6218     if (appData.autoRaiseBoard) {
6219         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6220     } else {
6221         XtSetArg(args[0], XtNleftBitmap, None);
6222     }
6223     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Raise Board"),
6224                 args, 1);
6225 }
6226
6227 void AutosaveProc(w, event, prms, nprms)
6228      Widget w;
6229      XEvent *event;
6230      String *prms;
6231      Cardinal *nprms;
6232 {
6233     Arg args[16];
6234
6235     appData.autoSaveGames = !appData.autoSaveGames;
6236
6237     if (appData.autoSaveGames) {
6238         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6239     } else {
6240         XtSetArg(args[0], XtNleftBitmap, None);
6241     }
6242     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
6243                 args, 1);
6244 }
6245
6246 void BlindfoldProc(w, event, prms, nprms)
6247      Widget w;
6248      XEvent *event;
6249      String *prms;
6250      Cardinal *nprms;
6251 {
6252     Arg args[16];
6253
6254     appData.blindfold = !appData.blindfold;
6255
6256     if (appData.blindfold) {
6257         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6258     } else {
6259         XtSetArg(args[0], XtNleftBitmap, None);
6260     }
6261     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Blindfold"),
6262                 args, 1);
6263
6264     DrawPosition(True, NULL);
6265 }
6266
6267 void TestLegalityProc(w, event, prms, nprms)
6268      Widget w;
6269      XEvent *event;
6270      String *prms;
6271      Cardinal *nprms;
6272 {
6273     Arg args[16];
6274
6275     appData.testLegality = !appData.testLegality;
6276
6277     if (appData.testLegality) {
6278         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6279     } else {
6280         XtSetArg(args[0], XtNleftBitmap, None);
6281     }
6282     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Test Legality"),
6283                 args, 1);
6284 }
6285
6286
6287 void FlashMovesProc(w, event, prms, nprms)
6288      Widget w;
6289      XEvent *event;
6290      String *prms;
6291      Cardinal *nprms;
6292 {
6293     Arg args[16];
6294
6295     if (appData.flashCount == 0) {
6296         appData.flashCount = 3;
6297     } else {
6298         appData.flashCount = -appData.flashCount;
6299     }
6300
6301     if (appData.flashCount > 0) {
6302         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6303     } else {
6304         XtSetArg(args[0], XtNleftBitmap, None);
6305     }
6306     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flash Moves"),
6307                 args, 1);
6308 }
6309
6310 void FlipViewProc(w, event, prms, nprms)
6311      Widget w;
6312      XEvent *event;
6313      String *prms;
6314      Cardinal *nprms;
6315 {
6316     flipView = !flipView;
6317     DrawPosition(True, NULL);
6318 }
6319
6320 void GetMoveListProc(w, event, prms, nprms)
6321      Widget w;
6322      XEvent *event;
6323      String *prms;
6324      Cardinal *nprms;
6325 {
6326     Arg args[16];
6327
6328     appData.getMoveList = !appData.getMoveList;
6329
6330     if (appData.getMoveList) {
6331         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6332         GetMoveListEvent();
6333     } else {
6334         XtSetArg(args[0], XtNleftBitmap, None);
6335     }
6336     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
6337                 args, 1);
6338 }
6339
6340 #if HIGHDRAG
6341 void HighlightDraggingProc(w, event, prms, nprms)
6342      Widget w;
6343      XEvent *event;
6344      String *prms;
6345      Cardinal *nprms;
6346 {
6347     Arg args[16];
6348
6349     appData.highlightDragging = !appData.highlightDragging;
6350
6351     if (appData.highlightDragging) {
6352         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6353     } else {
6354         XtSetArg(args[0], XtNleftBitmap, None);
6355     }
6356     XtSetValues(XtNameToWidget(menuBarWidget,
6357                                "menuOptions.Highlight Dragging"), args, 1);
6358 }
6359 #endif
6360
6361 void HighlightLastMoveProc(w, event, prms, nprms)
6362      Widget w;
6363      XEvent *event;
6364      String *prms;
6365      Cardinal *nprms;
6366 {
6367     Arg args[16];
6368
6369     appData.highlightLastMove = !appData.highlightLastMove;
6370
6371     if (appData.highlightLastMove) {
6372         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6373     } else {
6374         XtSetArg(args[0], XtNleftBitmap, None);
6375     }
6376     XtSetValues(XtNameToWidget(menuBarWidget,
6377                                "menuOptions.Highlight Last Move"), args, 1);
6378 }
6379
6380 void IcsAlarmProc(w, event, prms, nprms)
6381      Widget w;
6382      XEvent *event;
6383      String *prms;
6384      Cardinal *nprms;
6385 {
6386     Arg args[16];
6387
6388     appData.icsAlarm = !appData.icsAlarm;
6389
6390     if (appData.icsAlarm) {
6391         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6392     } else {
6393         XtSetArg(args[0], XtNleftBitmap, None);
6394     }
6395     XtSetValues(XtNameToWidget(menuBarWidget,
6396                                "menuOptions.ICS Alarm"), args, 1);
6397 }
6398
6399 void MoveSoundProc(w, event, prms, nprms)
6400      Widget w;
6401      XEvent *event;
6402      String *prms;
6403      Cardinal *nprms;
6404 {
6405     Arg args[16];
6406
6407     appData.ringBellAfterMoves = !appData.ringBellAfterMoves;
6408
6409     if (appData.ringBellAfterMoves) {
6410         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6411     } else {
6412         XtSetArg(args[0], XtNleftBitmap, None);
6413     }
6414     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
6415                 args, 1);
6416 }
6417
6418
6419 void OldSaveStyleProc(w, event, prms, nprms)
6420      Widget w;
6421      XEvent *event;
6422      String *prms;
6423      Cardinal *nprms;
6424 {
6425     Arg args[16];
6426
6427     appData.oldSaveStyle = !appData.oldSaveStyle;
6428
6429     if (appData.oldSaveStyle) {
6430         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6431     } else {
6432         XtSetArg(args[0], XtNleftBitmap, None);
6433     }
6434     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
6435                 args, 1);
6436 }
6437
6438 void PeriodicUpdatesProc(w, event, prms, nprms)
6439      Widget w;
6440      XEvent *event;
6441      String *prms;
6442      Cardinal *nprms;
6443 {
6444     Arg args[16];
6445
6446     PeriodicUpdatesEvent(!appData.periodicUpdates);
6447         
6448     if (appData.periodicUpdates) {
6449         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6450     } else {
6451         XtSetArg(args[0], XtNleftBitmap, None);
6452     }
6453     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Periodic Updates"),
6454                 args, 1);
6455 }
6456
6457 void PonderNextMoveProc(w, event, prms, nprms)
6458      Widget w;
6459      XEvent *event;
6460      String *prms;
6461      Cardinal *nprms;
6462 {
6463     Arg args[16];
6464
6465     PonderNextMoveEvent(!appData.ponderNextMove);
6466
6467     if (appData.ponderNextMove) {
6468         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6469     } else {
6470         XtSetArg(args[0], XtNleftBitmap, None);
6471     }
6472     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Ponder Next Move"),
6473                 args, 1);
6474 }
6475
6476 void PopupExitMessageProc(w, event, prms, nprms)
6477      Widget w;
6478      XEvent *event;
6479      String *prms;
6480      Cardinal *nprms;
6481 {
6482     Arg args[16];
6483
6484     appData.popupExitMessage = !appData.popupExitMessage;
6485
6486     if (appData.popupExitMessage) {
6487         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6488     } else {
6489         XtSetArg(args[0], XtNleftBitmap, None);
6490     }
6491     XtSetValues(XtNameToWidget(menuBarWidget,
6492                                "menuOptions.Popup Exit Message"), args, 1);
6493 }
6494
6495 void PopupMoveErrorsProc(w, event, prms, nprms)
6496      Widget w;
6497      XEvent *event;
6498      String *prms;
6499      Cardinal *nprms;
6500 {
6501     Arg args[16];
6502
6503     appData.popupMoveErrors = !appData.popupMoveErrors;
6504
6505     if (appData.popupMoveErrors) {
6506         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6507     } else {
6508         XtSetArg(args[0], XtNleftBitmap, None);
6509     }
6510     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Popup Move Errors"),
6511                 args, 1);
6512 }
6513
6514 void PremoveProc(w, event, prms, nprms)
6515      Widget w;
6516      XEvent *event;
6517      String *prms;
6518      Cardinal *nprms;
6519 {
6520     Arg args[16];
6521
6522     appData.premove = !appData.premove;
6523
6524     if (appData.premove) {
6525         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6526     } else {
6527         XtSetArg(args[0], XtNleftBitmap, None);
6528     }
6529     XtSetValues(XtNameToWidget(menuBarWidget,
6530                                "menuOptions.Premove"), args, 1);
6531 }
6532
6533 void QuietPlayProc(w, event, prms, nprms)
6534      Widget w;
6535      XEvent *event;
6536      String *prms;
6537      Cardinal *nprms;
6538 {
6539     Arg args[16];
6540
6541     appData.quietPlay = !appData.quietPlay;
6542
6543     if (appData.quietPlay) {
6544         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6545     } else {
6546         XtSetArg(args[0], XtNleftBitmap, None);
6547     }
6548     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Quiet Play"),
6549                 args, 1);
6550 }
6551
6552 void ShowCoordsProc(w, event, prms, nprms)
6553      Widget w;
6554      XEvent *event;
6555      String *prms;
6556      Cardinal *nprms;
6557 {
6558     Arg args[16];
6559
6560     appData.showCoords = !appData.showCoords;
6561
6562     if (appData.showCoords) {
6563         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6564     } else {
6565         XtSetArg(args[0], XtNleftBitmap, None);
6566     }
6567     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
6568                 args, 1);
6569
6570     DrawPosition(True, NULL);
6571 }
6572
6573 void ShowThinkingProc(w, event, prms, nprms)
6574      Widget w;
6575      XEvent *event;
6576      String *prms;
6577      Cardinal *nprms;
6578 {
6579     Arg args[16];
6580
6581     ShowThinkingEvent(!appData.showThinking);
6582
6583     if (appData.showThinking) {
6584         XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
6585     } else {
6586         XtSetArg(args[0], XtNleftBitmap, None);
6587     }
6588     XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Thinking"),
6589                 args, 1);
6590 }
6591
6592 void InfoProc(w, event, prms, nprms)
6593      Widget w;
6594      XEvent *event;
6595      String *prms;
6596      Cardinal *nprms;
6597 {
6598     char buf[MSG_SIZ];
6599     sprintf(buf, "xterm -e info --directory %s --directory . -f %s &",
6600             INFODIR, INFOFILE);
6601     system(buf);
6602 }
6603
6604 void ManProc(w, event, prms, nprms)
6605      Widget w;
6606      XEvent *event;
6607      String *prms;
6608      Cardinal *nprms;
6609 {
6610     char buf[MSG_SIZ];
6611     String name;
6612     if (nprms && *nprms > 0)
6613       name = prms[0];
6614     else
6615       name = "xboard";
6616     sprintf(buf, "xterm -e man %s &", name);
6617     system(buf);
6618 }
6619
6620 void HintProc(w, event, prms, nprms)
6621      Widget w;
6622      XEvent *event;
6623      String *prms;
6624      Cardinal *nprms;
6625 {
6626     HintEvent();
6627 }
6628
6629 void BookProc(w, event, prms, nprms)
6630      Widget w;
6631      XEvent *event;
6632      String *prms;
6633      Cardinal *nprms;
6634 {
6635     BookEvent();
6636 }
6637
6638 void AboutProc(w, event, prms, nprms)
6639      Widget w;
6640      XEvent *event;
6641      String *prms;
6642      Cardinal *nprms;
6643 {
6644     char buf[MSG_SIZ];
6645 #if ZIPPY
6646     char *zippy = " (with Zippy code)";
6647 #else
6648     char *zippy = "";
6649 #endif
6650     sprintf(buf, "%s%s\n\n%s\n%s\n\n%s%s\n%s",
6651             programVersion, zippy,
6652             "Copyright 1991 Digital Equipment Corporation",
6653             "Enhancements Copyright 1992-2001 Free Software Foundation",
6654             PRODUCT, " is free software and carries NO WARRANTY;",
6655             "see the file COPYING for more information.");
6656     ErrorPopUp("About XBoard", buf, FALSE);
6657 }
6658
6659 void DebugProc(w, event, prms, nprms)
6660      Widget w;
6661      XEvent *event;
6662      String *prms;
6663      Cardinal *nprms;
6664 {
6665     appData.debugMode = !appData.debugMode;
6666 }
6667
6668 void AboutGameProc(w, event, prms, nprms)
6669      Widget w;
6670      XEvent *event;
6671      String *prms;
6672      Cardinal *nprms;
6673 {
6674     AboutGameEvent();
6675 }
6676
6677 void NothingProc(w, event, prms, nprms)
6678      Widget w;
6679      XEvent *event;
6680      String *prms;
6681      Cardinal *nprms;
6682 {
6683     return;
6684 }
6685
6686 void Iconify(w, event, prms, nprms)
6687      Widget w;
6688      XEvent *event;
6689      String *prms;
6690      Cardinal *nprms;
6691 {
6692     Arg args[16];
6693     
6694     fromX = fromY = -1;
6695     XtSetArg(args[0], XtNiconic, True);
6696     XtSetValues(shellWidget, args, 1);
6697 }
6698
6699 void DisplayMessage(message, extMessage)
6700      char *message, *extMessage;
6701 {
6702     char buf[MSG_SIZ];
6703     Arg arg;
6704     
6705     if (extMessage) {
6706         if (*message) {
6707             sprintf(buf, "%s  %s", message, extMessage);
6708             message = buf;
6709         } else {
6710             message = extMessage;
6711         }
6712     }
6713     XtSetArg(arg, XtNlabel, message);
6714     XtSetValues(messageWidget, &arg, 1);
6715 }
6716
6717 void DisplayTitle(text)
6718      char *text;
6719 {
6720     Arg args[16];
6721     int i;
6722     char title[MSG_SIZ];
6723     char icon[MSG_SIZ];
6724
6725     if (text == NULL) text = "";
6726
6727     if (appData.titleInWindow) {
6728         i = 0;
6729         XtSetArg(args[i], XtNlabel, text);   i++;
6730         XtSetValues(titleWidget, args, i);
6731     }
6732
6733     if (*text != NULLCHAR) {
6734         strcpy(icon, text);
6735         strcpy(title, text);
6736     } else if (appData.icsActive) {
6737         sprintf(icon, "%s", appData.icsHost);
6738         sprintf(title, "%s: %s", programName, appData.icsHost);
6739     } else if (appData.cmailGameName[0] != NULLCHAR) {
6740         sprintf(icon, "%s", "CMail");
6741         sprintf(title, "%s: %s", programName, "CMail");
6742     } else if (appData.noChessProgram) {
6743         strcpy(icon, programName);
6744         strcpy(title, programName);
6745     } else {
6746         strcpy(icon, first.tidy);
6747         sprintf(title, "%s: %s", programName, first.tidy);
6748     }
6749     i = 0;
6750     XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
6751     XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
6752     XtSetValues(shellWidget, args, i);
6753 }
6754
6755
6756 void DisplayError(message, error)
6757      String message;
6758      int error;
6759 {
6760     char buf[MSG_SIZ];
6761
6762     if (error == 0) {
6763         if (appData.debugMode || appData.matchMode) {
6764             fprintf(stderr, "%s: %s\n", programName, message);
6765         }
6766     } else {
6767         if (appData.debugMode || appData.matchMode) {
6768             fprintf(stderr, "%s: %s: %s\n",
6769                     programName, message, strerror(error));
6770         }
6771         sprintf(buf, "%s: %s", message, strerror(error));
6772         message = buf;
6773     }   
6774     ErrorPopUp("Error", message, FALSE);
6775 }
6776
6777
6778 void DisplayMoveError(message)
6779      String message;
6780 {
6781     fromX = fromY = -1;
6782     ClearHighlights();
6783     DrawPosition(FALSE, NULL);
6784     if (appData.debugMode || appData.matchMode) {
6785         fprintf(stderr, "%s: %s\n", programName, message);
6786     }
6787     if (appData.popupMoveErrors) {
6788         ErrorPopUp("Error", message, FALSE);
6789     } else {
6790         DisplayMessage(message, "");
6791     }
6792 }
6793
6794
6795 void DisplayFatalError(message, error, status)
6796      String message;
6797      int error, status;
6798 {
6799     char buf[MSG_SIZ];
6800
6801     errorExitStatus = status;
6802     if (error == 0) {
6803         fprintf(stderr, "%s: %s\n", programName, message);
6804     } else {
6805         fprintf(stderr, "%s: %s: %s\n",
6806                 programName, message, strerror(error));
6807         sprintf(buf, "%s: %s", message, strerror(error));
6808         message = buf;
6809     }
6810     if (appData.popupExitMessage && boardWidget && XtIsRealized(boardWidget)) {
6811       ErrorPopUp(status ? "Fatal Error" : "Exiting", message, TRUE);
6812     } else {
6813       ExitEvent(status);
6814     }
6815 }
6816
6817 void DisplayInformation(message)
6818      String message;
6819 {
6820     ErrorPopDown();
6821     ErrorPopUp("Information", message, TRUE);
6822 }
6823
6824 static int
6825 NullXErrorCheck(dpy, error_event)
6826      Display *dpy;
6827      XErrorEvent *error_event;
6828 {
6829     return 0;
6830 }
6831
6832 void DisplayIcsInteractionTitle(message)
6833      String message;
6834 {
6835   if (oldICSInteractionTitle == NULL) {
6836     /* Magic to find the old window title, adapted from vim */
6837     char *wina = getenv("WINDOWID");
6838     if (wina != NULL) {
6839       Window win = (Window) atoi(wina);
6840       Window root, parent, *children;
6841       unsigned int nchildren;
6842       int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
6843       for (;;) {
6844         if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
6845         if (!XQueryTree(xDisplay, win, &root, &parent,
6846                         &children, &nchildren)) break;
6847         if (children) XFree((void *)children);
6848         if (parent == root || parent == 0) break;
6849         win = parent;
6850       }
6851       XSetErrorHandler(oldHandler);
6852     }
6853     if (oldICSInteractionTitle == NULL) {
6854       oldICSInteractionTitle = "xterm"; 
6855     }
6856   }  
6857   printf("\033]0;%s\007", message);
6858   fflush(stdout);
6859 }
6860
6861 char pendingReplyPrefix[MSG_SIZ];
6862 ProcRef pendingReplyPR;
6863
6864 void AskQuestionProc(w, event, prms, nprms)
6865      Widget w;
6866      XEvent *event;
6867      String *prms;
6868      Cardinal *nprms;
6869 {
6870     if (*nprms != 4) {
6871         fprintf(stderr, "AskQuestionProc needed 4 parameters, got %d\n",
6872                 *nprms);
6873         return;
6874     }
6875     AskQuestionEvent(prms[0], prms[1], prms[2], prms[3]);
6876 }
6877
6878 void AskQuestionPopDown()
6879 {
6880     if (!askQuestionUp) return;
6881     XtPopdown(askQuestionShell);
6882     XtDestroyWidget(askQuestionShell);
6883     askQuestionUp = False;
6884 }
6885
6886 void AskQuestionReplyAction(w, event, prms, nprms)
6887      Widget w;
6888      XEvent *event;
6889      String *prms;
6890      Cardinal *nprms;
6891 {
6892     char buf[MSG_SIZ];
6893     int err;
6894     String reply;
6895
6896     reply = XawDialogGetValueString(w = XtParent(w));
6897     strcpy(buf, pendingReplyPrefix);
6898     if (*buf) strcat(buf, " ");
6899     strcat(buf, reply);
6900     strcat(buf, "\n");
6901     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
6902     AskQuestionPopDown();
6903
6904     if (err) DisplayFatalError("Error writing to chess program", err, 0);
6905 }
6906
6907 void AskQuestionCallback(w, client_data, call_data)
6908      Widget w;
6909      XtPointer client_data, call_data;
6910 {
6911     String name;
6912     Arg args[16];
6913
6914     XtSetArg(args[0], XtNlabel, &name);
6915     XtGetValues(w, args, 1);
6916     
6917     if (strcmp(name, "cancel") == 0) {
6918         AskQuestionPopDown();
6919     } else {
6920         AskQuestionReplyAction(w, NULL, NULL, NULL);
6921     }
6922 }
6923
6924 void AskQuestion(title, question, replyPrefix, pr)
6925      char *title, *question, *replyPrefix;
6926      ProcRef pr;
6927 {
6928     Arg args[16];
6929     Widget popup, layout, dialog, edit;
6930     Window root, child;
6931     int x, y, i;
6932     int win_x, win_y;
6933     unsigned int mask;
6934     
6935     strcpy(pendingReplyPrefix, replyPrefix);
6936     pendingReplyPR = pr;
6937     
6938     i = 0;
6939     XtSetArg(args[i], XtNresizable, True); i++;
6940     XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
6941     askQuestionShell = popup =
6942       XtCreatePopupShell(title, transientShellWidgetClass,
6943                          shellWidget, args, i);
6944     
6945     layout =
6946       XtCreateManagedWidget(layoutName, formWidgetClass, popup,
6947                             layoutArgs, XtNumber(layoutArgs));
6948   
6949     i = 0;
6950     XtSetArg(args[i], XtNlabel, question); i++;
6951     XtSetArg(args[i], XtNvalue, ""); i++;
6952     XtSetArg(args[i], XtNborderWidth, 0); i++;
6953     dialog = XtCreateManagedWidget("question", dialogWidgetClass,
6954                                    layout, args, i);
6955     
6956     XawDialogAddButton(dialog, "enter", AskQuestionCallback,
6957                        (XtPointer) dialog);
6958     XawDialogAddButton(dialog, "cancel", AskQuestionCallback,
6959                        (XtPointer) dialog);
6960
6961     XtRealizeWidget(popup);
6962     CatchDeleteWindow(popup, "AskQuestionPopDown");
6963     
6964     XQueryPointer(xDisplay, xBoardWindow, &root, &child,
6965                   &x, &y, &win_x, &win_y, &mask);
6966     
6967     XtSetArg(args[0], XtNx, x - 10);
6968     XtSetArg(args[1], XtNy, y - 30);
6969     XtSetValues(popup, args, 2);
6970     
6971     XtPopup(popup, XtGrabExclusive);
6972     askQuestionUp = True;
6973     
6974     edit = XtNameToWidget(dialog, "*value");
6975     XtSetKeyboardFocus(popup, edit);
6976 }
6977
6978
6979 void
6980 PlaySound(name)
6981      char *name;
6982 {
6983   if (*name == NULLCHAR) {
6984     return;
6985   } else if (strcmp(name, "$") == 0) {
6986     putc(BELLCHAR, stderr);
6987   } else {
6988     char buf[2048];
6989     sprintf(buf, "%s '%s' &", appData.soundProgram, name);
6990     system(buf);
6991   }
6992 }
6993
6994 void
6995 RingBell()
6996 {
6997   PlaySound(appData.soundMove);
6998 }
6999
7000 void
7001 PlayIcsWinSound()
7002 {
7003   PlaySound(appData.soundIcsWin);
7004 }
7005
7006 void
7007 PlayIcsLossSound()
7008 {
7009   PlaySound(appData.soundIcsLoss);
7010 }
7011
7012 void
7013 PlayIcsDrawSound()
7014 {
7015   PlaySound(appData.soundIcsDraw);
7016 }
7017
7018 void
7019 PlayIcsUnfinishedSound()
7020 {
7021   PlaySound(appData.soundIcsUnfinished);
7022 }
7023
7024 void
7025 PlayAlarmSound()
7026 {
7027   PlaySound(appData.soundIcsAlarm);
7028 }
7029
7030 void
7031 EchoOn()
7032 {
7033     system("stty echo");
7034 }
7035
7036 void
7037 EchoOff()
7038 {
7039     system("stty -echo");
7040 }
7041
7042 void
7043 Colorize(cc, continuation)
7044      ColorClass cc;
7045      int continuation;
7046 {
7047     char buf[MSG_SIZ];
7048     int count, outCount, error;
7049
7050     if (textColors[(int)cc].bg > 0) {
7051         if (textColors[(int)cc].fg > 0) {
7052             sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
7053                     textColors[(int)cc].fg, textColors[(int)cc].bg);
7054         } else {
7055             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7056                     textColors[(int)cc].bg);
7057         }
7058     } else {
7059         if (textColors[(int)cc].fg > 0) {
7060             sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
7061                     textColors[(int)cc].fg);
7062         } else {
7063             sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
7064         }
7065     }
7066     count = strlen(buf);
7067     outCount = OutputToProcess(NoProc, buf, count, &error);
7068     if (outCount < count) {
7069         DisplayFatalError("Error writing to display", error, 1);
7070     }
7071
7072     if (continuation) return;
7073     switch (cc) {
7074     case ColorShout:
7075       PlaySound(appData.soundShout);
7076       break;
7077     case ColorSShout:
7078       PlaySound(appData.soundSShout);
7079       break;
7080     case ColorChannel1:
7081       PlaySound(appData.soundChannel1);
7082       break;
7083     case ColorChannel:
7084       PlaySound(appData.soundChannel);
7085       break;
7086     case ColorKibitz:
7087       PlaySound(appData.soundKibitz);
7088       break;
7089     case ColorTell:
7090       PlaySound(appData.soundTell);
7091       break;
7092     case ColorChallenge:
7093       PlaySound(appData.soundChallenge);
7094       break;
7095     case ColorRequest:
7096       PlaySound(appData.soundRequest);
7097       break;
7098     case ColorSeek:
7099       PlaySound(appData.soundSeek);
7100       break;
7101     case ColorNormal:
7102     case ColorNone:
7103     default:
7104       break;
7105     }
7106 }
7107
7108 char *UserName()
7109 {
7110     return getpwuid(getuid())->pw_name;
7111 }
7112
7113 static char *ExpandPathName(path)
7114      char *path;
7115 {
7116     static char static_buf[2000];
7117     char *d, *s, buf[2000];
7118     struct passwd *pwd;
7119   
7120     s = path;
7121     d = static_buf;
7122
7123     while (*s && isspace(*s))
7124       ++s;
7125
7126     if (!*s) {
7127         *d = 0;
7128         return static_buf;
7129     }
7130
7131     if (*s == '~') {
7132         if (*(s+1) == '/') {
7133             strcpy(d, getpwuid(getuid())->pw_dir);
7134             strcat(d, s+1);
7135         }
7136         else {
7137             strcpy(buf, s+1);
7138             *strchr(buf, '/') = 0;
7139             pwd = getpwnam(buf);
7140             if (!pwd)
7141               {
7142                   fprintf(stderr, "ERROR: Unknown user %s (in path %s)\n",
7143                           buf, path);
7144                   return NULL;
7145               }
7146             strcpy(d, pwd->pw_dir);
7147             strcat(d, strchr(s+1, '/'));
7148         }
7149     }
7150     else
7151       strcpy(d, s);
7152
7153     return static_buf;
7154 }  
7155
7156 char *HostName()
7157 {
7158     static char host_name[MSG_SIZ];
7159     
7160 #if HAVE_GETHOSTNAME
7161     gethostname(host_name, MSG_SIZ);
7162     return host_name;
7163 #else  /* not HAVE_GETHOSTNAME */
7164 # if HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H
7165     sysinfo(SI_HOSTNAME, host_name, MSG_SIZ);
7166     return host_name;
7167 # else /* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7168     return "localhost";
7169 # endif/* not (HAVE_SYSINFO && HAVE_SYS_SYSTEMINFO_H) */
7170 #endif /* not HAVE_GETHOSTNAME */
7171 }
7172
7173 XtIntervalId delayedEventTimerXID = 0;
7174 DelayedEventCallback delayedEventCallback = 0;
7175
7176 void
7177 FireDelayedEvent()
7178 {
7179     delayedEventTimerXID = 0;
7180     delayedEventCallback();
7181 }
7182
7183 void
7184 ScheduleDelayedEvent(cb, millisec)
7185      DelayedEventCallback cb; long millisec;
7186 {
7187     delayedEventCallback = cb;
7188     delayedEventTimerXID =
7189       XtAppAddTimeOut(appContext, millisec,
7190                       (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
7191 }
7192
7193 DelayedEventCallback
7194 GetDelayedEvent()
7195 {
7196   if (delayedEventTimerXID) {
7197     return delayedEventCallback;
7198   } else {
7199     return NULL;
7200   }
7201 }
7202
7203 void
7204 CancelDelayedEvent()
7205 {
7206   if (delayedEventTimerXID) {
7207     XtRemoveTimeOut(delayedEventTimerXID);
7208     delayedEventTimerXID = 0;
7209   }
7210 }
7211
7212 XtIntervalId loadGameTimerXID = 0;
7213
7214 int LoadGameTimerRunning()
7215 {
7216     return loadGameTimerXID != 0;
7217 }
7218
7219 int StopLoadGameTimer()
7220 {
7221     if (loadGameTimerXID != 0) {
7222         XtRemoveTimeOut(loadGameTimerXID);
7223         loadGameTimerXID = 0;
7224         return TRUE;
7225     } else {
7226         return FALSE;
7227     }
7228 }
7229
7230 void
7231 LoadGameTimerCallback(arg, id)
7232      XtPointer arg;
7233      XtIntervalId *id;
7234 {
7235     loadGameTimerXID = 0;
7236     AutoPlayGameLoop();
7237 }
7238
7239 void
7240 StartLoadGameTimer(millisec)
7241      long millisec;
7242 {
7243     loadGameTimerXID =
7244       XtAppAddTimeOut(appContext, millisec,
7245                       (XtTimerCallbackProc) LoadGameTimerCallback,
7246                       (XtPointer) 0);
7247 }
7248
7249 XtIntervalId analysisClockXID = 0;
7250
7251 void
7252 AnalysisClockCallback(arg, id)
7253      XtPointer arg;
7254      XtIntervalId *id;
7255 {
7256     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
7257         AnalysisPeriodicEvent(0);
7258         StartAnalysisClock();
7259     }
7260 }
7261
7262 void
7263 StartAnalysisClock()
7264 {
7265     analysisClockXID =
7266       XtAppAddTimeOut(appContext, 2000,
7267                       (XtTimerCallbackProc) AnalysisClockCallback,
7268                       (XtPointer) 0);
7269 }
7270
7271 XtIntervalId clockTimerXID = 0;
7272
7273 int ClockTimerRunning()
7274 {
7275     return clockTimerXID != 0;
7276 }
7277
7278 int StopClockTimer()
7279 {
7280     if (clockTimerXID != 0) {
7281         XtRemoveTimeOut(clockTimerXID);
7282         clockTimerXID = 0;
7283         return TRUE;
7284     } else {
7285         return FALSE;
7286     }
7287 }
7288
7289 void
7290 ClockTimerCallback(arg, id)
7291      XtPointer arg;
7292      XtIntervalId *id;
7293 {
7294     clockTimerXID = 0;
7295     DecrementClocks();
7296 }
7297
7298 void
7299 StartClockTimer(millisec)
7300      long millisec;
7301 {
7302     clockTimerXID =
7303       XtAppAddTimeOut(appContext, millisec,
7304                       (XtTimerCallbackProc) ClockTimerCallback,
7305                       (XtPointer) 0);
7306 }
7307
7308 void
7309 DisplayTimerLabel(w, color, timer, highlight)
7310      Widget w;
7311      char *color;
7312      long timer;
7313      int highlight;
7314 {
7315     char buf[MSG_SIZ];
7316     Arg args[16];
7317     
7318     if (appData.clockMode) {
7319         sprintf(buf, "%s: %s", color, TimeString(timer));
7320         XtSetArg(args[0], XtNlabel, buf);
7321     } else {
7322         sprintf(buf, "%s  ", color);
7323         XtSetArg(args[0], XtNlabel, buf);
7324     }
7325     
7326     if (highlight) {
7327         XtSetArg(args[1], XtNbackground, timerForegroundPixel);
7328         XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
7329     } else {
7330         XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
7331         XtSetArg(args[2], XtNforeground, timerForegroundPixel);
7332     }
7333     
7334     XtSetValues(w, args, 3);
7335 }
7336
7337 void
7338 DisplayWhiteClock(timeRemaining, highlight)
7339      long timeRemaining;
7340      int highlight;
7341 {
7342     Arg args[16];
7343     DisplayTimerLabel(whiteTimerWidget, "White", timeRemaining, highlight);
7344     if (highlight && iconPixmap == bIconPixmap) {
7345         iconPixmap = wIconPixmap;
7346         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7347         XtSetValues(shellWidget, args, 1);
7348     }
7349 }
7350
7351 void
7352 DisplayBlackClock(timeRemaining, highlight)
7353      long timeRemaining;
7354      int highlight;
7355 {
7356     Arg args[16];
7357     DisplayTimerLabel(blackTimerWidget, "Black", timeRemaining, highlight);
7358     if (highlight && iconPixmap == wIconPixmap) {
7359         iconPixmap = bIconPixmap;
7360         XtSetArg(args[0], XtNiconPixmap, iconPixmap);
7361         XtSetValues(shellWidget, args, 1);
7362     }
7363 }
7364
7365 #define CPNone 0
7366 #define CPReal 1
7367 #define CPComm 2
7368 #define CPSock 3
7369 #define CPLoop 4
7370 typedef int CPKind;
7371
7372 typedef struct {
7373     CPKind kind;
7374     int pid;
7375     int fdTo, fdFrom;  
7376 } ChildProc;
7377
7378
7379 int StartChildProcess(cmdLine, dir, pr)
7380      char *cmdLine; 
7381      char *dir;
7382      ProcRef *pr;
7383 {
7384     char *argv[64], *p;
7385     int i, pid;
7386     int to_prog[2], from_prog[2];
7387     ChildProc *cp;
7388     char buf[MSG_SIZ];
7389     
7390     if (appData.debugMode) {
7391         fprintf(stderr, "StartChildProcess (dir=\"%s\") %s\n",dir, cmdLine);
7392     }
7393
7394     /* We do NOT feed the cmdLine to the shell; we just
7395        parse it into blank-separated arguments in the
7396        most simple-minded way possible.
7397        */
7398     i = 0;
7399     strcpy(buf, cmdLine);
7400     p = buf;
7401     for (;;) {
7402         argv[i++] = p;
7403         p = strchr(p, ' ');
7404         if (p == NULL) break;
7405         *p++ = NULLCHAR;
7406     }
7407     argv[i] = NULL;
7408
7409     SetUpChildIO(to_prog, from_prog);
7410
7411     if ((pid = fork()) == 0) {
7412         /* Child process */
7413         dup2(to_prog[0], 0);
7414         dup2(from_prog[1], 1);
7415         close(to_prog[0]);
7416         close(to_prog[1]);
7417         close(from_prog[0]);
7418         close(from_prog[1]);
7419         dup2(1, fileno(stderr)); /* force stderr to the pipe */
7420
7421         if (dir[0] != NULLCHAR && chdir(dir) != 0) {
7422             perror(dir);
7423             exit(1);
7424         }
7425
7426         execvp(argv[0], argv);
7427         
7428         /* If we get here, exec failed */
7429         perror(argv[0]);
7430         exit(1);
7431     }
7432     
7433     /* Parent process */
7434     close(to_prog[0]);
7435     close(from_prog[1]);
7436     
7437     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7438     cp->kind = CPReal;
7439     cp->pid = pid;
7440     cp->fdFrom = from_prog[0];
7441     cp->fdTo = to_prog[1];
7442     *pr = (ProcRef) cp;
7443     return 0;
7444 }
7445
7446 void
7447 DestroyChildProcess(pr, signal)
7448      ProcRef pr;
7449      int signal;
7450 {
7451     ChildProc *cp = (ChildProc *) pr;
7452
7453     if (cp->kind != CPReal) return;
7454     cp->kind = CPNone;
7455     if (signal) {
7456       kill(cp->pid, SIGTERM);
7457     }
7458     /* Process is exiting either because of the kill or because of
7459        a quit command sent by the backend; either way, wait for it to die.
7460     */
7461     wait((int *) 0);
7462     close(cp->fdFrom);
7463     close(cp->fdTo);
7464 }
7465
7466 void
7467 InterruptChildProcess(pr)
7468      ProcRef pr;
7469 {
7470     ChildProc *cp = (ChildProc *) pr;
7471
7472     if (cp->kind != CPReal) return;
7473     (void) kill(cp->pid, SIGINT); /* stop it thinking */
7474 }
7475
7476 int OpenTelnet(host, port, pr)
7477      char *host;
7478      char *port;
7479      ProcRef *pr;
7480 {
7481     char cmdLine[MSG_SIZ];
7482
7483     if (port[0] == NULLCHAR) {
7484         sprintf(cmdLine, "%s %s", appData.telnetProgram, host);
7485     } else {
7486         sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);
7487     }
7488     return StartChildProcess(cmdLine, "", pr);
7489 }
7490
7491 int OpenTCP(host, port, pr)
7492      char *host;
7493      char *port;
7494      ProcRef *pr;
7495 {
7496 #if OMIT_SOCKETS
7497     DisplayFatalError("Socket support is not configured in", 0, 2);
7498 #else  /* !OMIT_SOCKETS */
7499     int s;
7500     struct sockaddr_in sa;
7501     struct hostent     *hp;
7502     unsigned short uport;
7503     ChildProc *cp;
7504
7505     if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
7506         return errno;
7507     }
7508
7509     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7510     sa.sin_family = AF_INET;
7511     sa.sin_addr.s_addr = INADDR_ANY;
7512     uport = (unsigned short) 0;
7513     sa.sin_port = htons(uport);
7514     if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
7515         return errno;
7516     }
7517
7518     memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
7519     if (!(hp = gethostbyname(host))) {
7520         int b0, b1, b2, b3;
7521         if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
7522             hp = (struct hostent *) calloc(1, sizeof(struct hostent));
7523             hp->h_addrtype = AF_INET;
7524             hp->h_length = 4;
7525             hp->h_addr_list = (char **) calloc(2, sizeof(char *));
7526             hp->h_addr_list[0] = (char *) malloc(4);
7527             hp->h_addr_list[0][0] = b0;
7528             hp->h_addr_list[0][1] = b1;
7529             hp->h_addr_list[0][2] = b2;
7530             hp->h_addr_list[0][3] = b3;
7531         } else {
7532             return ENOENT;
7533         }
7534     }
7535     sa.sin_family = hp->h_addrtype;
7536     uport = (unsigned short) atoi(port);
7537     sa.sin_port = htons(uport);
7538     memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
7539
7540     if (connect(s, (struct sockaddr *) &sa, 
7541                 sizeof(struct sockaddr_in)) < 0) {
7542         return errno;
7543     }
7544
7545     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7546     cp->kind = CPSock;
7547     cp->pid = 0;
7548     cp->fdFrom = s;
7549     cp->fdTo = s;
7550     *pr = (ProcRef) cp;
7551
7552 #endif /* !OMIT_SOCKETS */
7553
7554     return 0;
7555 }
7556
7557 int OpenCommPort(name, pr)
7558      char *name;
7559      ProcRef *pr;
7560 {
7561     int fd;
7562     ChildProc *cp;
7563
7564     fd = open(name, 2, 0);
7565     if (fd < 0) return errno;
7566
7567     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7568     cp->kind = CPComm;
7569     cp->pid = 0;
7570     cp->fdFrom = fd;
7571     cp->fdTo = fd;
7572     *pr = (ProcRef) cp;
7573
7574     return 0;
7575 }
7576
7577 int OpenLoopback(pr)
7578      ProcRef *pr;
7579 {
7580     ChildProc *cp;
7581     int to[2], from[2];
7582
7583     SetUpChildIO(to, from);
7584
7585     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
7586     cp->kind = CPLoop;
7587     cp->pid = 0;
7588     cp->fdFrom = to[0];         /* note not from[0]; we are doing a loopback */
7589     cp->fdTo = to[1];
7590     *pr = (ProcRef) cp;
7591
7592     return 0;
7593 }
7594
7595 int OpenRcmd(host, user, cmd, pr)
7596      char *host, *user, *cmd;
7597      ProcRef *pr;
7598 {
7599     DisplayFatalError("internal rcmd not implemented for Unix", 0, 1);
7600     return -1;
7601 }    
7602
7603 #define INPUT_SOURCE_BUF_SIZE 8192
7604
7605 typedef struct {
7606     CPKind kind;
7607     int fd;
7608     int lineByLine;
7609     char *unused;
7610     InputCallback func;
7611     XtInputId xid;
7612     char buf[INPUT_SOURCE_BUF_SIZE];
7613     VOIDSTAR closure;
7614 } InputSource;
7615
7616 void
7617 DoInputCallback(closure, source, xid) 
7618      caddr_t closure;
7619      int *source;
7620      XtInputId *xid;
7621 {
7622     InputSource *is = (InputSource *) closure;
7623     int count;
7624     int error;
7625     char *p, *q;
7626
7627     if (is->lineByLine) {
7628         count = read(is->fd, is->unused,
7629                      INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
7630         if (count <= 0) {
7631             (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
7632             return;
7633         }
7634         is->unused += count;
7635         p = is->buf;
7636         while (p < is->unused) {
7637             q = memchr(p, '\n', is->unused - p);
7638             if (q == NULL) break;
7639             q++;
7640             (is->func)(is, is->closure, p, q - p, 0);
7641             p = q;
7642         }
7643         q = is->buf;
7644         while (p < is->unused) {
7645             *q++ = *p++;
7646         }
7647         is->unused = q;
7648     } else {
7649         count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
7650         if (count == -1)
7651           error = errno;
7652         else
7653           error = 0;
7654         (is->func)(is, is->closure, is->buf, count, error);
7655     }   
7656 }
7657
7658 InputSourceRef AddInputSource(pr, lineByLine, func, closure)
7659      ProcRef pr;
7660      int lineByLine;
7661      InputCallback func;
7662      VOIDSTAR closure;
7663 {
7664     InputSource *is;
7665     ChildProc *cp = (ChildProc *) pr;
7666
7667     is = (InputSource *) calloc(1, sizeof(InputSource));
7668     is->lineByLine = lineByLine;
7669     is->func = func;
7670     if (pr == NoProc) {
7671         is->kind = CPReal;
7672         is->fd = fileno(stdin);
7673     } else {
7674         is->kind = cp->kind;
7675         is->fd = cp->fdFrom;
7676     }
7677     if (lineByLine) {
7678         is->unused = is->buf;
7679     }
7680     
7681     is->xid = XtAppAddInput(appContext, is->fd,
7682                             (XtPointer) (XtInputReadMask),
7683                             (XtInputCallbackProc) DoInputCallback,
7684                             (XtPointer) is);
7685     is->closure = closure;
7686     return (InputSourceRef) is;
7687 }
7688
7689 void
7690 RemoveInputSource(isr)
7691      InputSourceRef isr;
7692 {
7693     InputSource *is = (InputSource *) isr;
7694
7695     if (is->xid == 0) return;
7696     XtRemoveInput(is->xid);
7697     is->xid = 0;
7698 }
7699
7700 int OutputToProcess(pr, message, count, outError)
7701      ProcRef pr;
7702      char *message;
7703      int count;
7704      int *outError;
7705 {
7706     ChildProc *cp = (ChildProc *) pr;
7707     int outCount;
7708
7709     if (pr == NoProc)
7710       outCount = fwrite(message, 1, count, stdout);
7711     else
7712       outCount = write(cp->fdTo, message, count);
7713
7714     if (outCount == -1)
7715       *outError = errno;
7716     else
7717       *outError = 0;
7718
7719     return outCount;
7720 }
7721
7722 /* Output message to process, with "ms" milliseconds of delay
7723    between each character. This is needed when sending the logon
7724    script to ICC, which for some reason doesn't like the
7725    instantaneous send. */
7726 int OutputToProcessDelayed(pr, message, count, outError, msdelay)
7727      ProcRef pr;
7728      char *message;
7729      int count;
7730      int *outError;
7731      long msdelay;
7732 {
7733     ChildProc *cp = (ChildProc *) pr;
7734     int outCount = 0;
7735     int r;
7736     
7737     while (count--) {
7738         r = write(cp->fdTo, message++, 1);
7739         if (r == -1) {
7740             *outError = errno;
7741             return outCount;
7742         }
7743         ++outCount;
7744         if (msdelay >= 0)
7745           TimeDelay(msdelay);
7746     }
7747
7748     return outCount;
7749 }
7750
7751 /****   Animation code by Hugh Fisher, DCS, ANU.
7752
7753         Known problem: if a window overlapping the board is
7754         moved away while a piece is being animated underneath,
7755         the newly exposed area won't be updated properly.
7756         I can live with this.
7757
7758         Known problem: if you look carefully at the animation
7759         of pieces in mono mode, they are being drawn as solid
7760         shapes without interior detail while moving. Fixing
7761         this would be a major complication for minimal return.
7762 ****/
7763
7764 /*      Masks for XPM pieces. Black and white pieces can have
7765         different shapes, but in the interest of retaining my
7766         sanity pieces must have the same outline on both light
7767         and dark squares, and all pieces must use the same
7768         background square colors/images.                */
7769
7770 static void
7771 CreateAnimMasks (pieceDepth)
7772      int pieceDepth;
7773 {
7774   ChessSquare   piece;
7775   Pixmap        buf;
7776   GC            bufGC, maskGC;
7777   int           kind, n;
7778   unsigned long plane;
7779   XGCValues     values;
7780
7781   /* Need a bitmap just to get a GC with right depth */
7782   buf = XCreatePixmap(xDisplay, xBoardWindow,
7783                         8, 8, 1);
7784   values.foreground = 1;
7785   values.background = 0;
7786   /* Don't use XtGetGC, not read only */
7787   maskGC = XCreateGC(xDisplay, buf,
7788                     GCForeground | GCBackground, &values);
7789   XFreePixmap(xDisplay, buf);
7790
7791   buf = XCreatePixmap(xDisplay, xBoardWindow,
7792                       squareSize, squareSize, pieceDepth);            
7793   values.foreground = XBlackPixel(xDisplay, xScreen);
7794   values.background = XWhitePixel(xDisplay, xScreen);
7795   bufGC = XCreateGC(xDisplay, buf,
7796                     GCForeground | GCBackground, &values);
7797
7798   for (piece = WhitePawn; piece <= BlackKing; piece++) {
7799     /* Begin with empty mask */
7800     xpmMask[piece] = XCreatePixmap(xDisplay, xBoardWindow,
7801                                  squareSize, squareSize, 1);
7802     XSetFunction(xDisplay, maskGC, GXclear);
7803     XFillRectangle(xDisplay, xpmMask[piece], maskGC,
7804                    0, 0, squareSize, squareSize);
7805                    
7806     /* Take a copy of the piece */
7807     if (White(piece))
7808       kind = 0;
7809     else
7810       kind = 2;
7811     XSetFunction(xDisplay, bufGC, GXcopy);
7812     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % 6],
7813               buf, bufGC,
7814               0, 0, squareSize, squareSize, 0, 0);
7815               
7816     /* XOR the background (light) over the piece */
7817     XSetFunction(xDisplay, bufGC, GXxor);
7818     if (useImageSqs)
7819       XCopyArea(xDisplay, xpmLightSquare, buf, bufGC,
7820                 0, 0, squareSize, squareSize, 0, 0);
7821     else {
7822       XSetForeground(xDisplay, bufGC, lightSquareColor);
7823       XFillRectangle(xDisplay, buf, bufGC, 0, 0, squareSize, squareSize);
7824     }
7825     
7826     /* We now have an inverted piece image with the background
7827        erased. Construct mask by just selecting all the non-zero
7828        pixels - no need to reconstruct the original image.      */
7829     XSetFunction(xDisplay, maskGC, GXor);
7830     plane = 1;
7831     /* Might be quicker to download an XImage and create bitmap
7832        data from it rather than this N copies per piece, but it
7833        only takes a fraction of a second and there is a much
7834        longer delay for loading the pieces.             */
7835     for (n = 0; n < pieceDepth; n ++) {
7836       XCopyPlane(xDisplay, buf, xpmMask[piece], maskGC,
7837                  0, 0, squareSize, squareSize,
7838                  0, 0, plane);
7839       plane = plane << 1;
7840     }
7841   }
7842   /* Clean up */
7843   XFreePixmap(xDisplay, buf);
7844   XFreeGC(xDisplay, bufGC);
7845   XFreeGC(xDisplay, maskGC);
7846 }
7847
7848 static void
7849 InitAnimState (anim, info)
7850   AnimState * anim;
7851   XWindowAttributes * info;
7852 {
7853   XtGCMask  mask;
7854   XGCValues values;
7855   
7856   /* Each buffer is square size, same depth as window */
7857   anim->saveBuf = XCreatePixmap(xDisplay, xBoardWindow,
7858                         squareSize, squareSize, info->depth);
7859   anim->newBuf = XCreatePixmap(xDisplay, xBoardWindow,
7860                         squareSize, squareSize, info->depth);
7861
7862   /* Create a plain GC for blitting */
7863   mask = GCForeground | GCBackground | GCFunction |
7864          GCPlaneMask | GCGraphicsExposures;
7865   values.foreground = XBlackPixel(xDisplay, xScreen);
7866   values.background = XWhitePixel(xDisplay, xScreen);
7867   values.function   = GXcopy;
7868   values.plane_mask = AllPlanes;
7869   values.graphics_exposures = False;
7870   anim->blitGC = XCreateGC(xDisplay, xBoardWindow, mask, &values);
7871
7872   /* Piece will be copied from an existing context at
7873      the start of each new animation/drag. */
7874   anim->pieceGC = XCreateGC(xDisplay, xBoardWindow, 0, &values);
7875
7876   /* Outline will be a read-only copy of an existing */
7877   anim->outlineGC = None;
7878 }
7879
7880 static void
7881 CreateAnimVars ()
7882 {
7883   static int done = 0;
7884   XWindowAttributes info;
7885
7886   if (done) return;
7887   done = 1;
7888   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
7889   
7890   InitAnimState(&game, &info);
7891   InitAnimState(&player, &info);
7892   
7893   /* For XPM pieces, we need bitmaps to use as masks. */
7894   if (useImages)
7895     CreateAnimMasks(info.depth);
7896 }
7897
7898 #ifndef HAVE_USLEEP
7899
7900 static Boolean frameWaiting;
7901
7902 static RETSIGTYPE FrameAlarm (sig)
7903      int sig;
7904 {
7905   frameWaiting = False;
7906   /* In case System-V style signals.  Needed?? */
7907   signal(SIGALRM, FrameAlarm);
7908 }
7909
7910 static void
7911 FrameDelay (time)
7912      int time;
7913 {
7914   struct itimerval delay;
7915   
7916   XSync(xDisplay, False);
7917
7918   if (time > 0) {
7919     frameWaiting = True;
7920     signal(SIGALRM, FrameAlarm);
7921     delay.it_interval.tv_sec = 
7922       delay.it_value.tv_sec = time / 1000;
7923     delay.it_interval.tv_usec = 
7924       delay.it_value.tv_usec = (time % 1000) * 1000;
7925     setitimer(ITIMER_REAL, &delay, NULL);
7926 #if 0
7927     /* Ugh -- busy-wait! --tpm */
7928     while (frameWaiting); 
7929 #else
7930     while (frameWaiting) pause();
7931 #endif
7932     delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
7933     delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
7934     setitimer(ITIMER_REAL, &delay, NULL);
7935   }
7936 }
7937
7938 #else
7939
7940 static void
7941 FrameDelay (time)
7942      int time;
7943 {
7944   XSync(xDisplay, False);
7945   if (time > 0)
7946     usleep(time * 1000);
7947 }
7948
7949 #endif
7950
7951 /*      Convert board position to corner of screen rect and color       */
7952
7953 static void
7954 ScreenSquare(column, row, pt, color)
7955      int column; int row; XPoint * pt; int * color;
7956 {
7957   if (flipView) {
7958     pt->x = lineGap + ((BOARD_SIZE-1)-column) * (squareSize + lineGap);
7959     pt->y = lineGap + row * (squareSize + lineGap);
7960   } else {
7961     pt->x = lineGap + column * (squareSize + lineGap);
7962     pt->y = lineGap + ((BOARD_SIZE-1)-row) * (squareSize + lineGap);
7963   }
7964   *color = ((column + row) % 2) == 1;
7965 }
7966
7967 /*      Convert window coords to square                 */
7968
7969 static void
7970 BoardSquare(x, y, column, row)
7971      int x; int y; int * column; int * row;
7972 {
7973   *column = EventToSquare(x, BOARD_SIZE);
7974   if (flipView && *column >= 0)
7975     *column = BOARD_SIZE - 1 - *column;
7976   *row = EventToSquare(y, BOARD_SIZE);
7977   if (!flipView && *row >= 0)
7978     *row = BOARD_SIZE - 1 - *row;
7979 }
7980
7981 /*   Utilities  */
7982
7983 #undef Max  /* just in case */
7984 #undef Min
7985 #define Max(a, b) ((a) > (b) ? (a) : (b))
7986 #define Min(a, b) ((a) < (b) ? (a) : (b))
7987
7988 static void
7989 SetRect(rect, x, y, width, height)
7990      XRectangle * rect; int x; int y; int width; int height;
7991 {
7992   rect->x = x;
7993   rect->y = y;
7994   rect->width  = width;
7995   rect->height = height;
7996 }
7997
7998 /*      Test if two frames overlap. If they do, return
7999         intersection rect within old and location of
8000         that rect within new. */
8001         
8002 static Boolean
8003 Intersect(old, new, size, area, pt)
8004      XPoint * old; XPoint * new;
8005      int size; XRectangle * area; XPoint * pt;
8006 {
8007   if (old->x > new->x + size || new->x > old->x + size ||
8008       old->y > new->y + size || new->y > old->y + size) {
8009     return False;
8010   } else {
8011     SetRect(area, Max(new->x - old->x, 0), Max(new->y - old->y, 0),
8012             size - abs(old->x - new->x), size - abs(old->y - new->y));
8013     pt->x = Max(old->x - new->x, 0);
8014     pt->y = Max(old->y - new->y, 0);
8015     return True;
8016   }
8017 }
8018
8019 /*      For two overlapping frames, return the rect(s)
8020         in the old that do not intersect with the new.   */
8021         
8022 static void
8023 CalcUpdateRects(old, new, size, update, nUpdates)
8024      XPoint * old; XPoint * new; int size;
8025      XRectangle update[]; int * nUpdates;
8026 {
8027   int        count;
8028
8029   /* If old = new (shouldn't happen) then nothing to draw */
8030   if (old->x == new->x && old->y == new->y) {
8031     *nUpdates = 0;
8032     return;
8033   }
8034   /* Work out what bits overlap. Since we know the rects
8035      are the same size we don't need a full intersect calc. */
8036   count = 0;
8037   /* Top or bottom edge? */
8038   if (new->y > old->y) {
8039     SetRect(&(update[count]), old->x, old->y, size, new->y - old->y);
8040     count ++;
8041   } else if (old->y > new->y) {
8042     SetRect(&(update[count]), old->x, old->y + size - (old->y - new->y),
8043                               size, old->y - new->y);
8044     count ++;
8045   }
8046   /* Left or right edge - don't overlap any update calculated above. */
8047   if (new->x > old->x) {
8048     SetRect(&(update[count]), old->x, Max(new->y, old->y),
8049                               new->x - old->x, size - abs(new->y - old->y));
8050     count ++;
8051   } else if (old->x > new->x) {
8052     SetRect(&(update[count]), new->x + size, Max(new->y, old->y),
8053                               old->x - new->x, size - abs(new->y - old->y));
8054     count ++;
8055   }
8056   /* Done */
8057   *nUpdates = count;
8058 }
8059
8060 /*      Generate a series of frame coords from start->mid->finish.
8061         The movement rate doubles until the half way point is
8062         reached, then halves back down to the final destination,
8063         which gives a nice slow in/out effect. The algorithmn
8064         may seem to generate too many intermediates for short
8065         moves, but remember that the purpose is to attract the
8066         viewers attention to the piece about to be moved and
8067         then to where it ends up. Too few frames would be less
8068         noticeable.                                             */
8069
8070 static void
8071 Tween(start, mid, finish, factor, frames, nFrames)
8072      XPoint * start; XPoint * mid;
8073      XPoint * finish; int factor;
8074      XPoint frames[]; int * nFrames;
8075 {
8076   int fraction, n, count;
8077
8078   count = 0;
8079
8080   /* Slow in, stepping 1/16th, then 1/8th, ... */
8081   fraction = 1;
8082   for (n = 0; n < factor; n++)
8083     fraction *= 2;
8084   for (n = 0; n < factor; n++) {
8085     frames[count].x = start->x + (mid->x - start->x) / fraction;
8086     frames[count].y = start->y + (mid->y - start->y) / fraction;
8087     count ++;
8088     fraction = fraction / 2;
8089   }
8090   
8091   /* Midpoint */
8092   frames[count] = *mid;
8093   count ++;
8094   
8095   /* Slow out, stepping 1/2, then 1/4, ... */
8096   fraction = 2;
8097   for (n = 0; n < factor; n++) {
8098     frames[count].x = finish->x - (finish->x - mid->x) / fraction;
8099     frames[count].y = finish->y - (finish->y - mid->y) / fraction;
8100     count ++;
8101     fraction = fraction * 2;
8102   }
8103   *nFrames = count;
8104 }
8105
8106 /*      Draw a piece on the screen without disturbing what's there      */
8107
8108 static void
8109 SelectGCMask(piece, clip, outline, mask)
8110      ChessSquare piece; GC * clip; GC * outline; Pixmap * mask;
8111 {
8112   GC source;
8113
8114   /* Bitmap for piece being moved. */
8115   if (appData.monoMode) {
8116       *mask = *pieceToSolid(piece);
8117   } else if (useImages) {
8118 #if HAVE_LIBXPM
8119       *mask = xpmMask[piece];
8120 #else
8121       *mask = ximMaskPm[piece%6];
8122 #endif
8123   } else {
8124       *mask = *pieceToSolid(piece);
8125   }
8126
8127   /* GC for piece being moved. Square color doesn't matter, but
8128      since it gets modified we make a copy of the original. */
8129   if (White(piece)) {
8130     if (appData.monoMode)
8131       source = bwPieceGC;
8132     else
8133       source = wlPieceGC;
8134   } else {
8135     if (appData.monoMode)
8136       source = wbPieceGC;
8137     else
8138       source = blPieceGC;
8139   }
8140   XCopyGC(xDisplay, source, 0xFFFFFFFF, *clip);
8141   
8142   /* Outline only used in mono mode and is not modified */
8143   if (White(piece))
8144     *outline = bwPieceGC;
8145   else
8146     *outline = wbPieceGC;
8147 }
8148
8149 static void
8150 OverlayPiece(piece, clip, outline,  dest)
8151      ChessSquare piece; GC clip; GC outline; Drawable dest;
8152 {
8153   int   kind;
8154   
8155   if (!useImages) {
8156     /* Draw solid rectangle which will be clipped to shape of piece */
8157     XFillRectangle(xDisplay, dest, clip,
8158                    0, 0, squareSize, squareSize);
8159     if (appData.monoMode)
8160       /* Also draw outline in contrasting color for black
8161          on black / white on white cases                */
8162       XCopyPlane(xDisplay, *pieceToOutline(piece), dest, outline,
8163                  0, 0, squareSize, squareSize, 0, 0, 1);
8164   } else {
8165     /* Copy the piece */
8166     if (White(piece))
8167       kind = 0;
8168     else
8169       kind = 2;
8170     XCopyArea(xDisplay, xpmPieceBitmap[kind][((int)piece) % 6],
8171               dest, clip,
8172               0, 0, squareSize, squareSize,
8173               0, 0);            
8174   }
8175 }
8176
8177 /* Animate the movement of a single piece */
8178
8179 static void 
8180 BeginAnimation(anim, piece, startColor, start)
8181      AnimState *anim;
8182      ChessSquare piece;
8183      int startColor;
8184      XPoint * start;
8185 {
8186   Pixmap mask;
8187   
8188   /* The old buffer is initialised with the start square (empty) */
8189   BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
8190   anim->prevFrame = *start;
8191   
8192   /* The piece will be drawn using its own bitmap as a matte    */
8193   SelectGCMask(piece, &anim->pieceGC, &anim->outlineGC, &mask);
8194   XSetClipMask(xDisplay, anim->pieceGC, mask);
8195 }
8196
8197 static void
8198 AnimationFrame(anim, frame, piece)
8199      AnimState *anim;
8200      XPoint *frame;
8201      ChessSquare piece;
8202 {
8203   XRectangle updates[4];
8204   XRectangle overlap;
8205   XPoint     pt;
8206   int        count, i;
8207   
8208   /* Save what we are about to draw into the new buffer */
8209   XCopyArea(xDisplay, xBoardWindow, anim->newBuf, anim->blitGC,
8210             frame->x, frame->y, squareSize, squareSize,
8211             0, 0);
8212                 
8213   /* Erase bits of the previous frame */
8214   if (Intersect(&anim->prevFrame, frame, squareSize, &overlap, &pt)) {
8215     /* Where the new frame overlapped the previous,
8216        the contents in newBuf are wrong. */
8217     XCopyArea(xDisplay, anim->saveBuf, anim->newBuf, anim->blitGC,
8218               overlap.x, overlap.y,
8219               overlap.width, overlap.height,
8220               pt.x, pt.y);
8221     /* Repaint the areas in the old that don't overlap new */
8222     CalcUpdateRects(&anim->prevFrame, frame, squareSize, updates, &count);
8223     for (i = 0; i < count; i++)
8224       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8225                 updates[i].x - anim->prevFrame.x,
8226                 updates[i].y - anim->prevFrame.y,
8227                 updates[i].width, updates[i].height,
8228                 updates[i].x, updates[i].y);
8229   } else {
8230     /* Easy when no overlap */
8231     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8232                   0, 0, squareSize, squareSize,
8233                   anim->prevFrame.x, anim->prevFrame.y);
8234   }
8235
8236   /* Save this frame for next time round */
8237   XCopyArea(xDisplay, anim->newBuf, anim->saveBuf, anim->blitGC,
8238                 0, 0, squareSize, squareSize,
8239                 0, 0);
8240   anim->prevFrame = *frame;
8241   
8242   /* Draw piece over original screen contents, not current,
8243      and copy entire rect. Wipes out overlapping piece images. */
8244   OverlayPiece(piece, anim->pieceGC, anim->outlineGC, anim->newBuf);
8245   XCopyArea(xDisplay, anim->newBuf, xBoardWindow, anim->blitGC,
8246                 0, 0, squareSize, squareSize,
8247                 frame->x, frame->y);
8248 }
8249
8250 static void
8251 EndAnimation (anim, finish)
8252      AnimState *anim;
8253      XPoint *finish;
8254 {
8255   XRectangle updates[4];
8256   XRectangle overlap;
8257   XPoint     pt;
8258   int        count, i;
8259
8260   /* The main code will redraw the final square, so we
8261      only need to erase the bits that don't overlap.    */
8262   if (Intersect(&anim->prevFrame, finish, squareSize, &overlap, &pt)) {
8263     CalcUpdateRects(&anim->prevFrame, finish, squareSize, updates, &count);
8264     for (i = 0; i < count; i++)
8265       XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8266                 updates[i].x - anim->prevFrame.x,
8267                 updates[i].y - anim->prevFrame.y,
8268                 updates[i].width, updates[i].height,
8269                 updates[i].x, updates[i].y);
8270   } else {
8271     XCopyArea(xDisplay, anim->saveBuf, xBoardWindow, anim->blitGC,
8272                 0, 0, squareSize, squareSize,
8273                 anim->prevFrame.x, anim->prevFrame.y);
8274   }
8275 }
8276
8277 static void
8278 FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
8279      AnimState *anim;
8280      ChessSquare piece; int startColor;
8281      XPoint * start; XPoint * finish;
8282      XPoint frames[]; int nFrames;
8283 {
8284   int n;
8285
8286   BeginAnimation(anim, piece, startColor, start);
8287   for (n = 0; n < nFrames; n++) {
8288     AnimationFrame(anim, &(frames[n]), piece);
8289     FrameDelay(appData.animSpeed);
8290   }
8291   EndAnimation(anim, finish);
8292 }
8293
8294 /* Main control logic for deciding what to animate and how */
8295
8296 void
8297 AnimateMove(board, fromX, fromY, toX, toY)
8298      Board board;
8299      int fromX;
8300      int fromY;
8301      int toX;
8302      int toY;
8303 {
8304   ChessSquare piece;
8305   int hop;
8306   XPoint      start, finish, mid;
8307   XPoint      frames[kFactor * 2 + 1];
8308   int         nFrames, startColor, endColor;
8309
8310   /* Are we animating? */
8311   if (!appData.animate || appData.blindfold)
8312     return;
8313
8314   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
8315   piece = board[fromY][fromX];
8316   if (piece >= EmptySquare) return;
8317
8318 #if DONT_HOP
8319   hop = FALSE;
8320 #else
8321   hop = (piece == WhiteKnight || piece == BlackKnight);
8322 #endif
8323
8324   if (appData.debugMode) {
8325       printf("AnimateMove: piece %d %s from %d,%d to %d,%d \n",
8326              piece, hop ? "hops" : "slides", fromX, fromY, toX, toY);
8327   }
8328
8329   ScreenSquare(fromX, fromY, &start, &startColor);
8330   ScreenSquare(toX, toY, &finish, &endColor);
8331
8332   if (hop) {
8333     /* Knight: make diagonal movement then straight */
8334     if (abs(toY - fromY) < abs(toX - fromX)) {
8335        mid.x = start.x + (finish.x - start.x) / 2;
8336        mid.y = finish.y;
8337      } else {
8338        mid.x = finish.x;
8339        mid.y = start.y + (finish.y - start.y) / 2;
8340      }
8341   } else {
8342     mid.x = start.x + (finish.x - start.x) / 2;
8343     mid.y = start.y + (finish.y - start.y) / 2;
8344   }
8345   
8346   /* Don't use as many frames for very short moves */
8347   if (abs(toY - fromY) + abs(toX - fromX) <= 2)
8348     Tween(&start, &mid, &finish, kFactor - 1, frames, &nFrames);
8349   else
8350     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
8351   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
8352   
8353   /* Be sure end square is redrawn */
8354   damage[toY][toX] = True;
8355 }
8356
8357 static void
8358 DragPieceBegin(x, y)
8359      int x; int y;
8360 {
8361     int  boardX, boardY, color;
8362     XPoint corner;
8363
8364     /* Are we animating? */
8365     if (!appData.animateDragging || appData.blindfold)
8366       return;
8367      
8368     /* Figure out which square we start in and the
8369        mouse position relative to top left corner. */
8370     BoardSquare(x, y, &boardX, &boardY);
8371     player.startBoardX = boardX;
8372     player.startBoardY = boardY;
8373     ScreenSquare(boardX, boardY, &corner, &color);
8374     player.startSquare  = corner;
8375     player.startColor   = color;
8376 #if 0
8377     /* Start from exactly where the piece is.  This can be confusing
8378        if you start dragging far from the center of the square; most
8379        or all of the piece can be over a different square from the one
8380        the mouse pointer is in. */
8381     player.mouseDelta.x = x - corner.x;
8382     player.mouseDelta.y = y - corner.y;
8383 #else
8384     /* As soon as we start dragging, the piece will jump slightly to
8385        be centered over the mouse pointer. */
8386     player.mouseDelta.x = squareSize/2;
8387     player.mouseDelta.y = squareSize/2;
8388 #endif
8389     /* Initialise animation */
8390     player.dragPiece = PieceForSquare(boardX, boardY);
8391     /* Sanity check */
8392     if (player.dragPiece >= 0 && player.dragPiece < EmptySquare) {
8393         player.dragActive = True;
8394         BeginAnimation(&player, player.dragPiece, color, &corner);
8395         /* Mark this square as needing to be redrawn. Note that
8396            we don't remove the piece though, since logically (ie
8397            as seen by opponent) the move hasn't been made yet. */
8398         damage[boardY][boardX] = True;
8399     } else {
8400         player.dragActive = False;
8401     }
8402 }
8403
8404 static void
8405 DragPieceMove(x, y)
8406      int x; int y;
8407 {
8408     XPoint corner;
8409
8410     /* Are we animating? */
8411     if (!appData.animateDragging || appData.blindfold)
8412       return;
8413      
8414     /* Sanity check */
8415     if (! player.dragActive)
8416       return;
8417     /* Move piece, maintaining same relative position
8418        of mouse within square    */
8419     corner.x = x - player.mouseDelta.x;
8420     corner.y = y - player.mouseDelta.y;
8421     AnimationFrame(&player, &corner, player.dragPiece);
8422 #if HIGHDRAG
8423     if (appData.highlightDragging) {
8424         int boardX, boardY;
8425         BoardSquare(x, y, &boardX, &boardY);
8426         SetHighlights(fromX, fromY, boardX, boardY);
8427     }
8428 #endif
8429 }
8430
8431 static void
8432 DragPieceEnd(x, y)
8433      int x; int y;
8434 {
8435     int boardX, boardY, color;
8436     XPoint corner;
8437
8438     /* Are we animating? */
8439     if (!appData.animateDragging || appData.blindfold)
8440       return;
8441      
8442     /* Sanity check */
8443     if (! player.dragActive)
8444       return;
8445     /* Last frame in sequence is square piece is
8446        placed on, which may not match mouse exactly. */
8447     BoardSquare(x, y, &boardX, &boardY);
8448     ScreenSquare(boardX, boardY, &corner, &color);
8449     EndAnimation(&player, &corner);
8450
8451     /* Be sure end square is redrawn */
8452     damage[boardY][boardX] = True;
8453
8454     /* This prevents weird things happening with fast successive
8455        clicks which on my Sun at least can cause motion events
8456        without corresponding press/release. */
8457     player.dragActive = False;
8458 }
8459
8460 /* Handle expose event while piece being dragged */
8461
8462 static void
8463 DrawDragPiece ()
8464 {
8465   if (!player.dragActive || appData.blindfold)
8466     return;
8467
8468   /* What we're doing: logically, the move hasn't been made yet,
8469      so the piece is still in it's original square. But visually
8470      it's being dragged around the board. So we erase the square
8471      that the piece is on and draw it at the last known drag point. */
8472   BlankSquare(player.startSquare.x, player.startSquare.y,
8473                 player.startColor, EmptySquare, xBoardWindow);
8474   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
8475   damage[player.startBoardY][player.startBoardX] = TRUE;
8476 }
8477