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