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