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