f975e95c3fc472685866ed4c202e84ba061683f2
[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
145 # if HAVE_LIBREADLINE /* add gnu-readline support */
146 #include <readline/readline.h>
147 #include <readline/history.h>
148 # endif
149
150 #include <X11/Intrinsic.h>
151 #include <X11/StringDefs.h>
152 #include <X11/Shell.h>
153 #include <X11/cursorfont.h>
154 #include <X11/Xatom.h>
155 #include <X11/Xmu/Atoms.h>
156 #if USE_XAW3D
157 #include <X11/Xaw3d/Dialog.h>
158 #include <X11/Xaw3d/Form.h>
159 #include <X11/Xaw3d/List.h>
160 #include <X11/Xaw3d/Label.h>
161 #include <X11/Xaw3d/SimpleMenu.h>
162 #include <X11/Xaw3d/SmeBSB.h>
163 #include <X11/Xaw3d/SmeLine.h>
164 #include <X11/Xaw3d/Box.h>
165 #include <X11/Xaw3d/MenuButton.h>
166 #include <X11/Xaw3d/Text.h>
167 #include <X11/Xaw3d/AsciiText.h>
168 #else
169 #include <X11/Xaw/Dialog.h>
170 #include <X11/Xaw/Form.h>
171 #include <X11/Xaw/List.h>
172 #include <X11/Xaw/Label.h>
173 #include <X11/Xaw/SimpleMenu.h>
174 #include <X11/Xaw/SmeBSB.h>
175 #include <X11/Xaw/SmeLine.h>
176 #include <X11/Xaw/Box.h>
177 #include <X11/Xaw/MenuButton.h>
178 #include <X11/Xaw/Text.h>
179 #include <X11/Xaw/AsciiText.h>
180 #endif
181
182 // [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
183 #include "common.h"
184
185 #if HAVE_LIBXPM
186 #include <X11/xpm.h>
187 #include "pixmaps/pixmaps.h"
188 #define IMAGE_EXT "xpm"
189 #else
190 #define IMAGE_EXT "xim"
191 #include "bitmaps/bitmaps.h"
192 #endif
193
194 #include "bitmaps/icon_white.bm"
195 #include "bitmaps/icon_black.bm"
196 #include "bitmaps/checkmark.bm"
197
198 #include "frontend.h"
199 #include "backend.h"
200 #include "backendz.h"
201 #include "moves.h"
202 #include "xboard.h"
203 #include "childio.h"
204 #include "xgamelist.h"
205 #include "xhistory.h"
206 #include "xedittags.h"
207 #include "gettext.h"
208
209 // must be moved to xengineoutput.h
210
211 void EngineOutputProc P((Widget w, XEvent *event,
212                          String *prms, Cardinal *nprms));
213 void EvalGraphProc P((Widget w, XEvent *event,
214                       String *prms, Cardinal *nprms));
215
216
217 #ifdef __EMX__
218 #ifndef HAVE_USLEEP
219 #define HAVE_USLEEP
220 #endif
221 #define usleep(t)   _sleep2(((t)+500)/1000)
222 #endif
223
224 #ifdef ENABLE_NLS
225 # define  _(s) gettext (s)
226 # define N_(s) gettext_noop (s)
227 #else
228 # define  _(s) (s)
229 # define N_(s)  s
230 #endif
231
232 typedef struct {
233     String string;
234     XtActionProc proc;
235 } MenuItem;
236
237 typedef struct {
238     String name;
239     MenuItem *mi;
240 } Menu;
241
242 int main P((int argc, char **argv));
243 FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
244                 char *init_path, char *mode, int (*show_entry)(), char **name_return));
245 RETSIGTYPE CmailSigHandler P((int sig));
246 RETSIGTYPE IntSigHandler P((int sig));
247 RETSIGTYPE TermSizeSigHandler P((int sig));
248 void CreateGCs P((void));
249 void CreateXIMPieces P((void));
250 void CreateXPMPieces P((void));
251 void CreateXPMBoard P((char *s, int n));
252 void CreatePieces P((void));
253 void CreatePieceMenus P((void));
254 Widget CreateMenuBar P((Menu *mb));
255 Widget CreateButtonBar P ((MenuItem *mi));
256 char *FindFont P((char *pattern, int targetPxlSize));
257 void PieceMenuPopup P((Widget w, XEvent *event,
258                        String *params, Cardinal *num_params));
259 static void PieceMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
260 static void DropMenuSelect P((Widget w, ChessSquare piece, caddr_t junk));
261 void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
262                    u_int wreq, u_int hreq));
263 void CreateGrid P((void));
264 int EventToSquare P((int x, int limit));
265 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
266 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
267 void HandleUserMove P((Widget w, XEvent *event,
268                      String *prms, Cardinal *nprms));
269 void AnimateUserMove P((Widget w, XEvent * event,
270                      String * params, Cardinal * nParams));
271 void HandlePV P((Widget w, XEvent * event,
272                      String * params, Cardinal * nParams));
273 void SelectPV P((Widget w, XEvent * event,
274                      String * params, Cardinal * nParams));
275 void StopPV P((Widget w, XEvent * event,
276                      String * params, Cardinal * nParams));
277 void WhiteClock P((Widget w, XEvent *event,
278                    String *prms, Cardinal *nprms));
279 void BlackClock P((Widget w, XEvent *event,
280                    String *prms, Cardinal *nprms));
281 void DrawPositionProc P((Widget w, XEvent *event,
282                      String *prms, Cardinal *nprms));
283 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
284                      Board board));
285 void CommentClick P((Widget w, XEvent * event,
286                    String * params, Cardinal * nParams));
287 void CommentPopUp P((char *title, char *label));
288 void CommentPopDown P((void));
289 void CommentCallback P((Widget w, XtPointer client_data,
290                         XtPointer call_data));
291 void ICSInputBoxPopUp P((void));
292 void ICSInputBoxPopDown P((void));
293 void FileNamePopUp P((char *label, char *def,
294                       FileProc proc, char *openMode));
295 void FileNamePopDown P((void));
296 void FileNameCallback P((Widget w, XtPointer client_data,
297                          XtPointer call_data));
298 void FileNameAction P((Widget w, XEvent *event,
299                        String *prms, Cardinal *nprms));
300 void AskQuestionReplyAction P((Widget w, XEvent *event,
301                           String *prms, Cardinal *nprms));
302 void AskQuestionProc P((Widget w, XEvent *event,
303                           String *prms, Cardinal *nprms));
304 void AskQuestionPopDown P((void));
305 void PromotionPopDown P((void));
306 void PromotionCallback P((Widget w, XtPointer client_data,
307                           XtPointer call_data));
308 void EditCommentPopDown P((void));
309 void EditCommentCallback P((Widget w, XtPointer client_data,
310                             XtPointer call_data));
311 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
312 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
313 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
314 void LoadNextGameProc P((Widget w, XEvent *event, String *prms,
315                          Cardinal *nprms));
316 void LoadPrevGameProc P((Widget w, XEvent *event, String *prms,
317                          Cardinal *nprms));
318 void ReloadGameProc P((Widget w, XEvent *event, String *prms,
319                        Cardinal *nprms));
320 void LoadPositionProc P((Widget w, XEvent *event,
321                          String *prms, Cardinal *nprms));
322 void LoadNextPositionProc P((Widget w, XEvent *event, String *prms,
323                          Cardinal *nprms));
324 void LoadPrevPositionProc P((Widget w, XEvent *event, String *prms,
325                          Cardinal *nprms));
326 void ReloadPositionProc P((Widget w, XEvent *event, String *prms,
327                        Cardinal *nprms));
328 void CopyPositionProc P((Widget w, XEvent *event, String *prms,
329                          Cardinal *nprms));
330 void PastePositionProc P((Widget w, XEvent *event, String *prms,
331                           Cardinal *nprms));
332 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
333 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
334 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
335 void SavePositionProc P((Widget w, XEvent *event,
336                          String *prms, Cardinal *nprms));
337 void MailMoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
338 void ReloadCmailMsgProc P((Widget w, XEvent *event, String *prms,
339                             Cardinal *nprms));
340 void QuitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
341 void PauseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
342 void MachineBlackProc P((Widget w, XEvent *event, String *prms,
343                          Cardinal *nprms));
344 void MachineWhiteProc P((Widget w, XEvent *event,
345                          String *prms, Cardinal *nprms));
346 void AnalyzeModeProc P((Widget w, XEvent *event,
347                          String *prms, Cardinal *nprms));
348 void AnalyzeFileProc P((Widget w, XEvent *event,
349                          String *prms, Cardinal *nprms));
350 void TwoMachinesProc P((Widget w, XEvent *event, String *prms,
351                         Cardinal *nprms));
352 void IcsClientProc P((Widget w, XEvent *event, String *prms,
353                       Cardinal *nprms));
354 void EditGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
355 void EditPositionProc P((Widget w, XEvent *event,
356                          String *prms, Cardinal *nprms));
357 void TrainingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
358 void EditCommentProc P((Widget w, XEvent *event,
359                         String *prms, Cardinal *nprms));
360 void IcsInputBoxProc P((Widget w, XEvent *event,
361                         String *prms, Cardinal *nprms));
362 void AcceptProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
363 void DeclineProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
364 void RematchProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
365 void CallFlagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
366 void DrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
367 void AbortProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
368 void AdjournProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
369 void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
370 void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
371 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
372 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
373 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
374 void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
375 void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
376 void StopObservingProc P((Widget w, XEvent *event, String *prms,
377                           Cardinal *nprms));
378 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
379                           Cardinal *nprms));
380 void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
381 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
382 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
383 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
384 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
385 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
386 void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
387 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
388                          Cardinal *nprms));
389 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
390                         Cardinal *nprms));
391 void MoveNowProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
392 void AlwaysQueenProc P((Widget w, XEvent *event, String *prms,
393                         Cardinal *nprms));
394 void AnimateDraggingProc P((Widget w, XEvent *event, String *prms,
395                          Cardinal *nprms));
396 void AnimateMovingProc P((Widget w, XEvent *event, String *prms,
397                          Cardinal *nprms));
398 void AutocommProc P((Widget w, XEvent *event, String *prms,
399                      Cardinal *nprms));
400 void AutoflagProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
401 void AutoflipProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
402 void AutobsProc P((Widget w, XEvent *event, String *prms,
403                         Cardinal *nprms));
404 void AutoraiseProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
405 void AutosaveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
406 void BlindfoldProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
407 void FlashMovesProc P((Widget w, XEvent *event, String *prms,
408                        Cardinal *nprms));
409 void FlipViewProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
410 void GetMoveListProc P((Widget w, XEvent *event, String *prms,
411                         Cardinal *nprms));
412 void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
413                               Cardinal *nprms));
414 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
415                               Cardinal *nprms));
416 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
417 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
418 void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
419                          Cardinal *nprms));
420 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
421                          Cardinal *nprms));
422 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
423                            Cardinal *nprms));
424 void PopupMoveErrorsProc P((Widget w, XEvent *event, String *prms,
425                         Cardinal *nprms));
426 void PopupExitMessageProc P((Widget w, XEvent *event, String *prms,
427                              Cardinal *nprms));
428 void PremoveProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
429 void QuietPlayProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
430 void ShowCoordsProc P((Widget w, XEvent *event, String *prms,
431                        Cardinal *nprms));
432 void ShowThinkingProc P((Widget w, XEvent *event, String *prms,
433                          Cardinal *nprms));
434 void HideThinkingProc P((Widget w, XEvent *event, String *prms,
435                          Cardinal *nprms));
436 void TestLegalityProc P((Widget w, XEvent *event, String *prms,
437                           Cardinal *nprms));
438 void SaveSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
439 void SaveOnExitProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
440 void InfoProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
441 void ManProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
442 void HintProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
443 void BookProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
444 void AboutGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
445 void AboutProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
446 void DebugProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
447 void NothingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
448 void Iconify P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
449 void DisplayMove P((int moveNumber));
450 void DisplayTitle P((char *title));
451 void ICSInitScript P((void));
452 int LoadGamePopUp P((FILE *f, int gameNumber, char *title));
453 void ErrorPopUp P((char *title, char *text, int modal));
454 void ErrorPopDown P((void));
455 static char *ExpandPathName P((char *path));
456 static void CreateAnimVars P((void));
457 static void DragPieceMove P((int x, int y));
458 static void DrawDragPiece P((void));
459 char *ModeToWidgetName P((GameMode mode));
460 void ShuffleMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
461 void EngineMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
462 void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
463 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
464 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
465 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
466 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
467 void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
468 void GameListOptionsPopDown P(());
469 void ShufflePopDown P(());
470 void EnginePopDown P(());
471 void UciPopDown P(());
472 void TimeControlPopDown P(());
473 void NewVariantPopDown P(());
474 void SettingsPopDown P(());
475 void update_ics_width P(());
476 int get_term_width P(());
477 int CopyMemoProc P(());
478
479 # if HAVE_LIBREADLINE /* add gnu-readline support */
480 static void ReadlineCompleteHandler P((char *)); 
481 # endif
482
483 /*
484 * XBoard depends on Xt R4 or higher
485 */
486 int xtVersion = XtSpecificationRelease;
487
488 int xScreen;
489 Display *xDisplay;
490 Window xBoardWindow;
491 Pixel lightSquareColor, darkSquareColor, whitePieceColor, blackPieceColor,
492   jailSquareColor, highlightSquareColor, premoveHighlightColor;
493 Pixel lowTimeWarningColor;
494 GC lightSquareGC, darkSquareGC, jailSquareGC, lineGC, wdPieceGC, wlPieceGC,
495   bdPieceGC, blPieceGC, wbPieceGC, bwPieceGC, coordGC, highlineGC,
496   wjPieceGC, bjPieceGC, prelineGC, countGC;
497 Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
498 Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
499   whiteTimerWidget, blackTimerWidget, titleWidget, widgetList[16],
500   commentShell, promotionShell, whitePieceMenu, blackPieceMenu, dropMenu,
501   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
502   ICSInputShell, fileNameShell, askQuestionShell;
503 Widget historyShell, evalGraphShell, gameListShell;
504 int hOffset; // [HGM] dual
505 XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
506 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
507 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
508 Font clockFontID, coordFontID, countFontID;
509 XFontStruct *clockFontStruct, *coordFontStruct, *countFontStruct;
510 XtAppContext appContext;
511 char *layoutName;
512 char *oldICSInteractionTitle;
513
514 FileProc fileProc;
515 char *fileOpenMode;
516 char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
517
518 # if HAVE_LIBREADLINE /* gnu readline support */
519 static char* readline_buffer;
520 static int readline_complete=0;
521 extern int sending_ICS_login;
522 extern int sending_ICS_password;
523 #endif
524
525 Position commentX = -1, commentY = -1;
526 Dimension commentW, commentH;
527 typedef unsigned int BoardSize;
528 BoardSize boardSize;
529 Boolean chessProgram;
530
531 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
532 int squareSize, smallLayout = 0, tinyLayout = 0,
533   marginW, marginH, // [HGM] for run-time resizing
534   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
535   ICSInputBoxUp = False, askQuestionUp = False,
536   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
537   editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
538 Pixel timerForegroundPixel, timerBackgroundPixel;
539 Pixel buttonForegroundPixel, buttonBackgroundPixel;
540 char *chessDir, *programName, *programVersion,
541   *gameCopyFilename, *gamePasteFilename;
542 Boolean alwaysOnTop = False;
543 Boolean saveSettingsOnExit;
544 char *settingsFileName;
545 char *icsTextMenuString;
546 char *icsNames;
547 char *firstChessProgramNames;
548 char *secondChessProgramNames;
549
550 WindowPlacement wpMain;
551 WindowPlacement wpConsole;
552 WindowPlacement wpComment;
553 WindowPlacement wpMoveHistory;
554 WindowPlacement wpEvalGraph;
555 WindowPlacement wpEngineOutput;
556 WindowPlacement wpGameList;
557 WindowPlacement wpTags;
558
559 #define SOLID 0
560 #define OUTLINE 1
561 Pixmap pieceBitmap[2][(int)BlackPawn];
562 Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
563 Pixmap xpmPieceBitmap[4][(int)BlackPawn];       /* LL, LD, DL, DD actually used*/
564 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];    /* LL, LD, DL, DD set to select from */
565 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
566 Pixmap xpmBoardBitmap[2];
567 int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
568 XImage *ximPieceBitmap[4][(int)BlackPawn+4];    /* LL, LD, DL, DD */
569 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
570 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
571 XImage *ximLightSquare, *ximDarkSquare;
572 XImage *xim_Cross;
573
574 #define pieceToSolid(piece) &pieceBitmap[SOLID][(piece) % (int)BlackPawn]
575 #define pieceToOutline(piece) &pieceBitmap[OUTLINE][(piece) % (int)BlackPawn]
576
577 #define White(piece) ((int)(piece) < (int)BlackPawn)
578
579 /* Variables for doing smooth animation. This whole thing
580    would be much easier if the board was double-buffered,
581    but that would require a fairly major rewrite.       */
582
583 typedef struct {
584         Pixmap  saveBuf;
585         Pixmap  newBuf;
586         GC      blitGC, pieceGC, outlineGC;
587         XPoint  startSquare, prevFrame, mouseDelta;
588         int     startColor;
589         int     dragPiece;
590         Boolean dragActive;
591         int     startBoardX, startBoardY;
592     } AnimState;
593
594 /* There can be two pieces being animated at once: a player
595    can begin dragging a piece before the remote opponent has moved. */
596
597 static AnimState game, player;
598
599 /* Bitmaps for use as masks when drawing XPM pieces.
600    Need one for each black and white piece.             */
601 static Pixmap xpmMask[BlackKing + 1];
602
603 /* This magic number is the number of intermediate frames used
604    in each half of the animation. For short moves it's reduced
605    by 1. The total number of frames will be factor * 2 + 1.  */
606 #define kFactor    4
607
608 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
609
610 MenuItem fileMenu[] = {
611     {N_("New Game"), ResetProc},
612     {N_("New Shuffle Game ..."), ShuffleMenuProc},
613     {N_("New Variant ..."), NewVariantProc},      // [HGM] variant: not functional yet
614     {"----", NothingProc},
615     {N_("Load Game"), LoadGameProc},
616     {N_("Load Next Game"), LoadNextGameProc},
617     {N_("Load Previous Game"), LoadPrevGameProc},
618     {N_("Reload Same Game"), ReloadGameProc},
619     {N_("Save Game"), SaveGameProc},
620     {"----", NothingProc},
621     {N_("Copy Game"), CopyGameProc},
622     {N_("Paste Game"), PasteGameProc},
623     {"----", NothingProc},
624     {N_("Load Position"), LoadPositionProc},
625     {N_("Load Next Position"), LoadNextPositionProc},
626     {N_("Load Previous Position"), LoadPrevPositionProc},
627     {N_("Reload Same Position"), ReloadPositionProc},
628     {N_("Save Position"), SavePositionProc},
629     {"----", NothingProc},
630     {N_("Copy Position"), CopyPositionProc},
631     {N_("Paste Position"), PastePositionProc},
632     {"----", NothingProc},
633     {N_("Mail Move"), MailMoveProc},
634     {N_("Reload CMail Message"), ReloadCmailMsgProc},
635     {"----", NothingProc},
636     {N_("Exit"), QuitProc},
637     {NULL, NULL}
638 };
639
640 MenuItem modeMenu[] = {
641     {N_("Machine White"), MachineWhiteProc},
642     {N_("Machine Black"), MachineBlackProc},
643     {N_("Two Machines"), TwoMachinesProc},
644     {N_("Analysis Mode"), AnalyzeModeProc},
645     {N_("Analyze File"), AnalyzeFileProc },
646     {N_("ICS Client"), IcsClientProc},
647     {N_("Edit Game"), EditGameProc},
648     {N_("Edit Position"), EditPositionProc},
649     {N_("Training"), TrainingProc},
650     {"----", NothingProc},
651     {N_("Show Engine Output"), EngineOutputProc},
652     {N_("Show Evaluation Graph"), EvalGraphProc},
653     {N_("Show Game List"), ShowGameListProc},
654     {N_("Show Move History"), HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
655     {"----", NothingProc},
656     {N_("Edit Tags"), EditTagsProc},
657     {N_("Edit Comment"), EditCommentProc},
658     {N_("ICS Input Box"), IcsInputBoxProc},
659     {N_("Pause"), PauseProc},
660     {NULL, NULL}
661 };
662
663 MenuItem actionMenu[] = {
664     {N_("Accept"), AcceptProc},
665     {N_("Decline"), DeclineProc},
666     {N_("Rematch"), RematchProc},
667     {"----", NothingProc},
668     {N_("Call Flag"), CallFlagProc},
669     {N_("Draw"), DrawProc},
670     {N_("Adjourn"), AdjournProc},
671     {N_("Abort"), AbortProc},
672     {N_("Resign"), ResignProc},
673     {"----", NothingProc},
674     {N_("Stop Observing"), StopObservingProc},
675     {N_("Stop Examining"), StopExaminingProc},
676     {N_("Upload to Examine"), UploadProc},
677     {"----", NothingProc},
678     {N_("Adjudicate to White"), AdjuWhiteProc},
679     {N_("Adjudicate to Black"), AdjuBlackProc},
680     {N_("Adjudicate Draw"), AdjuDrawProc},
681     {NULL, NULL}
682 };
683
684 MenuItem stepMenu[] = {
685     {N_("Backward"), BackwardProc},
686     {N_("Forward"), ForwardProc},
687     {N_("Back to Start"), ToStartProc},
688     {N_("Forward to End"), ToEndProc},
689     {N_("Revert"), RevertProc},
690     {N_("Annotate"), AnnotateProc},
691     {N_("Truncate Game"), TruncateGameProc},
692     {"----", NothingProc},
693     {N_("Move Now"), MoveNowProc},
694     {N_("Retract Move"), RetractMoveProc},
695     {NULL, NULL}
696 };
697
698 MenuItem optionsMenu[] = {
699     {N_("Flip View"), FlipViewProc},
700     {"----", NothingProc},
701     {N_("Adjudications ..."), EngineMenuProc},
702     {N_("General Settings ..."), UciMenuProc},
703     {N_("Engine #1 Settings ..."), FirstSettingsProc},
704     {N_("Engine #2 Settings ..."), SecondSettingsProc},
705     {N_("Time Control ..."), TimeControlProc},
706     {N_("Game List ..."), GameListOptionsPopUp},
707     {"----", NothingProc},
708     {N_("Always Queen"), AlwaysQueenProc},
709     {N_("Animate Dragging"), AnimateDraggingProc},
710     {N_("Animate Moving"), AnimateMovingProc},
711     {N_("Auto Comment"), AutocommProc},
712     {N_("Auto Flag"), AutoflagProc},
713     {N_("Auto Flip View"), AutoflipProc},
714     {N_("Auto Observe"), AutobsProc},
715     {N_("Auto Raise Board"), AutoraiseProc},
716     {N_("Auto Save"), AutosaveProc},
717     {N_("Blindfold"), BlindfoldProc},
718     {N_("Flash Moves"), FlashMovesProc},
719     {N_("Get Move List"), GetMoveListProc},
720 #if HIGHDRAG
721     {N_("Highlight Dragging"), HighlightDraggingProc},
722 #endif
723     {N_("Highlight Last Move"), HighlightLastMoveProc},
724     {N_("Move Sound"), MoveSoundProc},
725     {N_("ICS Alarm"), IcsAlarmProc},
726     {N_("Old Save Style"), OldSaveStyleProc},
727     {N_("Periodic Updates"), PeriodicUpdatesProc},
728     {N_("Ponder Next Move"), PonderNextMoveProc},
729     {N_("Popup Exit Message"), PopupExitMessageProc},
730     {N_("Popup Move Errors"), PopupMoveErrorsProc},
731     {N_("Premove"), PremoveProc},
732     {N_("Quiet Play"), QuietPlayProc},
733     {N_("Show Coords"), ShowCoordsProc},
734     {N_("Hide Thinking"), HideThinkingProc},
735     {N_("Test Legality"), TestLegalityProc},
736     {"----", NothingProc},
737     {N_("Save Settings Now"), SaveSettingsProc},
738     {N_("Save Settings on Exit"), SaveOnExitProc},
739     {NULL, NULL}
740 };
741
742 MenuItem helpMenu[] = {
743     {N_("Info XBoard"), InfoProc},
744     {N_("Man XBoard"), ManProc},
745     {"----", NothingProc},
746     {N_("Hint"), HintProc},
747     {N_("Book"), BookProc},
748     {"----", NothingProc},
749     {N_("About XBoard"), AboutProc},
750     {NULL, NULL}
751 };
752
753 Menu menuBar[] = {
754     {N_("File"), fileMenu},
755     {N_("Mode"), modeMenu},
756     {N_("Action"), actionMenu},
757     {N_("Step"), stepMenu},
758     {N_("Options"), optionsMenu},
759     {N_("Help"), helpMenu},
760     {NULL, NULL}
761 };
762
763 #define PAUSE_BUTTON N_("P")
764 MenuItem buttonBar[] = {
765     {"<<", ToStartProc},
766     {"<", BackwardProc},
767     {PAUSE_BUTTON, PauseProc},
768     {">", ForwardProc},
769     {">>", ToEndProc},
770     {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    :Ctrl<Key>m: MoveNowProc() \n \
1063    :Ctrl<Key>x: RetractMoveProc() \n \
1064    :Meta<Key>J: EngineMenuProc() \n \
1065    :Meta<Key>U: UciMenuProc() \n \
1066    :Meta<Key>T: TimeControlProc() \n \
1067    :Ctrl<Key>Q: AlwaysQueenProc() \n \
1068    :Ctrl<Key>F: AutoflagProc() \n \
1069    :Ctrl<Key>A: AnimateMovingProc() \n \
1070    :Ctrl<Key>P: PonderNextMoveProc() \n \
1071    :Ctrl<Key>L: TestLegalityProc() \n \
1072    :Ctrl<Key>H: HideThinkingProc() \n \
1073    :<Key>-: Iconify() \n \
1074    :<Key>F1: ManProc() \n \
1075    :<Key>F2: FlipViewProc() \n \
1076    <KeyDown>.: BackwardProc() \n \
1077    <KeyUp>.: ForwardProc() \n \
1078    Shift<Key>1: AskQuestionProc(\"Direct command\",\
1079                                 \"Send to chess program:\",,1) \n \
1080    Shift<Key>2: AskQuestionProc(\"Direct command\",\
1081                                 \"Send to second chess program:\",,2) \n";
1082
1083 char boardTranslations[] =
1084    "<Btn1Down>: HandleUserMove(0) \n \
1085    Shift<Btn1Up>: HandleUserMove(1) \n \
1086    <Btn1Up>: HandleUserMove(0) \n \
1087    <Btn1Motion>: AnimateUserMove() \n \
1088    <Btn3Motion>: HandlePV() \n \
1089    <Btn3Up>: PieceMenuPopup(menuB) \n \
1090    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
1091                  PieceMenuPopup(menuB) \n \
1092    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
1093                  PieceMenuPopup(menuW) \n \
1094    Shift<Btn3Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD)\
1095                  PieceMenuPopup(menuW) \n \
1096    Any<Btn3Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD) \
1097                  PieceMenuPopup(menuB) \n";
1098
1099 char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
1100 char blackTranslations[] = "<BtnDown>: BlackClock()\n";
1101
1102 char ICSInputTranslations[] =
1103     "<Key>Up: UpKeyProc() \n "
1104     "<Key>Down: DownKeyProc() \n "
1105     "<Key>Return: EnterKeyProc() \n";
1106
1107 // [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
1108 //             as the widget is destroyed before the up-click can call extend-end
1109 char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
1110
1111 String xboardResources[] = {
1112     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
1113     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
1114     "*errorpopup*translations: #override\\n <Key>Return: ErrorPopDown()",
1115     NULL
1116   };
1117
1118
1119 /* Max possible square size */
1120 #define MAXSQSIZE 256
1121
1122 static int xpm_avail[MAXSQSIZE];
1123
1124 #ifdef HAVE_DIR_STRUCT
1125
1126 /* Extract piece size from filename */
1127 static int
1128 xpm_getsize(name, len, ext)
1129      char *name;
1130      int len;
1131      char *ext;
1132 {
1133     char *p, *d;
1134     char buf[10];
1135
1136     if (len < 4)
1137       return 0;
1138
1139     if ((p=strchr(name, '.')) == NULL ||
1140         StrCaseCmp(p+1, ext) != 0)
1141       return 0;
1142
1143     p = name + 3;
1144     d = buf;
1145
1146     while (*p && isdigit(*p))
1147       *(d++) = *(p++);
1148
1149     *d = 0;
1150     return atoi(buf);
1151 }
1152
1153 /* Setup xpm_avail */
1154 static int
1155 xpm_getavail(dirname, ext)
1156      char *dirname;
1157      char *ext;
1158 {
1159     DIR *dir;
1160     struct dirent *ent;
1161     int  i;
1162
1163     for (i=0; i<MAXSQSIZE; ++i)
1164       xpm_avail[i] = 0;
1165
1166     if (appData.debugMode)
1167       fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
1168
1169     dir = opendir(dirname);
1170     if (!dir)
1171       {
1172           fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
1173                   programName, dirname);
1174           exit(1);
1175       }
1176
1177     while ((ent=readdir(dir)) != NULL) {
1178         i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
1179         if (i > 0 && i < MAXSQSIZE)
1180           xpm_avail[i] = 1;
1181     }
1182
1183     closedir(dir);
1184
1185     return 0;
1186 }
1187
1188 void
1189 xpm_print_avail(fp, ext)
1190      FILE *fp;
1191      char *ext;
1192 {
1193     int i;
1194
1195     fprintf(fp, _("Available `%s' sizes:\n"), ext);
1196     for (i=1; i<MAXSQSIZE; ++i) {
1197         if (xpm_avail[i])
1198           printf("%d\n", i);
1199     }
1200 }
1201
1202 /* Return XPM piecesize closest to size */
1203 int
1204 xpm_closest_to(dirname, size, ext)
1205      char *dirname;
1206      int size;
1207      char *ext;
1208 {
1209     int i;
1210     int sm_diff = MAXSQSIZE;
1211     int sm_index = 0;
1212     int diff;
1213
1214     xpm_getavail(dirname, ext);
1215
1216     if (appData.debugMode)
1217       xpm_print_avail(stderr, ext);
1218
1219     for (i=1; i<MAXSQSIZE; ++i) {
1220         if (xpm_avail[i]) {
1221             diff = size - i;
1222             diff = (diff<0) ? -diff : diff;
1223             if (diff < sm_diff) {
1224                 sm_diff = diff;
1225                 sm_index = i;
1226             }
1227         }
1228     }
1229
1230     if (!sm_index) {
1231         fprintf(stderr, _("Error: No `%s' files!\n"), ext);
1232         exit(1);
1233     }
1234
1235     return sm_index;
1236 }
1237 #else   /* !HAVE_DIR_STRUCT */
1238 /* If we are on a system without a DIR struct, we can't
1239    read the directory, so we can't collect a list of
1240    filenames, etc., so we can't do any size-fitting. */
1241 int
1242 xpm_closest_to(dirname, size, ext)
1243      char *dirname;
1244      int size;
1245      char *ext;
1246 {
1247     fprintf(stderr, _("\
1248 Warning: No DIR structure found on this system --\n\
1249          Unable to autosize for XPM/XIM pieces.\n\
1250    Please report this error to frankm@hiwaay.net.\n\
1251    Include system type & operating system in message.\n"));
1252     return size;
1253 }
1254 #endif /* HAVE_DIR_STRUCT */
1255
1256 static char *cnames[9] = { "black", "red", "green", "yellow", "blue",
1257                              "magenta", "cyan", "white" };
1258 typedef struct {
1259     int attr, bg, fg;
1260 } TextColors;
1261 TextColors textColors[(int)NColorClasses];
1262
1263 /* String is: "fg, bg, attr". Which is 0, 1, 2 */
1264 static int
1265 parse_color(str, which)
1266      char *str;
1267      int which;
1268 {
1269     char *p, buf[100], *d;
1270     int i;
1271
1272     if (strlen(str) > 99)       /* watch bounds on buf */
1273       return -1;
1274
1275     p = str;
1276     d = buf;
1277     for (i=0; i<which; ++i) {
1278         p = strchr(p, ',');
1279         if (!p)
1280           return -1;
1281         ++p;
1282     }
1283
1284     /* Could be looking at something like:
1285        black, , 1
1286        .. in which case we want to stop on a comma also */
1287     while (*p && *p != ',' && !isalpha(*p) && !isdigit(*p))
1288       ++p;
1289
1290     if (*p == ',') {
1291         return -1;              /* Use default for empty field */
1292     }
1293
1294     if (which == 2 || isdigit(*p))
1295       return atoi(p);
1296
1297     while (*p && isalpha(*p))
1298       *(d++) = *(p++);
1299
1300     *d = 0;
1301
1302     for (i=0; i<8; ++i) {
1303         if (!StrCaseCmp(buf, cnames[i]))
1304           return which? (i+40) : (i+30);
1305     }
1306     if (!StrCaseCmp(buf, "default")) return -1;
1307
1308     fprintf(stderr, _("%s: unrecognized color %s\n"), programName, buf);
1309     return -2;
1310 }
1311
1312 static int
1313 parse_cpair(cc, str)
1314      ColorClass cc;
1315      char *str;
1316 {
1317     if ((textColors[(int)cc].fg=parse_color(str, 0)) == -2) {
1318         fprintf(stderr, _("%s: can't parse foreground color in `%s'\n"),
1319                 programName, str);
1320         return -1;
1321     }
1322
1323     /* bg and attr are optional */
1324     textColors[(int)cc].bg = parse_color(str, 1);
1325     if ((textColors[(int)cc].attr = parse_color(str, 2)) < 0) {
1326         textColors[(int)cc].attr = 0;
1327     }
1328     return 0;
1329 }
1330
1331
1332 /* Arrange to catch delete-window events */
1333 Atom wm_delete_window;
1334 void
1335 CatchDeleteWindow(Widget w, String procname)
1336 {
1337   char buf[MSG_SIZ];
1338   XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
1339   snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
1340   XtAugmentTranslations(w, XtParseTranslationTable(buf));
1341 }
1342
1343 void
1344 BoardToTop()
1345 {
1346   Arg args[16];
1347   XtSetArg(args[0], XtNiconic, False);
1348   XtSetValues(shellWidget, args, 1);
1349
1350   XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
1351 }
1352
1353 //---------------------------------------------------------------------------------------------------------
1354 // some symbol definitions to provide the proper (= XBoard) context for the code in args.h
1355 #define XBOARD True
1356 #define JAWS_ARGS
1357 #define CW_USEDEFAULT (1<<31)
1358 #define ICS_TEXT_MENU_SIZE 90
1359 #define DEBUG_FILE "xboard.debug"
1360 #define SetCurrentDirectory chdir
1361 #define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
1362 #define OPTCHAR "-"
1363 #define SEPCHAR " "
1364
1365 // these two must some day move to frontend.h, when they are implemented
1366 Boolean GameListIsUp();
1367
1368 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
1369 #include "args.h"
1370
1371 // front-end part of option handling
1372
1373 // [HGM] This platform-dependent table provides the location for storing the color info
1374 extern char *crWhite, * crBlack;
1375
1376 void *
1377 colorVariable[] = {
1378   &appData.whitePieceColor,
1379   &appData.blackPieceColor,
1380   &appData.lightSquareColor,
1381   &appData.darkSquareColor,
1382   &appData.highlightSquareColor,
1383   &appData.premoveHighlightColor,
1384   &appData.lowTimeWarningColor,
1385   NULL,
1386   NULL,
1387   NULL,
1388   NULL,
1389   NULL,
1390   &crWhite,
1391   &crBlack,
1392   NULL
1393 };
1394
1395 // [HGM] font: keep a font for each square size, even non-stndard ones
1396 #define NUM_SIZES 18
1397 #define MAX_SIZE 130
1398 Boolean fontSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
1399 char *fontTable[NUM_FONTS][MAX_SIZE];
1400
1401 void
1402 ParseFont(char *name, int number)
1403 { // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
1404   int size;
1405   if(sscanf(name, "size%d:", &size)) {
1406     // [HGM] font: font is meant for specific boardSize (likely from settings file);
1407     //       defer processing it until we know if it matches our board size
1408     if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
1409         fontTable[number][size] = strdup(strchr(name, ':')+1);
1410         fontValid[number][size] = True;
1411     }
1412     return;
1413   }
1414   switch(number) {
1415     case 0: // CLOCK_FONT
1416         appData.clockFont = strdup(name);
1417       break;
1418     case 1: // MESSAGE_FONT
1419         appData.font = strdup(name);
1420       break;
1421     case 2: // COORD_FONT
1422         appData.coordFont = strdup(name);
1423       break;
1424     default:
1425       return;
1426   }
1427   fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
1428 }
1429
1430 void
1431 SetFontDefaults()
1432 { // only 2 fonts currently
1433   appData.clockFont = CLOCK_FONT_NAME;
1434   appData.coordFont = COORD_FONT_NAME;
1435   appData.font  =   DEFAULT_FONT_NAME;
1436 }
1437
1438 void
1439 CreateFonts()
1440 { // no-op, until we identify the code for this already in XBoard and move it here
1441 }
1442
1443 void
1444 ParseColor(int n, char *name)
1445 { // in XBoard, just copy the color-name string
1446   if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
1447 }
1448
1449 void
1450 ParseTextAttribs(ColorClass cc, char *s)
1451 {
1452     (&appData.colorShout)[cc] = strdup(s);
1453 }
1454
1455 void
1456 ParseBoardSize(void *addr, char *name)
1457 {
1458     appData.boardSize = strdup(name);
1459 }
1460
1461 void
1462 LoadAllSounds()
1463 { // In XBoard the sound-playing program takes care of obtaining the actual sound
1464 }
1465
1466 void
1467 SetCommPortDefaults()
1468 { // for now, this is a no-op, as the corresponding option does not exist in XBoard
1469 }
1470
1471 // [HGM] args: these three cases taken out to stay in front-end
1472 void
1473 SaveFontArg(FILE *f, ArgDescriptor *ad)
1474 {
1475   char *name;
1476   int i, n = (int)ad->argLoc;
1477   switch(n) {
1478     case 0: // CLOCK_FONT
1479         name = appData.clockFont;
1480       break;
1481     case 1: // MESSAGE_FONT
1482         name = appData.font;
1483       break;
1484     case 2: // COORD_FONT
1485         name = appData.coordFont;
1486       break;
1487     default:
1488       return;
1489   }
1490   for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
1491     if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
1492         fontTable[n][squareSize] = strdup(name);
1493         fontValid[n][squareSize] = True;
1494         break;
1495   }
1496   for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
1497     fprintf(f, OPTCHAR "%s" SEPCHAR "size%d:%s\n", ad->argName, i, fontTable[n][i]);
1498 }
1499
1500 void
1501 ExportSounds()
1502 { // nothing to do, as the sounds are at all times represented by their text-string names already
1503 }
1504
1505 void
1506 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
1507 {       // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
1508         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
1509 }
1510
1511 void
1512 SaveColor(FILE *f, ArgDescriptor *ad)
1513 {       // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
1514         if(colorVariable[(int)ad->argLoc])
1515         fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
1516 }
1517
1518 void
1519 SaveBoardSize(FILE *f, char *name, void *addr)
1520 { // wrapper to shield back-end from BoardSize & sizeInfo
1521   fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
1522 }
1523
1524 void
1525 ParseCommPortSettings(char *s)
1526 { // no such option in XBoard (yet)
1527 }
1528
1529 extern Widget engineOutputShell;
1530 extern Widget tagsShell, editTagsShell;
1531 void
1532 GetActualPlacement(Widget wg, WindowPlacement *wp)
1533 {
1534   Arg args[16];
1535   Dimension w, h;
1536   Position x, y;
1537   int i;
1538
1539   if(!wg) return;
1540
1541     i = 0;
1542     XtSetArg(args[i], XtNx, &x); i++;
1543     XtSetArg(args[i], XtNy, &y); i++;
1544     XtSetArg(args[i], XtNwidth, &w); i++;
1545     XtSetArg(args[i], XtNheight, &h); i++;
1546     XtGetValues(wg, args, i);
1547     wp->x = x - 4;
1548     wp->y = y - 23;
1549     wp->height = h;
1550     wp->width = w;
1551 }
1552
1553 void
1554 GetWindowCoords()
1555 { // wrapper to shield use of window handles from back-end (make addressible by number?)
1556   // In XBoard this will have to wait until awareness of window parameters is implemented
1557   GetActualPlacement(shellWidget, &wpMain);
1558   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
1559   if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
1560   if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
1561   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
1562   if(commentShell) GetActualPlacement(commentShell, &wpComment);
1563   else             GetActualPlacement(editShell,    &wpComment);
1564   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
1565   else      GetActualPlacement(editTagsShell, &wpTags);
1566 }
1567
1568 void
1569 PrintCommPortSettings(FILE *f, char *name)
1570 { // This option does not exist in XBoard
1571 }
1572
1573 int
1574 MySearchPath(char *installDir, char *name, char *fullname)
1575 { // just append installDir and name. Perhaps ExpandPath should be used here?
1576   name = ExpandPathName(name);
1577   if(name && name[0] == '/')
1578     safeStrCpy(fullname, name, MSG_SIZ );
1579   else {
1580     sprintf(fullname, "%s%c%s", installDir, '/', name);
1581   }
1582   return 1;
1583 }
1584
1585 int
1586 MyGetFullPathName(char *name, char *fullname)
1587 { // should use ExpandPath?
1588   name = ExpandPathName(name);
1589   safeStrCpy(fullname, name, MSG_SIZ );
1590   return 1;
1591 }
1592
1593 void
1594 EnsureOnScreen(int *x, int *y, int minX, int minY)
1595 {
1596   return;
1597 }
1598
1599 int
1600 MainWindowUp()
1601 { // [HGM] args: allows testing if main window is realized from back-end
1602   return xBoardWindow != 0;
1603 }
1604
1605 void
1606 PopUpStartupDialog()
1607 {  // start menu not implemented in XBoard
1608 }
1609
1610 char *
1611 ConvertToLine(int argc, char **argv)
1612 {
1613   static char line[128*1024], buf[1024];
1614   int i;
1615
1616   line[0] = NULLCHAR;
1617   for(i=1; i<argc; i++)
1618     {
1619       if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
1620           && argv[i][0] != '{' )
1621         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
1622       else
1623         snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
1624       strncat(line, buf, 128*1024 - strlen(line) - 1 );
1625     }
1626
1627   line[strlen(line)-1] = NULLCHAR;
1628   return line;
1629 }
1630
1631 //--------------------------------------------------------------------------------------------
1632
1633 extern Boolean twoBoards, partnerUp;
1634
1635 #ifdef IDSIZES
1636   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
1637 #else
1638 #define BoardSize int
1639 void InitDrawingSizes(BoardSize boardSize, int flags)
1640 {   // [HGM] resize is functional now, but for board format changes only (nr of ranks, files)
1641     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1642     Arg args[16];
1643     XtGeometryResult gres;
1644     int i;
1645
1646     if(!formWidget) return;
1647
1648     /*
1649      * Enable shell resizing.
1650      */
1651     shellArgs[0].value = (XtArgVal) &w;
1652     shellArgs[1].value = (XtArgVal) &h;
1653     XtGetValues(shellWidget, shellArgs, 2);
1654
1655     shellArgs[4].value = 3*w; shellArgs[2].value = 10;
1656     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
1657     XtSetValues(shellWidget, &shellArgs[2], 4);
1658
1659     XtSetArg(args[0], XtNdefaultDistance, &sep);
1660     XtGetValues(formWidget, args, 1);
1661
1662     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1663     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1664     CreateGrid();
1665     hOffset = boardWidth + 10;
1666     for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
1667         secondSegments[i] = gridSegments[i];
1668         secondSegments[i].x1 += hOffset;
1669         secondSegments[i].x2 += hOffset;
1670     }
1671
1672     XtSetArg(args[0], XtNwidth, boardWidth);
1673     XtSetArg(args[1], XtNheight, boardHeight);
1674     XtSetValues(boardWidget, args, 2);
1675
1676     timerWidth = (boardWidth - sep) / 2;
1677     XtSetArg(args[0], XtNwidth, timerWidth);
1678     XtSetValues(whiteTimerWidget, args, 1);
1679     XtSetValues(blackTimerWidget, args, 1);
1680
1681     XawFormDoLayout(formWidget, False);
1682
1683     if (appData.titleInWindow) {
1684         i = 0;
1685         XtSetArg(args[i], XtNborderWidth, &bor); i++;
1686         XtSetArg(args[i], XtNheight, &h);  i++;
1687         XtGetValues(titleWidget, args, i);
1688         if (smallLayout) {
1689             w = boardWidth - 2*bor;
1690         } else {
1691             XtSetArg(args[0], XtNwidth, &w);
1692             XtGetValues(menuBarWidget, args, 1);
1693             w = boardWidth - w - sep - 2*bor - 2; // WIDTH_FUDGE
1694         }
1695
1696         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
1697         if (gres != XtGeometryYes && appData.debugMode) {
1698             fprintf(stderr,
1699                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
1700                     programName, gres, w, h, wr, hr);
1701         }
1702     }
1703
1704     XawFormDoLayout(formWidget, True);
1705
1706     /*
1707      * Inhibit shell resizing.
1708      */
1709     shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
1710     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
1711     shellArgs[4].value = shellArgs[2].value = w;
1712     shellArgs[5].value = shellArgs[3].value = h;
1713     XtSetValues(shellWidget, &shellArgs[0], 6);
1714
1715     // [HGM] pieces: tailor piece bitmaps to needs of specific variant
1716     // (only for xpm)
1717     if(useImages) {
1718       for(i=0; i<4; i++) {
1719         int p;
1720         for(p=0; p<=(int)WhiteKing; p++)
1721            xpmPieceBitmap[i][p] = xpmPieceBitmap2[i][p]; // defaults
1722         if(gameInfo.variant == VariantShogi) {
1723            xpmPieceBitmap[i][(int)WhiteCannon] = xpmPieceBitmap2[i][(int)WhiteKing+1];
1724            xpmPieceBitmap[i][(int)WhiteNightrider] = xpmPieceBitmap2[i][(int)WhiteKing+2];
1725            xpmPieceBitmap[i][(int)WhiteSilver] = xpmPieceBitmap2[i][(int)WhiteKing+3];
1726            xpmPieceBitmap[i][(int)WhiteGrasshopper] = xpmPieceBitmap2[i][(int)WhiteKing+4];
1727            xpmPieceBitmap[i][(int)WhiteQueen] = xpmPieceBitmap2[i][(int)WhiteLance];
1728         }
1729 #ifdef GOTHIC
1730         if(gameInfo.variant == VariantGothic) {
1731            xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
1732         }
1733 #endif
1734 #if !HAVE_LIBXPM
1735         // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
1736         for(p=0; p<=(int)WhiteKing; p++)
1737            ximMaskPm[p] = ximMaskPm2[p]; // defaults
1738         if(gameInfo.variant == VariantShogi) {
1739            ximMaskPm[(int)WhiteCannon] = ximMaskPm2[(int)WhiteKing+1];
1740            ximMaskPm[(int)WhiteNightrider] = ximMaskPm2[(int)WhiteKing+2];
1741            ximMaskPm[(int)WhiteSilver] = ximMaskPm2[(int)WhiteKing+3];
1742            ximMaskPm[(int)WhiteGrasshopper] = ximMaskPm2[(int)WhiteKing+4];
1743            ximMaskPm[(int)WhiteQueen] = ximMaskPm2[(int)WhiteLance];
1744         }
1745 #ifdef GOTHIC
1746         if(gameInfo.variant == VariantGothic) {
1747            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
1748         }
1749 #endif
1750 #endif
1751       }
1752     } else {
1753       for(i=0; i<2; i++) {
1754         int p;
1755         for(p=0; p<=(int)WhiteKing; p++)
1756            pieceBitmap[i][p] = pieceBitmap2[i][p]; // defaults
1757         if(gameInfo.variant == VariantShogi) {
1758            pieceBitmap[i][(int)WhiteCannon] = pieceBitmap2[i][(int)WhiteKing+1];
1759            pieceBitmap[i][(int)WhiteNightrider] = pieceBitmap2[i][(int)WhiteKing+2];
1760            pieceBitmap[i][(int)WhiteSilver] = pieceBitmap2[i][(int)WhiteKing+3];
1761            pieceBitmap[i][(int)WhiteGrasshopper] = pieceBitmap2[i][(int)WhiteKing+4];
1762            pieceBitmap[i][(int)WhiteQueen] = pieceBitmap2[i][(int)WhiteLance];
1763         }
1764 #ifdef GOTHIC
1765         if(gameInfo.variant == VariantGothic) {
1766            pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
1767         }
1768 #endif
1769       }
1770     }
1771 #if HAVE_LIBXPM
1772     CreateAnimVars();
1773 #endif
1774 }
1775 #endif
1776
1777 int
1778 main(argc, argv)
1779      int argc;
1780      char **argv;
1781 {
1782     int i, j, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
1783     XSetWindowAttributes window_attributes;
1784     Arg args[16];
1785     Dimension timerWidth, boardWidth, boardHeight, w, h, sep, bor, wr, hr;
1786     XrmValue vFrom, vTo;
1787     XtGeometryResult gres;
1788     char *p;
1789     XrmDatabase xdb;
1790     int forceMono = False;
1791
1792     srandom(time(0)); // [HGM] book: make random truly random
1793
1794     setbuf(stdout, NULL);
1795     setbuf(stderr, NULL);
1796     debugFP = stderr;
1797     
1798 # if HAVE_LIBREADLINE
1799     /* install gnu-readline handler */
1800     rl_callback_handler_install("> ", ReadlineCompleteHandler);
1801     rl_readline_name="XBoard";
1802 # endif
1803
1804     if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
1805         printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
1806         exit(0);
1807     }
1808
1809     programName = strrchr(argv[0], '/');
1810     if (programName == NULL)
1811       programName = argv[0];
1812     else
1813       programName++;
1814
1815 #ifdef ENABLE_NLS
1816     XtSetLanguageProc(NULL, NULL, NULL);
1817     bindtextdomain(PACKAGE, LOCALEDIR);
1818     textdomain(PACKAGE);
1819 #endif
1820
1821     shellWidget =
1822       XtAppInitialize(&appContext, "XBoard", shellOptions,
1823                       XtNumber(shellOptions),
1824                       &argc, argv, xboardResources, NULL, 0);
1825     appData.boardSize = "";
1826     InitAppData(ConvertToLine(argc, argv));
1827     p = getenv("HOME");
1828     if (p == NULL) p = "/tmp";
1829     i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
1830     gameCopyFilename = (char*) malloc(i);
1831     gamePasteFilename = (char*) malloc(i);
1832     snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
1833     snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
1834
1835     XtGetApplicationResources(shellWidget, (XtPointer) &appData,
1836                               clientResources, XtNumber(clientResources),
1837                               NULL, 0);
1838
1839     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
1840         static char buf[MSG_SIZ];
1841         EscapeExpand(buf, appData.initString);
1842         appData.initString = strdup(buf);
1843         EscapeExpand(buf, appData.secondInitString);
1844         appData.secondInitString = strdup(buf);
1845         EscapeExpand(buf, appData.firstComputerString);
1846         appData.firstComputerString = strdup(buf);
1847         EscapeExpand(buf, appData.secondComputerString);
1848         appData.secondComputerString = strdup(buf);
1849     }
1850
1851     if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
1852         chessDir = ".";
1853     } else {
1854         if (chdir(chessDir) != 0) {
1855             fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
1856             perror(chessDir);
1857             exit(1);
1858         }
1859     }
1860
1861     if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
1862         /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
1863         if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
1864            printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
1865            exit(errno);
1866         }
1867         setbuf(debugFP, NULL);
1868     }
1869
1870     /* [HGM,HR] make sure board size is acceptable */
1871     if(appData.NrFiles > BOARD_FILES ||
1872        appData.NrRanks > BOARD_RANKS   )
1873          DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
1874
1875 #if !HIGHDRAG
1876     /* This feature does not work; animation needs a rewrite */
1877     appData.highlightDragging = FALSE;
1878 #endif
1879     InitBackEnd1();
1880
1881     xDisplay = XtDisplay(shellWidget);
1882     xScreen = DefaultScreen(xDisplay);
1883     wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
1884
1885         gameInfo.variant = StringToVariant(appData.variant);
1886         InitPosition(FALSE);
1887
1888 #ifdef IDSIZE
1889     InitDrawingSizes(-1, 0); // [HGM] initsize: make this into a subroutine
1890 #else
1891     if (isdigit(appData.boardSize[0])) {
1892         i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
1893                    &lineGap, &clockFontPxlSize, &coordFontPxlSize,
1894                    &fontPxlSize, &smallLayout, &tinyLayout);
1895         if (i == 0) {
1896             fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
1897                     programName, appData.boardSize);
1898             exit(2);
1899         }
1900         if (i < 7) {
1901             /* Find some defaults; use the nearest known size */
1902             SizeDefaults *szd, *nearest;
1903             int distance = 99999;
1904             nearest = szd = sizeDefaults;
1905             while (szd->name != NULL) {
1906                 if (abs(szd->squareSize - squareSize) < distance) {
1907                     nearest = szd;
1908                     distance = abs(szd->squareSize - squareSize);
1909                     if (distance == 0) break;
1910                 }
1911                 szd++;
1912             }
1913             if (i < 2) lineGap = nearest->lineGap;
1914             if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
1915             if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
1916             if (i < 5) fontPxlSize = nearest->fontPxlSize;
1917             if (i < 6) smallLayout = nearest->smallLayout;
1918             if (i < 7) tinyLayout = nearest->tinyLayout;
1919         }
1920     } else {
1921         SizeDefaults *szd = sizeDefaults;
1922         if (*appData.boardSize == NULLCHAR) {
1923             while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
1924                    DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
1925               szd++;
1926             }
1927             if (szd->name == NULL) szd--;
1928             appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
1929         } else {
1930             while (szd->name != NULL &&
1931                    StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
1932             if (szd->name == NULL) {
1933                 fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
1934                         programName, appData.boardSize);
1935                 exit(2);
1936             }
1937         }
1938         squareSize = szd->squareSize;
1939         lineGap = szd->lineGap;
1940         clockFontPxlSize = szd->clockFontPxlSize;
1941         coordFontPxlSize = szd->coordFontPxlSize;
1942         fontPxlSize = szd->fontPxlSize;
1943         smallLayout = szd->smallLayout;
1944         tinyLayout = szd->tinyLayout;
1945         // [HGM] font: use defaults from settings file if available and not overruled
1946     }
1947     if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
1948         appData.clockFont = fontTable[CLOCK_FONT][squareSize];
1949     if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
1950         appData.font = fontTable[MESSAGE_FONT][squareSize];
1951     if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
1952         appData.coordFont = fontTable[COORD_FONT][squareSize];
1953
1954     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
1955     if (strlen(appData.pixmapDirectory) > 0) {
1956         p = ExpandPathName(appData.pixmapDirectory);
1957         if (!p) {
1958             fprintf(stderr, _("Error expanding path name \"%s\"\n"),
1959                    appData.pixmapDirectory);
1960             exit(1);
1961         }
1962         if (appData.debugMode) {
1963           fprintf(stderr, _("\
1964 XBoard square size (hint): %d\n\
1965 %s fulldir:%s:\n"), squareSize, IMAGE_EXT, p);
1966         }
1967         squareSize = xpm_closest_to(p, squareSize, IMAGE_EXT);
1968         if (appData.debugMode) {
1969             fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
1970         }
1971     }
1972
1973     /* [HR] height treated separately (hacked) */
1974     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
1975     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
1976     if (appData.showJail == 1) {
1977         /* Jail on top and bottom */
1978         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1979         XtSetArg(boardArgs[2], XtNheight,
1980                  boardHeight + 2*(lineGap + squareSize));
1981     } else if (appData.showJail == 2) {
1982         /* Jail on sides */
1983         XtSetArg(boardArgs[1], XtNwidth,
1984                  boardWidth + 2*(lineGap + squareSize));
1985         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1986     } else {
1987         /* No jail */
1988         XtSetArg(boardArgs[1], XtNwidth, boardWidth);
1989         XtSetArg(boardArgs[2], XtNheight, boardHeight);
1990     }
1991
1992     /*
1993      * Determine what fonts to use.
1994      */
1995     appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
1996     clockFontID = XLoadFont(xDisplay, appData.clockFont);
1997     clockFontStruct = XQueryFont(xDisplay, clockFontID);
1998     appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
1999     coordFontID = XLoadFont(xDisplay, appData.coordFont);
2000     coordFontStruct = XQueryFont(xDisplay, coordFontID);
2001     appData.font = FindFont(appData.font, fontPxlSize);
2002     countFontID = XLoadFont(xDisplay, appData.coordFont); // [HGM] holdings
2003     countFontStruct = XQueryFont(xDisplay, countFontID);
2004 //    appData.font = FindFont(appData.font, fontPxlSize);
2005
2006     xdb = XtDatabase(xDisplay);
2007     XrmPutStringResource(&xdb, "*font", appData.font);
2008
2009     /*
2010      * Detect if there are not enough colors available and adapt.
2011      */
2012     if (DefaultDepth(xDisplay, xScreen) <= 2) {
2013       appData.monoMode = True;
2014     }
2015
2016     if (!appData.monoMode) {
2017         vFrom.addr = (caddr_t) appData.lightSquareColor;
2018         vFrom.size = strlen(appData.lightSquareColor);
2019         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2020         if (vTo.addr == NULL) {
2021           appData.monoMode = True;
2022           forceMono = True;
2023         } else {
2024           lightSquareColor = *(Pixel *) vTo.addr;
2025         }
2026     }
2027     if (!appData.monoMode) {
2028         vFrom.addr = (caddr_t) appData.darkSquareColor;
2029         vFrom.size = strlen(appData.darkSquareColor);
2030         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2031         if (vTo.addr == NULL) {
2032           appData.monoMode = True;
2033           forceMono = True;
2034         } else {
2035           darkSquareColor = *(Pixel *) vTo.addr;
2036         }
2037     }
2038     if (!appData.monoMode) {
2039         vFrom.addr = (caddr_t) appData.whitePieceColor;
2040         vFrom.size = strlen(appData.whitePieceColor);
2041         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2042         if (vTo.addr == NULL) {
2043           appData.monoMode = True;
2044           forceMono = True;
2045         } else {
2046           whitePieceColor = *(Pixel *) vTo.addr;
2047         }
2048     }
2049     if (!appData.monoMode) {
2050         vFrom.addr = (caddr_t) appData.blackPieceColor;
2051         vFrom.size = strlen(appData.blackPieceColor);
2052         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2053         if (vTo.addr == NULL) {
2054           appData.monoMode = True;
2055           forceMono = True;
2056         } else {
2057           blackPieceColor = *(Pixel *) vTo.addr;
2058         }
2059     }
2060
2061     if (!appData.monoMode) {
2062         vFrom.addr = (caddr_t) appData.highlightSquareColor;
2063         vFrom.size = strlen(appData.highlightSquareColor);
2064         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2065         if (vTo.addr == NULL) {
2066           appData.monoMode = True;
2067           forceMono = True;
2068         } else {
2069           highlightSquareColor = *(Pixel *) vTo.addr;
2070         }
2071     }
2072
2073     if (!appData.monoMode) {
2074         vFrom.addr = (caddr_t) appData.premoveHighlightColor;
2075         vFrom.size = strlen(appData.premoveHighlightColor);
2076         XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2077         if (vTo.addr == NULL) {
2078           appData.monoMode = True;
2079           forceMono = True;
2080         } else {
2081           premoveHighlightColor = *(Pixel *) vTo.addr;
2082         }
2083     }
2084
2085     if (forceMono) {
2086       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
2087               programName);
2088
2089       if (appData.bitmapDirectory == NULL ||
2090               appData.bitmapDirectory[0] == NULLCHAR)
2091             appData.bitmapDirectory = DEF_BITMAP_DIR;
2092     }
2093
2094     if (appData.lowTimeWarning && !appData.monoMode) {
2095       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
2096       vFrom.size = strlen(appData.lowTimeWarningColor);
2097       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
2098       if (vTo.addr == NULL)
2099                 appData.monoMode = True;
2100       else
2101                 lowTimeWarningColor = *(Pixel *) vTo.addr;
2102     }
2103
2104     if (appData.monoMode && appData.debugMode) {
2105         fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
2106                 (unsigned long) XWhitePixel(xDisplay, xScreen),
2107                 (unsigned long) XBlackPixel(xDisplay, xScreen));
2108     }
2109
2110     if (parse_cpair(ColorShout, appData.colorShout) < 0 ||
2111         parse_cpair(ColorSShout, appData.colorSShout) < 0 ||
2112         parse_cpair(ColorChannel1, appData.colorChannel1) < 0  ||
2113         parse_cpair(ColorChannel, appData.colorChannel) < 0  ||
2114         parse_cpair(ColorKibitz, appData.colorKibitz) < 0 ||
2115         parse_cpair(ColorTell, appData.colorTell) < 0 ||
2116         parse_cpair(ColorChallenge, appData.colorChallenge) < 0  ||
2117         parse_cpair(ColorRequest, appData.colorRequest) < 0  ||
2118         parse_cpair(ColorSeek, appData.colorSeek) < 0  ||
2119         parse_cpair(ColorNormal, appData.colorNormal) < 0)
2120       {
2121           if (appData.colorize) {
2122               fprintf(stderr,
2123                       _("%s: can't parse color names; disabling colorization\n"),
2124                       programName);
2125           }
2126           appData.colorize = FALSE;
2127       }
2128     textColors[ColorNone].fg = textColors[ColorNone].bg = -1;
2129     textColors[ColorNone].attr = 0;
2130
2131     XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
2132
2133     /*
2134      * widget hierarchy
2135      */
2136     if (tinyLayout) {
2137         layoutName = "tinyLayout";
2138     } else if (smallLayout) {
2139         layoutName = "smallLayout";
2140     } else {
2141         layoutName = "normalLayout";
2142     }
2143     /* Outer layoutWidget is there only to provide a name for use in
2144        resources that depend on the layout style */
2145     layoutWidget =
2146       XtCreateManagedWidget(layoutName, formWidgetClass, shellWidget,
2147                             layoutArgs, XtNumber(layoutArgs));
2148     formWidget =
2149       XtCreateManagedWidget("form", formWidgetClass, layoutWidget,
2150                             formArgs, XtNumber(formArgs));
2151     XtSetArg(args[0], XtNdefaultDistance, &sep);
2152     XtGetValues(formWidget, args, 1);
2153
2154     j = 0;
2155     widgetList[j++] = menuBarWidget = CreateMenuBar(menuBar);
2156     XtSetArg(args[0], XtNtop,    XtChainTop);
2157     XtSetArg(args[1], XtNbottom, XtChainTop);
2158     XtSetArg(args[2], XtNright,  XtChainLeft);
2159     XtSetValues(menuBarWidget, args, 3);
2160
2161     widgetList[j++] = whiteTimerWidget =
2162       XtCreateWidget("whiteTime", labelWidgetClass,
2163                      formWidget, timerArgs, XtNumber(timerArgs));
2164     XtSetArg(args[0], XtNfont, clockFontStruct);
2165     XtSetArg(args[1], XtNtop,    XtChainTop);
2166     XtSetArg(args[2], XtNbottom, XtChainTop);
2167     XtSetValues(whiteTimerWidget, args, 3);
2168
2169     widgetList[j++] = blackTimerWidget =
2170       XtCreateWidget("blackTime", labelWidgetClass,
2171                      formWidget, timerArgs, XtNumber(timerArgs));
2172     XtSetArg(args[0], XtNfont, clockFontStruct);
2173     XtSetArg(args[1], XtNtop,    XtChainTop);
2174     XtSetArg(args[2], XtNbottom, XtChainTop);
2175     XtSetValues(blackTimerWidget, args, 3);
2176
2177     if (appData.titleInWindow) {
2178         widgetList[j++] = titleWidget =
2179           XtCreateWidget("title", labelWidgetClass, formWidget,
2180                          titleArgs, XtNumber(titleArgs));
2181         XtSetArg(args[0], XtNtop,    XtChainTop);
2182         XtSetArg(args[1], XtNbottom, XtChainTop);
2183         XtSetValues(titleWidget, args, 2);
2184     }
2185
2186     if (appData.showButtonBar) {
2187       widgetList[j++] = buttonBarWidget = CreateButtonBar(buttonBar);
2188       XtSetArg(args[0], XtNleft,  XtChainRight); // [HGM] glue to right window edge
2189       XtSetArg(args[1], XtNright, XtChainRight); //       for good run-time sizing
2190       XtSetArg(args[2], XtNtop,    XtChainTop);
2191       XtSetArg(args[3], XtNbottom, XtChainTop);
2192       XtSetValues(buttonBarWidget, args, 4);
2193     }
2194
2195     widgetList[j++] = messageWidget =
2196       XtCreateWidget("message", labelWidgetClass, formWidget,
2197                      messageArgs, XtNumber(messageArgs));
2198     XtSetArg(args[0], XtNtop,    XtChainTop);
2199     XtSetArg(args[1], XtNbottom, XtChainTop);
2200     XtSetValues(messageWidget, args, 2);
2201
2202     widgetList[j++] = boardWidget =
2203       XtCreateWidget("board", widgetClass, formWidget, boardArgs,
2204                      XtNumber(boardArgs));
2205
2206     XtManageChildren(widgetList, j);
2207
2208     timerWidth = (boardWidth - sep) / 2;
2209     XtSetArg(args[0], XtNwidth, timerWidth);
2210     XtSetValues(whiteTimerWidget, args, 1);
2211     XtSetValues(blackTimerWidget, args, 1);
2212
2213     XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
2214     XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
2215     XtGetValues(whiteTimerWidget, args, 2);
2216
2217     if (appData.showButtonBar) {
2218       XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
2219       XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
2220       XtGetValues(XtNameToWidget(buttonBarWidget, PAUSE_BUTTON), args, 2);
2221     }
2222
2223     /*
2224      * formWidget uses these constraints but they are stored
2225      * in the children.
2226      */
2227     i = 0;
2228     XtSetArg(args[i], XtNfromHoriz, 0); i++;
2229     XtSetValues(menuBarWidget, args, i);
2230     if (appData.titleInWindow) {
2231         if (smallLayout) {
2232             i = 0;
2233             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2234             XtSetValues(whiteTimerWidget, args, i);
2235             i = 0;
2236             XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2237             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2238             XtSetValues(blackTimerWidget, args, i);
2239             i = 0;
2240             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2241             XtSetArg(args[i], XtNjustify, XtJustifyLeft); i++;
2242             XtSetValues(titleWidget, args, i);
2243             i = 0;
2244             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2245             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2246             XtSetValues(messageWidget, args, i);
2247             if (appData.showButtonBar) {
2248               i = 0;
2249               XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2250               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2251               XtSetValues(buttonBarWidget, args, i);
2252             }
2253         } else {
2254             i = 0;
2255             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2256             XtSetValues(whiteTimerWidget, args, i);
2257             i = 0;
2258             XtSetArg(args[i], XtNfromVert, titleWidget); i++;
2259             XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2260             XtSetValues(blackTimerWidget, args, i);
2261             i = 0;
2262             XtSetArg(args[i], XtNfromHoriz, menuBarWidget); i++;
2263             XtSetValues(titleWidget, args, i);
2264             i = 0;
2265             XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2266             XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2267             XtSetValues(messageWidget, args, i);
2268             if (appData.showButtonBar) {
2269               i = 0;
2270               XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2271               XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2272               XtSetValues(buttonBarWidget, args, i);
2273             }
2274         }
2275     } else {
2276         i = 0;
2277         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2278         XtSetValues(whiteTimerWidget, args, i);
2279         i = 0;
2280         XtSetArg(args[i], XtNfromVert, menuBarWidget); i++;
2281         XtSetArg(args[i], XtNfromHoriz, whiteTimerWidget); i++;
2282         XtSetValues(blackTimerWidget, args, i);
2283         i = 0;
2284         XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2285         XtSetArg(args[i], XtNresizable, (XtArgVal) True); i++;
2286         XtSetValues(messageWidget, args, i);
2287         if (appData.showButtonBar) {
2288           i = 0;
2289           XtSetArg(args[i], XtNfromVert, whiteTimerWidget); i++;
2290           XtSetArg(args[i], XtNfromHoriz, messageWidget); i++;
2291           XtSetValues(buttonBarWidget, args, i);
2292         }
2293     }
2294     i = 0;
2295     XtSetArg(args[0], XtNfromVert, messageWidget);
2296     XtSetArg(args[1], XtNtop,    XtChainTop);
2297     XtSetArg(args[2], XtNbottom, XtChainBottom);
2298     XtSetArg(args[3], XtNleft,   XtChainLeft);
2299     XtSetArg(args[4], XtNright,  XtChainRight);
2300     XtSetValues(boardWidget, args, 5);
2301
2302     XtRealizeWidget(shellWidget);
2303
2304     if(wpMain.x > 0) {
2305       XtSetArg(args[0], XtNx, wpMain.x);
2306       XtSetArg(args[1], XtNy, wpMain.y);
2307       XtSetValues(shellWidget, args, 2);
2308     }
2309
2310     /*
2311      * Correct the width of the message and title widgets.
2312      * It is not known why some systems need the extra fudge term.
2313      * The value "2" is probably larger than needed.
2314      */
2315     XawFormDoLayout(formWidget, False);
2316
2317 #define WIDTH_FUDGE 2
2318     i = 0;
2319     XtSetArg(args[i], XtNborderWidth, &bor);  i++;
2320     XtSetArg(args[i], XtNheight, &h);  i++;
2321     XtGetValues(messageWidget, args, i);
2322     if (appData.showButtonBar) {
2323       i = 0;
2324       XtSetArg(args[i], XtNwidth, &w);  i++;
2325       XtGetValues(buttonBarWidget, args, i);
2326       w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2327     } else {
2328       w = boardWidth - 2*bor + 1; /*!! +1 compensates for kludge below */
2329     }
2330
2331     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2332     if (gres != XtGeometryYes && appData.debugMode) {
2333       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2334               programName, gres, w, h, wr, hr);
2335     }
2336
2337     /* !! Horrible hack to work around bug in XFree86 4.0.1 (X11R6.4.3) */
2338     /* The size used for the child widget in layout lags one resize behind
2339        its true size, so we resize a second time, 1 pixel smaller.  Yeech! */
2340     w--;
2341     gres = XtMakeResizeRequest(messageWidget, w, h, &wr, &hr);
2342     if (gres != XtGeometryYes && appData.debugMode) {
2343       fprintf(stderr, _("%s: messageWidget geometry error %d %d %d %d %d\n"),
2344               programName, gres, w, h, wr, hr);
2345     }
2346     /* !! end hack */
2347     XtSetArg(args[0], XtNleft,  XtChainLeft);  // [HGM] glue ends for good run-time sizing
2348     XtSetArg(args[1], XtNright, XtChainRight);
2349     XtSetValues(messageWidget, args, 2);
2350
2351     if (appData.titleInWindow) {
2352         i = 0;
2353         XtSetArg(args[i], XtNborderWidth, &bor); i++;
2354         XtSetArg(args[i], XtNheight, &h);  i++;
2355         XtGetValues(titleWidget, args, i);
2356         if (smallLayout) {
2357             w = boardWidth - 2*bor;
2358         } else {
2359             XtSetArg(args[0], XtNwidth, &w);
2360             XtGetValues(menuBarWidget, args, 1);
2361             w = boardWidth - w - sep - 2*bor - WIDTH_FUDGE;
2362         }
2363
2364         gres = XtMakeResizeRequest(titleWidget, w, h, &wr, &hr);
2365         if (gres != XtGeometryYes && appData.debugMode) {
2366             fprintf(stderr,
2367                     _("%s: titleWidget geometry error %d %d %d %d %d\n"),
2368                     programName, gres, w, h, wr, hr);
2369         }
2370     }
2371     XawFormDoLayout(formWidget, True);
2372
2373     xBoardWindow = XtWindow(boardWidget);
2374
2375     // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
2376     //       not need to go into InitDrawingSizes().
2377 #endif
2378
2379     /*
2380      * Create X checkmark bitmap and initialize option menu checks.
2381      */
2382     ReadBitmap(&xMarkPixmap, "checkmark.bm",
2383                checkmark_bits, checkmark_width, checkmark_height);
2384     XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
2385     if (appData.alwaysPromoteToQueen) {
2386         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Always Queen"),
2387                     args, 1);
2388     }
2389     if (appData.animateDragging) {
2390         XtSetValues(XtNameToWidget(menuBarWidget,
2391                                    "menuOptions.Animate Dragging"),
2392                     args, 1);
2393     }
2394     if (appData.animate) {
2395         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Animate Moving"),
2396                     args, 1);
2397     }
2398     if (appData.autoComment) {
2399         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Comment"),
2400                     args, 1);
2401     }
2402     if (appData.autoCallFlag) {
2403         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Flag"),
2404                     args, 1);
2405     }
2406     if (appData.autoFlipView) {
2407         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Auto Flip View"),
2408                     args, 1);
2409     }
2410     if (appData.autoObserve) {
2411         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Observe"),
2412                     args, 1);
2413     }
2414     if (appData.autoRaiseBoard) {
2415         XtSetValues(XtNameToWidget(menuBarWidget,
2416                                    "menuOptions.Auto Raise Board"), args, 1);
2417     }
2418     if (appData.autoSaveGames) {
2419         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2420                     args, 1);
2421     }
2422     if (appData.saveGameFile[0] != NULLCHAR) {
2423         /* Can't turn this off from menu */
2424         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2425                     args, 1);
2426         XtSetSensitive(XtNameToWidget(menuBarWidget, "menuOptions.Auto Save"),
2427                        False);
2428
2429     }
2430     if (appData.blindfold) {
2431         XtSetValues(XtNameToWidget(menuBarWidget,
2432                                    "menuOptions.Blindfold"), args, 1);
2433     }
2434     if (appData.flashCount > 0) {
2435         XtSetValues(XtNameToWidget(menuBarWidget,
2436                                    "menuOptions.Flash Moves"),
2437                     args, 1);
2438     }
2439     if (appData.getMoveList) {
2440         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Get Move List"),
2441                     args, 1);
2442     }
2443 #if HIGHDRAG
2444     if (appData.highlightDragging) {
2445         XtSetValues(XtNameToWidget(menuBarWidget,
2446                                    "menuOptions.Highlight Dragging"),
2447                     args, 1);
2448     }
2449 #endif
2450     if (appData.highlightLastMove) {
2451         XtSetValues(XtNameToWidget(menuBarWidget,
2452                                    "menuOptions.Highlight Last Move"),
2453                     args, 1);
2454     }
2455     if (appData.icsAlarm) {
2456         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
2457                     args, 1);
2458     }
2459     if (appData.ringBellAfterMoves) {
2460         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
2461                     args, 1);
2462     }
2463     if (appData.oldSaveStyle) {
2464         XtSetValues(XtNameToWidget(menuBarWidget,
2465                                    "menuOptions.Old Save Style"), args, 1);
2466     }
2467     if (appData.periodicUpdates) {
2468         XtSetValues(XtNameToWidget(menuBarWidget,
2469                                    "menuOptions.Periodic Updates"), args, 1);
2470     }
2471     if (appData.ponderNextMove) {
2472         XtSetValues(XtNameToWidget(menuBarWidget,
2473                                    "menuOptions.Ponder Next Move"), args, 1);
2474     }
2475     if (appData.popupExitMessage) {
2476         XtSetValues(XtNameToWidget(menuBarWidget,
2477                                    "menuOptions.Popup Exit Message"), args, 1);
2478     }
2479     if (appData.popupMoveErrors) {
2480         XtSetValues(XtNameToWidget(menuBarWidget,
2481                                    "menuOptions.Popup Move Errors"), args, 1);
2482     }
2483     if (appData.premove) {
2484         XtSetValues(XtNameToWidget(menuBarWidget,
2485                                    "menuOptions.Premove"), args, 1);
2486     }
2487     if (appData.quietPlay) {
2488         XtSetValues(XtNameToWidget(menuBarWidget,
2489                                    "menuOptions.Quiet Play"), args, 1);
2490     }
2491     if (appData.showCoords) {
2492         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Show Coords"),
2493                     args, 1);
2494     }
2495     if (appData.hideThinkingFromHuman) {
2496         XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Hide Thinking"),
2497                     args, 1);
2498     }
2499     if (appData.testLegality) {
2500         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Test Legality"),
2501                     args, 1);
2502     }
2503     if (saveSettingsOnExit) {
2504         XtSetValues(XtNameToWidget(menuBarWidget,"menuOptions.Save Settings on Exit"),
2505                     args, 1);
2506     }
2507
2508     /*
2509      * Create an icon.
2510      */
2511     ReadBitmap(&wIconPixmap, "icon_white.bm",
2512                icon_white_bits, icon_white_width, icon_white_height);
2513     ReadBitmap(&bIconPixmap, "icon_black.bm",
2514                icon_black_bits, icon_black_width, icon_black_height);
2515     iconPixmap = wIconPixmap;
2516     i = 0;
2517     XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
2518     XtSetValues(shellWidget, args, i);
2519
2520     /*
2521      * Create a cursor for the board widget.
2522      */
2523     window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
2524     XChangeWindowAttributes(xDisplay, xBoardWindow,
2525                             CWCursor, &window_attributes);
2526
2527     /*
2528      * Inhibit shell resizing.
2529      */
2530     shellArgs[0].value = (XtArgVal) &w;
2531     shellArgs[1].value = (XtArgVal) &h;
2532     XtGetValues(shellWidget, shellArgs, 2);
2533     shellArgs[4].value = shellArgs[2].value = w;
2534     shellArgs[5].value = shellArgs[3].value = h;
2535     XtSetValues(shellWidget, &shellArgs[2], 4);
2536     marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
2537     marginH =  h - boardHeight;
2538
2539     CatchDeleteWindow(shellWidget, "QuitProc");
2540
2541     CreateGCs();
2542     CreateGrid();
2543 #if HAVE_LIBXPM
2544     if (appData.bitmapDirectory[0] != NULLCHAR) {
2545       CreatePieces();
2546     } else {
2547       CreateXPMPieces();
2548       CreateXPMBoard(appData.liteBackTextureFile, 1);
2549       CreateXPMBoard(appData.darkBackTextureFile, 0);
2550     }
2551 #else
2552     CreateXIMPieces();
2553     /* Create regular pieces */
2554     if (!useImages) CreatePieces();
2555 #endif
2556
2557     CreatePieceMenus();
2558
2559     if (appData.animate || appData.animateDragging)
2560       CreateAnimVars();
2561
2562     XtAugmentTranslations(formWidget,
2563                           XtParseTranslationTable(globalTranslations));
2564     XtAugmentTranslations(boardWidget,
2565                           XtParseTranslationTable(boardTranslations));
2566     XtAugmentTranslations(whiteTimerWidget,
2567                           XtParseTranslationTable(whiteTranslations));
2568     XtAugmentTranslations(blackTimerWidget,
2569                           XtParseTranslationTable(blackTranslations));
2570
2571     /* Why is the following needed on some versions of X instead
2572      * of a translation? */
2573     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
2574                       (XtEventHandler) EventProc, NULL);
2575     /* end why */
2576
2577     /* [AS] Restore layout */
2578     if( wpMoveHistory.visible ) {
2579       HistoryPopUp();
2580     }
2581
2582     if( wpEvalGraph.visible )
2583       {
2584         EvalGraphPopUp();
2585       };
2586
2587     if( wpEngineOutput.visible ) {
2588       EngineOutputPopUp();
2589     }
2590
2591     InitBackEnd2();
2592
2593     if (errorExitStatus == -1) {
2594         if (appData.icsActive) {
2595             /* We now wait until we see "login:" from the ICS before
2596                sending the logon script (problems with timestamp otherwise) */
2597             /*ICSInitScript();*/
2598             if (appData.icsInputBox) ICSInputBoxPopUp();
2599         }
2600
2601     #ifdef SIGWINCH
2602     signal(SIGWINCH, TermSizeSigHandler);
2603     #endif
2604         signal(SIGINT, IntSigHandler);
2605         signal(SIGTERM, IntSigHandler);
2606         if (*appData.cmailGameName != NULLCHAR) {
2607             signal(SIGUSR1, CmailSigHandler);
2608         }
2609     }
2610     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
2611     InitPosition(TRUE);
2612     XtSetKeyboardFocus(shellWidget, formWidget);
2613
2614     XtAppMainLoop(appContext);
2615     if (appData.debugMode) fclose(debugFP); // [DM] debug
2616     return 0;
2617 }
2618
2619 void
2620 ShutDownFrontEnd()
2621 {
2622     if (appData.icsActive && oldICSInteractionTitle != NULL) {
2623         DisplayIcsInteractionTitle(oldICSInteractionTitle);
2624     }
2625     if (saveSettingsOnExit) SaveSettings(settingsFileName);
2626     unlink(gameCopyFilename);
2627     unlink(gamePasteFilename);
2628
2629 # if HAVE_LIBREADLINE
2630     /* remove gnu-readline handler.  */
2631     rl_callback_handler_remove();
2632 #endif
2633
2634     return;
2635 }
2636
2637 RETSIGTYPE TermSizeSigHandler(int sig)
2638 {
2639     update_ics_width();
2640 }
2641
2642 RETSIGTYPE
2643 IntSigHandler(sig)
2644      int sig;
2645 {
2646     ExitEvent(sig);
2647 }
2648
2649 RETSIGTYPE
2650 CmailSigHandler(sig)
2651      int sig;
2652 {
2653     int dummy = 0;
2654     int error;
2655
2656     signal(SIGUSR1, SIG_IGN);   /* suspend handler     */
2657
2658     /* Activate call-back function CmailSigHandlerCallBack()             */
2659     OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
2660
2661     signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
2662 }
2663
2664 void
2665 CmailSigHandlerCallBack(isr, closure, message, count, error)
2666      InputSourceRef isr;
2667      VOIDSTAR closure;
2668      char *message;
2669      int count;
2670      int error;
2671 {
2672     BoardToTop();
2673     ReloadCmailMsgEvent(TRUE);  /* Reload cmail msg  */
2674 }
2675 /**** end signal code ****/
2676
2677
2678 void
2679 ICSInitScript()
2680 {
2681   /* try to open the icsLogon script, either in the location given
2682    * or in the users HOME directory
2683    */
2684
2685   FILE *f;
2686   char buf[MSG_SIZ];
2687   char *homedir;
2688
2689   f = fopen(appData.icsLogon, "r");
2690   if (f == NULL)
2691     {
2692       homedir = getenv("HOME");
2693       if (homedir != NULL)
2694         {
2695           safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
2696           strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
2697           strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
2698           f = fopen(buf, "r");
2699         }
2700     }
2701
2702   if (f != NULL)
2703     ProcessICSInitScript(f);
2704   else
2705     printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
2706
2707   return;
2708 }
2709
2710 void
2711 ResetFrontEnd()
2712 {
2713     CommentPopDown();
2714     EditCommentPopDown();
2715     TagsPopDown();
2716     return;
2717 }
2718
2719 typedef struct {
2720     char *name;
2721     Boolean value;
2722 } Enables;
2723
2724 void
2725 GreyRevert(grey)
2726      Boolean grey;
2727 {
2728     Widget w;
2729     if (!menuBarWidget) return;
2730     w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
2731     if (w == NULL) {
2732       DisplayError("menuStep.Revert", 0);
2733     } else {
2734       XtSetSensitive(w, !grey);
2735     }
2736     w = XtNameToWidget(menuBarWidget, "menuStep.Annotate");
2737     if (w == NULL) {
2738       DisplayError("menuStep.Annotate", 0);
2739     } else {
2740       XtSetSensitive(w, !grey);
2741     }
2742 }
2743
2744 void
2745 SetMenuEnables(enab)
2746      Enables *enab;
2747 {
2748   Widget w;
2749   if (!menuBarWidget) return;
2750   while (enab->name != NULL) {
2751     w = XtNameToWidget(menuBarWidget, enab->name);
2752     if (w == NULL) {
2753       DisplayError(enab->name, 0);
2754     } else {
2755       XtSetSensitive(w, enab->value);
2756     }
2757     enab++;
2758   }
2759 }
2760
2761 Enables icsEnables[] = {
2762     { "menuFile.Mail Move", False },
2763     { "menuFile.Reload CMail Message", False },
2764     { "menuMode.Machine Black", False },
2765     { "menuMode.Machine White", False },
2766     { "menuMode.Analysis Mode", False },
2767     { "menuMode.Analyze File", False },
2768     { "menuMode.Two Machines", False },
2769 #ifndef ZIPPY
2770     { "menuHelp.Hint", False },
2771     { "menuHelp.Book", False },
2772     { "menuStep.Move Now", False },
2773     { "menuOptions.Periodic Updates", False },
2774     { "menuOptions.Hide Thinking", False },
2775     { "menuOptions.Ponder Next Move", False },
2776 #endif
2777     { "menuStep.Annotate", False },
2778     { NULL, False }
2779 };
2780
2781 Enables ncpEnables[] = {
2782     { "menuFile.Mail Move", False },
2783     { "menuFile.Reload CMail Message", False },
2784     { "menuMode.Machine White", False },
2785     { "menuMode.Machine Black", False },
2786     { "menuMode.Analysis Mode", False },
2787     { "menuMode.Analyze File", False },
2788     { "menuMode.Two Machines", False },
2789     { "menuMode.ICS Client", False },
2790     { "menuMode.ICS Input Box", False },
2791     { "Action", False },
2792     { "menuStep.Revert", False },
2793     { "menuStep.Annotate", False },
2794     { "menuStep.Move Now", False },
2795     { "menuStep.Retract Move", False },
2796     { "menuOptions.Auto Comment", False },
2797     { "menuOptions.Auto Flag", False },
2798     { "menuOptions.Auto Flip View", False },
2799     { "menuOptions.Auto Observe", False },
2800     { "menuOptions.Auto Raise Board", False },
2801     { "menuOptions.Get Move List", False },
2802     { "menuOptions.ICS Alarm", False },
2803     { "menuOptions.Move Sound", False },
2804     { "menuOptions.Quiet Play", False },
2805     { "menuOptions.Hide Thinking", False },
2806     { "menuOptions.Periodic Updates", False },
2807     { "menuOptions.Ponder Next Move", False },
2808     { "menuHelp.Hint", False },
2809     { "menuHelp.Book", False },
2810     { NULL, False }
2811 };
2812
2813 Enables gnuEnables[] = {
2814     { "menuMode.ICS Client", False },
2815     { "menuMode.ICS Input Box", False },
2816     { "menuAction.Accept", False },
2817     { "menuAction.Decline", False },
2818     { "menuAction.Rematch", False },
2819     { "menuAction.Adjourn", False },
2820     { "menuAction.Stop Examining", False },
2821     { "menuAction.Stop Observing", False },
2822     { "menuAction.Upload to Examine", False },
2823     { "menuStep.Revert", False },
2824     { "menuStep.Annotate", False },
2825     { "menuOptions.Auto Comment", False },
2826     { "menuOptions.Auto Observe", False },
2827     { "menuOptions.Auto Raise Board", False },
2828     { "menuOptions.Get Move List", False },
2829     { "menuOptions.Premove", False },
2830     { "menuOptions.Quiet Play", False },
2831
2832     /* The next two options rely on SetCmailMode being called *after*    */
2833     /* SetGNUMode so that when GNU is being used to give hints these     */
2834     /* menu options are still available                                  */
2835
2836     { "menuFile.Mail Move", False },
2837     { "menuFile.Reload CMail Message", False },
2838     { NULL, False }
2839 };
2840
2841 Enables cmailEnables[] = {
2842     { "Action", True },
2843     { "menuAction.Call Flag", False },
2844     { "menuAction.Draw", True },
2845     { "menuAction.Adjourn", False },
2846     { "menuAction.Abort", False },
2847     { "menuAction.Stop Observing", False },
2848     { "menuAction.Stop Examining", False },
2849     { "menuFile.Mail Move", True },
2850     { "menuFile.Reload CMail Message", True },
2851     { NULL, False }
2852 };
2853
2854 Enables trainingOnEnables[] = {
2855   { "menuMode.Edit Comment", False },
2856   { "menuMode.Pause", False },
2857   { "menuStep.Forward", False },
2858   { "menuStep.Backward", False },
2859   { "menuStep.Forward to End", False },
2860   { "menuStep.Back to Start", False },
2861   { "menuStep.Move Now", False },
2862   { "menuStep.Truncate Game", False },
2863   { NULL, False }
2864 };
2865
2866 Enables trainingOffEnables[] = {
2867   { "menuMode.Edit Comment", True },
2868   { "menuMode.Pause", True },
2869   { "menuStep.Forward", True },
2870   { "menuStep.Backward", True },
2871   { "menuStep.Forward to End", True },
2872   { "menuStep.Back to Start", True },
2873   { "menuStep.Move Now", True },
2874   { "menuStep.Truncate Game", True },
2875   { NULL, False }
2876 };
2877
2878 Enables machineThinkingEnables[] = {
2879   { "menuFile.Load Game", False },
2880   { "menuFile.Load Next Game", False },
2881   { "menuFile.Load Previous Game", False },
2882   { "menuFile.Reload Same Game", False },
2883   { "menuFile.Paste Game", False },
2884   { "menuFile.Load Position", False },
2885   { "menuFile.Load Next Position", False },
2886   { "menuFile.Load Previous Position", False },
2887   { "menuFile.Reload Same Position", False },
2888   { "menuFile.Paste Position", False },
2889   { "menuMode.Machine White", False },
2890   { "menuMode.Machine Black", False },
2891   { "menuMode.Two Machines", False },
2892   { "menuStep.Retract Move", False },
2893   { NULL, False }
2894 };
2895
2896 Enables userThinkingEnables[] = {
2897   { "menuFile.Load Game", True },
2898   { "menuFile.Load Next Game", True },
2899   { "menuFile.Load Previous Game", True },
2900   { "menuFile.Reload Same Game", True },
2901   { "menuFile.Paste Game", True },
2902   { "menuFile.Load Position", True },
2903   { "menuFile.Load Next Position", True },
2904   { "menuFile.Load Previous Position", True },
2905   { "menuFile.Reload Same Position", True },
2906   { "menuFile.Paste Position", True },
2907   { "menuMode.Machine White", True },
2908   { "menuMode.Machine Black", True },
2909   { "menuMode.Two Machines", True },
2910   { "menuStep.Retract Move", True },
2911   { NULL, False }
2912 };
2913
2914 void SetICSMode()
2915 {
2916   SetMenuEnables(icsEnables);
2917
2918 #if ZIPPY
2919   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
2920      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
2921 #endif
2922 }
2923
2924 void
2925 SetNCPMode()
2926 {
2927   SetMenuEnables(ncpEnables);
2928 }
2929
2930 void
2931 SetGNUMode()
2932 {
2933   SetMenuEnables(gnuEnables);
2934 }
2935
2936 void
2937 SetCmailMode()
2938 {
2939   SetMenuEnables(cmailEnables);
2940 }
2941
2942 void
2943 SetTrainingModeOn()
2944 {
2945   SetMenuEnables(trainingOnEnables);
2946   if (appData.showButtonBar) {
2947     XtSetSensitive(buttonBarWidget, False);
2948   }
2949   CommentPopDown();
2950 }
2951
2952 void
2953 SetTrainingModeOff()
2954 {
2955   SetMenuEnables(trainingOffEnables);
2956   if (appData.showButtonBar) {
2957     XtSetSensitive(buttonBarWidget, True);
2958   }
2959 }
2960
2961 void
2962 SetUserThinkingEnables()
2963 {
2964   if (appData.noChessProgram) return;
2965   SetMenuEnables(userThinkingEnables);
2966 }
2967
2968 void
2969 SetMachineThinkingEnables()
2970 {
2971   if (appData.noChessProgram) return;
2972   SetMenuEnables(machineThinkingEnables);
2973   switch (gameMode) {
2974   case MachinePlaysBlack:
2975   case MachinePlaysWhite:
2976   case TwoMachinesPlay:
2977     XtSetSensitive(XtNameToWidget(menuBarWidget,
2978                                   ModeToWidgetName(gameMode)), True);
2979     break;
2980   default:
2981     break;
2982   }
2983 }
2984
2985 // [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
2986 #define HISTORY_SIZE 64\r
2987 static char *history[HISTORY_SIZE];\r
2988 int histIn = 0, histP = 0;\r
2989 \r
2990 void\r
2991 SaveInHistory(char *cmd)\r
2992 {\r
2993   if (history[histIn] != NULL) {\r
2994     free(history[histIn]);\r
2995     history[histIn] = NULL;\r
2996   }\r
2997   if (*cmd == NULLCHAR) return;\r
2998   history[histIn] = StrSave(cmd);\r
2999   histIn = (histIn + 1) % HISTORY_SIZE;\r
3000   if (history[histIn] != NULL) {\r
3001     free(history[histIn]);\r
3002     history[histIn] = NULL;\r
3003   }\r
3004   histP = histIn;\r
3005 }\r
3006 \r
3007 char *\r
3008 PrevInHistory(char *cmd)\r
3009 {\r
3010   int newhp;\r
3011   if (histP == histIn) {\r
3012     if (history[histIn] != NULL) free(history[histIn]);\r
3013     history[histIn] = StrSave(cmd);\r
3014   }\r
3015   newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;\r
3016   if (newhp == histIn || history[newhp] == NULL) return NULL;\r
3017   histP = newhp;\r
3018   return history[histP];\r
3019 }\r
3020 \r
3021 char *\r
3022 NextInHistory()\r
3023 {\r
3024   if (histP == histIn) return NULL;\r
3025   histP = (histP + 1) % HISTORY_SIZE;\r
3026   return history[histP];   \r
3027 }
3028 // end of borrowed code\r
3029 \r
3030 #define Abs(n) ((n)<0 ? -(n) : (n))
3031
3032 /*
3033  * Find a font that matches "pattern" that is as close as
3034  * possible to the targetPxlSize.  Prefer fonts that are k
3035  * pixels smaller to fonts that are k pixels larger.  The
3036  * pattern must be in the X Consortium standard format,
3037  * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
3038  * The return value should be freed with XtFree when no
3039  * longer needed.
3040  */
3041 char *
3042 FindFont(pattern, targetPxlSize)
3043      char *pattern;
3044      int targetPxlSize;
3045 {
3046     char **fonts, *p, *best, *scalable, *scalableTail;
3047     int i, j, nfonts, minerr, err, pxlSize;
3048
3049 #ifdef ENABLE_NLS
3050     char **missing_list;
3051     int missing_count;
3052     char *def_string, *base_fnt_lst, strInt[3];
3053     XFontSet fntSet;
3054     XFontStruct **fnt_list;
3055
3056     base_fnt_lst = calloc(1, strlen(pattern) + 3);
3057     snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
3058     p = strstr(pattern, "--");
3059     strncpy(base_fnt_lst, pattern, p - pattern + 2);
3060     strcat(base_fnt_lst, strInt);
3061     strcat(base_fnt_lst, strchr(p + 2, '-'));
3062
3063     if ((fntSet = XCreateFontSet(xDisplay,
3064                                  base_fnt_lst,
3065                                  &missing_list,
3066                                  &missing_count,
3067                                  &def_string)) == NULL) {
3068
3069        fprintf(stderr, _("Unable to create font set.\n"));
3070        exit (2);
3071     }
3072
3073     nfonts = XFontsOfFontSet(fntSet, &fnt_list, &fonts);
3074 #else
3075     fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
3076     if (nfonts < 1) {
3077         fprintf(stderr, _("%s: no fonts match pattern %s\n"),
3078                 programName, pattern);
3079         exit(2);
3080     }
3081 #endif
3082
3083     best = fonts[0];
3084     scalable = NULL;
3085     minerr = 999999;
3086     for (i=0; i<nfonts; i++) {
3087         j = 0;
3088         p = fonts[i];
3089         if (*p != '-') continue;
3090         while (j < 7) {
3091             if (*p == NULLCHAR) break;
3092             if (*p++ == '-') j++;
3093         }
3094         if (j < 7) continue;
3095         pxlSize = atoi(p);
3096         if (pxlSize == 0) {
3097             scalable = fonts[i];
3098             scalableTail = p;
3099         } else {
3100             err = pxlSize - targetPxlSize;
3101             if (Abs(err) < Abs(minerr) ||
3102                 (minerr > 0 && err < 0 && -err == minerr)) {
3103                 best = fonts[i];
3104                 minerr = err;
3105             }
3106         }
3107     }
3108     if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
3109         /* If the error is too big and there is a scalable font,
3110            use the scalable font. */
3111         int headlen = scalableTail - scalable;
3112         p = (char *) XtMalloc(strlen(scalable) + 10);
3113         while (isdigit(*scalableTail)) scalableTail++;
3114         sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
3115     } else {
3116         p = (char *) XtMalloc(strlen(best) + 2);
3117         safeStrCpy(p, best, strlen(best)+1 );
3118     }
3119     if (appData.debugMode) {
3120         fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
3121                 pattern, targetPxlSize, p);
3122     }
3123 #ifdef ENABLE_NLS
3124     if (missing_count > 0)
3125        XFreeStringList(missing_list);
3126     XFreeFontSet(xDisplay, fntSet);
3127 #else
3128      XFreeFontNames(fonts);
3129 #endif
3130     return p;
3131 }
3132
3133 void CreateGCs()
3134 {
3135     XtGCMask value_mask = GCLineWidth | GCLineStyle | GCForeground
3136       | GCBackground | GCFunction | GCPlaneMask;
3137     XGCValues gc_values;
3138     GC copyInvertedGC;
3139
3140     gc_values.plane_mask = AllPlanes;
3141     gc_values.line_width = lineGap;
3142     gc_values.line_style = LineSolid;
3143     gc_values.function = GXcopy;
3144
3145     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3146     gc_values.background = XBlackPixel(xDisplay, xScreen);
3147     lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3148
3149     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3150     gc_values.background = XWhitePixel(xDisplay, xScreen);
3151     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
3152     XSetFont(xDisplay, coordGC, coordFontID);
3153
3154     // [HGM] make font for holdings counts (white on black0
3155     gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3156     gc_values.background = XBlackPixel(xDisplay, xScreen);
3157     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
3158     XSetFont(xDisplay, countGC, countFontID);
3159
3160     if (appData.monoMode) {
3161         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3162         gc_values.background = XWhitePixel(xDisplay, xScreen);
3163         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3164
3165         gc_values.foreground = XWhitePixel(xDisplay, xScreen);
3166         gc_values.background = XBlackPixel(xDisplay, xScreen);
3167         lightSquareGC = wbPieceGC
3168           = XtGetGC(shellWidget, value_mask, &gc_values);
3169
3170         gc_values.foreground = XBlackPixel(xDisplay, xScreen);
3171         gc_values.background = XWhitePixel(xDisplay, xScreen);
3172         darkSquareGC = bwPieceGC
3173           = XtGetGC(shellWidget, value_mask, &gc_values);
3174
3175         if (DefaultDepth(xDisplay, xScreen) == 1) {
3176             /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
3177             gc_values.function = GXcopyInverted;
3178             copyInvertedGC = XtGetGC(shellWidget, value_mask, &gc_values);
3179             gc_values.function = GXcopy;
3180             if (XBlackPixel(xDisplay, xScreen) == 1) {
3181                 bwPieceGC = darkSquareGC;
3182                 wbPieceGC = copyInvertedGC;
3183             } else {
3184                 bwPieceGC = copyInvertedGC;
3185                 wbPieceGC = lightSquareGC;
3186             }
3187         }
3188     } else {
3189         gc_values.foreground = highlightSquareColor;
3190         gc_values.background = highlightSquareColor;
3191         highlineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3192
3193         gc_values.foreground = premoveHighlightColor;
3194         gc_values.background = premoveHighlightColor;
3195         prelineGC = XtGetGC(shellWidget, value_mask, &gc_values);
3196
3197         gc_values.foreground = lightSquareColor;
3198         gc_values.background = darkSquareColor;
3199         lightSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3200
3201         gc_values.foreground = darkSquareColor;
3202         gc_values.background = lightSquareColor;
3203         darkSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3204
3205         gc_values.foreground = jailSquareColor;
3206         gc_values.background = jailSquareColor;
3207         jailSquareGC = XtGetGC(shellWidget, value_mask, &gc_values);
3208
3209         gc_values.foreground = whitePieceColor;
3210         gc_values.background = darkSquareColor;
3211         wdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3212
3213         gc_values.foreground = whitePieceColor;
3214         gc_values.background = lightSquareColor;
3215         wlPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3216
3217         gc_values.foreground = whitePieceColor;
3218         gc_values.background = jailSquareColor;
3219         wjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3220
3221         gc_values.foreground = blackPieceColor;
3222         gc_values.background = darkSquareColor;
3223         bdPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3224
3225         gc_values.foreground = blackPieceColor;
3226         gc_values.background = lightSquareColor;
3227         blPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3228
3229         gc_values.foreground = blackPieceColor;
3230         gc_values.background = jailSquareColor;
3231         bjPieceGC = XtGetGC(shellWidget, value_mask, &gc_values);
3232     }
3233 }
3234
3235 void loadXIM(xim, xmask, filename, dest, mask)
3236      XImage *xim;
3237      XImage *xmask;
3238      char *filename;
3239      Pixmap *dest;
3240      Pixmap *mask;
3241 {
3242     int x, y, w, h, p;
3243     FILE *fp;
3244     Pixmap temp;
3245     XGCValues   values;
3246     GC maskGC;
3247
3248     fp = fopen(filename, "rb");
3249     if (!fp) {
3250         fprintf(stderr, _("%s: error loading XIM!\n"), programName);
3251         exit(1);
3252     }
3253
3254     w = fgetc(fp);
3255     h = fgetc(fp);
3256
3257     for (y=0; y<h; ++y) {
3258         for (x=0; x<h; ++x) {
3259             p = fgetc(fp);
3260
3261             switch (p) {
3262               case 0:
3263                 XPutPixel(xim, x, y, blackPieceColor);
3264                 if (xmask)
3265                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3266                 break;
3267               case 1:
3268                 XPutPixel(xim, x, y, darkSquareColor);
3269                 if (xmask)
3270                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3271                 break;
3272               case 2:
3273                 XPutPixel(xim, x, y, whitePieceColor);
3274                 if (xmask)
3275                   XPutPixel(xmask, x, y, WhitePixel(xDisplay,xScreen));
3276                 break;
3277               case 3:
3278                 XPutPixel(xim, x, y, lightSquareColor);
3279                 if (xmask)
3280                   XPutPixel(xmask, x, y, BlackPixel(xDisplay,xScreen));
3281                 break;
3282             }
3283         }
3284     }
3285
3286     /* create Pixmap of piece */
3287     *dest = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3288                           w, h, xim->depth);
3289     XPutImage(xDisplay, *dest, lightSquareGC, xim,
3290               0, 0, 0, 0, w, h);
3291
3292     /* create Pixmap of clipmask
3293        Note: We assume the white/black pieces have the same
3294              outline, so we make only 6 masks. This is okay
3295              since the XPM clipmask routines do the same. */
3296     if (xmask) {
3297       temp = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3298                             w, h, xim->depth);
3299       XPutImage(xDisplay, temp, lightSquareGC, xmask,
3300               0, 0, 0, 0, w, h);
3301
3302       /* now create the 1-bit version */
3303       *mask = XCreatePixmap(xDisplay, DefaultRootWindow(xDisplay),
3304                           w, h, 1);
3305
3306       values.foreground = 1;
3307       values.background = 0;
3308
3309       /* Don't use XtGetGC, not read only */
3310       maskGC = XCreateGC(xDisplay, *mask,
3311                     GCForeground | GCBackground, &values);
3312       XCopyPlane(xDisplay, temp, *mask, maskGC,
3313                   0, 0, squareSize, squareSize, 0, 0, 1);
3314       XFreePixmap(xDisplay, temp);
3315     }
3316 }
3317
3318
3319 char pieceBitmapNames[] = "pnbrqfeacwmohijgdvlsukpnsl";
3320
3321 void CreateXIMPieces()
3322 {
3323     int piece, kind;
3324     char buf[MSG_SIZ];
3325     u_int ss;
3326     static char *ximkind[] = { "ll", "ld", "dl", "dd" };
3327     XImage *ximtemp;
3328
3329     ss = squareSize;
3330
3331     /* The XSynchronize calls were copied from CreatePieces.
3332        Not sure if needed, but can't hurt */
3333     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3334                                      buffering bug */
3335
3336     /* temp needed by loadXIM() */
3337     ximtemp = XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3338                  0, 0, ss, ss, AllPlanes, XYPixmap);
3339
3340     if (strlen(appData.pixmapDirectory) == 0) {
3341       useImages = 0;
3342     } else {
3343         useImages = 1;
3344         if (appData.monoMode) {
3345           DisplayFatalError(_("XIM pieces cannot be used in monochrome mode"),
3346                             0, 2);
3347           ExitEvent(2);
3348         }
3349         fprintf(stderr, _("\nLoading XIMs...\n"));
3350         /* Load pieces */
3351         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3352             fprintf(stderr, "%d", piece+1);
3353             for (kind=0; kind<4; kind++) {
3354                 fprintf(stderr, ".");
3355                 snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xim",
3356                         ExpandPathName(appData.pixmapDirectory),
3357                         piece <= (int) WhiteKing ? "" : "w",
3358                         pieceBitmapNames[piece],
3359                         ximkind[kind], ss);
3360                 ximPieceBitmap[kind][piece] =
3361                   XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3362                             0, 0, ss, ss, AllPlanes, XYPixmap);
3363                 if (appData.debugMode)
3364                   fprintf(stderr, _("(File:%s:) "), buf);
3365                 loadXIM(ximPieceBitmap[kind][piece],
3366                         ximtemp, buf,
3367                         &(xpmPieceBitmap2[kind][piece]),
3368                         &(ximMaskPm2[piece]));
3369                 if(piece <= (int)WhiteKing)
3370                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3371             }
3372             fprintf(stderr," ");
3373         }
3374         /* Load light and dark squares */
3375         /* If the LSQ and DSQ pieces don't exist, we will
3376            draw them with solid squares. */
3377         snprintf(buf,sizeof(buf), "%s/lsq%u.xim", ExpandPathName(appData.pixmapDirectory), ss);
3378         if (access(buf, 0) != 0) {
3379             useImageSqs = 0;
3380         } else {
3381             useImageSqs = 1;
3382             fprintf(stderr, _("light square "));
3383             ximLightSquare=
3384               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3385                         0, 0, ss, ss, AllPlanes, XYPixmap);
3386             if (appData.debugMode)
3387               fprintf(stderr, _("(File:%s:) "), buf);
3388
3389             loadXIM(ximLightSquare, NULL, buf, &xpmLightSquare, NULL);
3390             fprintf(stderr, _("dark square "));
3391             snprintf(buf,sizeof(buf), "%s/dsq%u.xim",
3392                     ExpandPathName(appData.pixmapDirectory), ss);
3393             if (appData.debugMode)
3394               fprintf(stderr, _("(File:%s:) "), buf);
3395             ximDarkSquare=
3396               XGetImage(xDisplay, DefaultRootWindow(xDisplay),
3397                         0, 0, ss, ss, AllPlanes, XYPixmap);
3398             loadXIM(ximDarkSquare, NULL, buf, &xpmDarkSquare, NULL);
3399             xpmJailSquare = xpmLightSquare;
3400         }
3401         fprintf(stderr, _("Done.\n"));
3402     }
3403     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
3404 }
3405
3406 #if HAVE_LIBXPM
3407 void CreateXPMBoard(char *s, int kind)
3408 {
3409     XpmAttributes attr;
3410     attr.valuemask = 0;
3411     if(s == NULL || *s == 0 || *s == '*') return;
3412     if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
3413         useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
3414     }
3415 }
3416
3417 void CreateXPMPieces()
3418 {
3419     int piece, kind, r;
3420     char buf[MSG_SIZ];
3421     u_int ss = squareSize;
3422     XpmAttributes attr;
3423     static char *xpmkind[] = { "ll", "ld", "dl", "dd" };
3424     XpmColorSymbol symbols[4];
3425
3426     /* The XSynchronize calls were copied from CreatePieces.
3427        Not sure if needed, but can't hurt */
3428     XSynchronize(xDisplay, True); /* Work-around for xlib/xt buffering bug */
3429
3430     /* Setup translations so piece colors match square colors */
3431     symbols[0].name = "light_piece";
3432     symbols[0].value = appData.whitePieceColor;
3433     symbols[1].name = "dark_piece";
3434     symbols[1].value = appData.blackPieceColor;
3435     symbols[2].name = "light_square";
3436     symbols[2].value = appData.lightSquareColor;
3437     symbols[3].name = "dark_square";
3438     symbols[3].value = appData.darkSquareColor;
3439
3440     attr.valuemask = XpmColorSymbols;
3441     attr.colorsymbols = symbols;
3442     attr.numsymbols = 4;
3443
3444     if (appData.monoMode) {
3445       DisplayFatalError(_("XPM pieces cannot be used in monochrome mode"),
3446                         0, 2);
3447       ExitEvent(2);
3448     }
3449     if (strlen(appData.pixmapDirectory) == 0) {
3450         XpmPieces* pieces = builtInXpms;
3451         useImages = 1;
3452         /* Load pieces */
3453         while (pieces->size != squareSize && pieces->size) pieces++;
3454         if (!pieces->size) {
3455           fprintf(stderr, _("No builtin XPM pieces of size %d\n"), squareSize);
3456           exit(1);
3457         }
3458         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3459             for (kind=0; kind<4; kind++) {
3460
3461                 if ((r=XpmCreatePixmapFromData(xDisplay, xBoardWindow,
3462                                                pieces->xpm[piece][kind],
3463                                                &(xpmPieceBitmap2[kind][piece]),
3464                                                NULL, &attr)) != 0) {
3465                   fprintf(stderr, _("Error %d loading XPM image \"%s\"\n"),
3466                           r, buf);
3467                   exit(1);
3468                 }
3469                 if(piece <= (int) WhiteKing)
3470                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3471             }
3472         }
3473         useImageSqs = 0;
3474         xpmJailSquare = xpmLightSquare;
3475     } else {
3476         useImages = 1;
3477
3478         fprintf(stderr, _("\nLoading XPMs...\n"));
3479
3480         /* Load pieces */
3481         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3482             fprintf(stderr, "%d ", piece+1);
3483             for (kind=0; kind<4; kind++) {
3484               snprintf(buf, sizeof(buf), "%s/%s%c%s%u.xpm",
3485                         ExpandPathName(appData.pixmapDirectory),
3486                         piece > (int) WhiteKing ? "w" : "",
3487                         pieceBitmapNames[piece],
3488                         xpmkind[kind], ss);
3489                 if (appData.debugMode) {
3490                     fprintf(stderr, _("(File:%s:) "), buf);
3491                 }
3492                 if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3493                                            &(xpmPieceBitmap2[kind][piece]),
3494                                            NULL, &attr)) != 0) {
3495                     if(piece != (int)WhiteKing && piece > (int)WhiteQueen) {
3496                       // [HGM] missing: read of unorthodox piece failed; substitute King.
3497                       snprintf(buf, sizeof(buf), "%s/k%s%u.xpm",
3498                                 ExpandPathName(appData.pixmapDirectory),
3499                                 xpmkind[kind], ss);
3500                         if (appData.debugMode) {
3501                             fprintf(stderr, _("(Replace by File:%s:) "), buf);
3502                         }
3503                         r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3504                                                 &(xpmPieceBitmap2[kind][piece]),
3505                                                 NULL, &attr);
3506                     }
3507                     if (r != 0) {
3508                         fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"),
3509                                 r, buf);
3510                         exit(1);
3511                     }
3512                 }
3513                 if(piece <= (int) WhiteKing)
3514                     xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
3515             }
3516         }
3517         /* Load light and dark squares */
3518         /* If the LSQ and DSQ pieces don't exist, we will
3519            draw them with solid squares. */
3520         fprintf(stderr, _("light square "));
3521         snprintf(buf, sizeof(buf), "%s/lsq%u.xpm", ExpandPathName(appData.pixmapDirectory), ss);
3522         if (access(buf, 0) != 0) {
3523             useImageSqs = 0;
3524         } else {
3525             useImageSqs = 1;
3526             if (appData.debugMode)
3527               fprintf(stderr, _("(File:%s:) "), buf);
3528
3529             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3530                                        &xpmLightSquare, NULL, &attr)) != 0) {
3531                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3532                 exit(1);
3533             }
3534             fprintf(stderr, _("dark square "));
3535             snprintf(buf, sizeof(buf), "%s/dsq%u.xpm",
3536                     ExpandPathName(appData.pixmapDirectory), ss);
3537             if (appData.debugMode) {
3538                 fprintf(stderr, _("(File:%s:) "), buf);
3539             }
3540             if ((r=XpmReadFileToPixmap(xDisplay, xBoardWindow, buf,
3541                                        &xpmDarkSquare, NULL, &attr)) != 0) {
3542                 fprintf(stderr, _("Error %d loading XPM file \"%s\"\n"), r, buf);
3543                 exit(1);
3544             }
3545         }
3546         xpmJailSquare = xpmLightSquare;
3547         fprintf(stderr, _("Done.\n"));
3548     }
3549     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3550                                       buffering bug */
3551 }
3552 #endif /* HAVE_LIBXPM */
3553
3554 #if HAVE_LIBXPM
3555 /* No built-in bitmaps */
3556 void CreatePieces()
3557 {
3558     int piece, kind;
3559     char buf[MSG_SIZ];
3560     u_int ss = squareSize;
3561
3562     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3563                                      buffering bug */
3564
3565     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3566         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3567           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3568                    pieceBitmapNames[piece],
3569                    ss, kind == SOLID ? 's' : 'o');
3570           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
3571           if(piece <= (int)WhiteKing)
3572             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3573         }
3574     }
3575
3576     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3577                                       buffering bug */
3578 }
3579 #else
3580 /* With built-in bitmaps */
3581 void CreatePieces()
3582 {
3583     BuiltInBits* bib = builtInBits;
3584     int piece, kind;
3585     char buf[MSG_SIZ];
3586     u_int ss = squareSize;
3587
3588     XSynchronize(xDisplay, True); /* Work-around for xlib/xt
3589                                      buffering bug */
3590
3591     while (bib->squareSize != ss && bib->squareSize != 0) bib++;
3592
3593     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
3594         for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
3595           snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
3596                    pieceBitmapNames[piece],
3597                    ss, kind == SOLID ? 's' : 'o');
3598           ReadBitmap(&pieceBitmap2[kind][piece], buf,
3599                      bib->bits[kind][piece], ss, ss);
3600           if(piece <= (int)WhiteKing)
3601             pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
3602         }
3603     }
3604
3605     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
3606                                       buffering bug */
3607 }
3608 #endif
3609
3610 void ReadBitmap(pm, name, bits, wreq, hreq)
3611      Pixmap *pm;
3612      String name;
3613      unsigned char bits[];
3614      u_int wreq, hreq;
3615 {
3616     int x_hot, y_hot;
3617     u_int w, h;
3618     int errcode;
3619     char msg[MSG_SIZ], fullname[MSG_SIZ];
3620
3621     if (*appData.bitmapDirectory != NULLCHAR) {
3622       safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
3623       strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
3624       strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
3625       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
3626                                 &w, &h, pm, &x_hot, &y_hot);
3627       fprintf(stderr, "load %s\n", name);
3628         if (errcode != BitmapSuccess) {
3629             switch (errcode) {
3630               case BitmapOpenFailed:
3631                 snprintf(msg, sizeof(msg), _("Can't open bitmap file %s"), fullname);
3632                 break;
3633               case BitmapFileInvalid:
3634                 snprintf(msg, sizeof(msg), _("Invalid bitmap in file %s"), fullname);
3635                 break;
3636               case BitmapNoMemory:
3637                 snprintf(msg, sizeof(msg), _("Ran out of memory reading bitmap file %s"),
3638                         fullname);
3639                 break;
3640               default:
3641                 snprintf(msg, sizeof(msg), _("Unknown XReadBitmapFile error %d on file %s"),
3642                         errcode, fullname);
3643                 break;
3644             }
3645             fprintf(stderr, _("%s: %s...using built-in\n"),
3646                     programName, msg);
3647         } else if (w != wreq || h != hreq) {
3648             fprintf(stderr,
3649                     _("%s: Bitmap %s is %dx%d, not %dx%d...using built-in\n"),
3650                     programName, fullname, w, h, wreq, hreq);
3651         } else {
3652             return;
3653         }
3654     }
3655     if (bits != NULL) {
3656         *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
3657                                     wreq, hreq);
3658     }
3659 }
3660
3661 void CreateGrid()
3662 {
3663     int i, j;
3664
3665     if (lineGap == 0) return;
3666
3667     /* [HR] Split this into 2 loops for non-square boards. */
3668
3669     for (i = 0; i < BOARD_HEIGHT + 1; i++) {
3670         gridSegments[i].x1 = 0;
3671         gridSegments[i].x2 =
3672           lineGap + BOARD_WIDTH * (squareSize + lineGap);
3673         gridSegments[i].y1 = gridSegments[i].y2
3674           = lineGap / 2 + (i * (squareSize + lineGap));
3675     }
3676
3677     for (j = 0; j < BOARD_WIDTH + 1; j++) {
3678         gridSegments[j + i].y1 = 0;
3679         gridSegments[j + i].y2 =
3680           lineGap + BOARD_HEIGHT * (squareSize + lineGap);
3681         gridSegments[j + i].x1 = gridSegments[j + i].x2
3682           = lineGap / 2 + (j * (squareSize + lineGap));
3683     }
3684 }
3685
3686 static void MenuBarSelect(w, addr, index)
3687      Widget w;
3688      caddr_t addr;
3689      caddr_t index;
3690 {
3691     XtActionProc proc = (XtActionProc) addr;
3692
3693     (proc)(NULL, NULL, NULL, NULL);
3694 }
3695
3696 void CreateMenuBarPopup(parent, name, mb)
3697      Widget parent;
3698      String name;
3699      Menu *mb;
3700 {
3701     int j;
3702     Widget menu, entry;
3703     MenuItem *mi;
3704     Arg args[16];
3705
3706     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3707                               parent, NULL, 0);
3708     j = 0;
3709     XtSetArg(args[j], XtNleftMargin, 20);   j++;
3710     XtSetArg(args[j], XtNrightMargin, 20);  j++;
3711     mi = mb->mi;
3712     while (mi->string != NULL) {
3713         if (strcmp(mi->string, "----") == 0) {
3714             entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
3715                                           menu, args, j);
3716         } else {
3717           XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
3718             entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
3719                                           menu, args, j+1);
3720             XtAddCallback(entry, XtNcallback,
3721                           (XtCallbackProc) MenuBarSelect,
3722                           (caddr_t) mi->proc);
3723         }
3724         mi++;
3725     }
3726 }
3727
3728 Widget CreateMenuBar(mb)
3729      Menu *mb;
3730 {
3731     int j;
3732     Widget anchor, menuBar;
3733     Arg args[16];
3734     char menuName[MSG_SIZ];
3735
3736     j = 0;
3737     XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
3738     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3739     XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3740     menuBar = XtCreateWidget("menuBar", boxWidgetClass,
3741                              formWidget, args, j);
3742
3743     while (mb->name != NULL) {
3744         safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
3745         strncat(menuName, mb->name, MSG_SIZ - strlen(menuName) - 1);
3746         j = 0;
3747         XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
3748         if (tinyLayout) {
3749             char shortName[2];
3750             shortName[0] = _(mb->name)[0];
3751             shortName[1] = NULLCHAR;
3752             XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
3753         }
3754       else {
3755           XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
3756       }
3757
3758         XtSetArg(args[j], XtNborderWidth, 0);                   j++;
3759         anchor = XtCreateManagedWidget(mb->name, menuButtonWidgetClass,
3760                                        menuBar, args, j);
3761         CreateMenuBarPopup(menuBar, menuName, mb);
3762         mb++;
3763     }
3764     return menuBar;
3765 }
3766
3767 Widget CreateButtonBar(mi)
3768      MenuItem *mi;
3769 {
3770     int j;
3771     Widget button, buttonBar;
3772     Arg args[16];
3773
3774     j = 0;
3775     XtSetArg(args[j], XtNorientation, XtorientHorizontal); j++;
3776     if (tinyLayout) {
3777         XtSetArg(args[j], XtNhSpace, 0); j++;
3778     }
3779     XtSetArg(args[j], XtNborderWidth, 0); j++;
3780     XtSetArg(args[j], XtNvSpace, 0);                        j++;
3781     buttonBar = XtCreateWidget("buttonBar", boxWidgetClass,
3782                                formWidget, args, j);
3783
3784     while (mi->string != NULL) {
3785         j = 0;
3786         if (tinyLayout) {
3787             XtSetArg(args[j], XtNinternalWidth, 2); j++;
3788             XtSetArg(args[j], XtNborderWidth, 0); j++;
3789         }
3790       XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string))); j++;
3791         button = XtCreateManagedWidget(mi->string, commandWidgetClass,
3792                                        buttonBar, args, j);
3793         XtAddCallback(button, XtNcallback,
3794                       (XtCallbackProc) MenuBarSelect,
3795                       (caddr_t) mi->proc);
3796         mi++;
3797     }
3798     return buttonBar;
3799 }
3800
3801 Widget
3802 CreatePieceMenu(name, color)
3803      char *name;
3804      int color;
3805 {
3806     int i;
3807     Widget entry, menu;
3808     Arg args[16];
3809     ChessSquare selection;
3810
3811     menu = XtCreatePopupShell(name, simpleMenuWidgetClass,
3812                               boardWidget, args, 0);
3813
3814     for (i = 0; i < PIECE_MENU_SIZE; i++) {
3815         String item = pieceMenuStrings[color][i];
3816
3817         if (strcmp(item, "----") == 0) {
3818             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3819                                           menu, NULL, 0);
3820         } else {
3821           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3822             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3823                                 menu, args, 1);
3824             selection = pieceMenuTranslation[color][i];
3825             XtAddCallback(entry, XtNcallback,
3826                           (XtCallbackProc) PieceMenuSelect,
3827                           (caddr_t) selection);
3828             if (selection == WhitePawn || selection == BlackPawn) {
3829                 XtSetArg(args[0], XtNpopupOnEntry, entry);
3830                 XtSetValues(menu, args, 1);
3831             }
3832         }
3833     }
3834     return menu;
3835 }
3836
3837 void
3838 CreatePieceMenus()
3839 {
3840     int i;
3841     Widget entry;
3842     Arg args[16];
3843     ChessSquare selection;
3844
3845     whitePieceMenu = CreatePieceMenu("menuW", 0);
3846     blackPieceMenu = CreatePieceMenu("menuB", 1);
3847
3848     XtRegisterGrabAction(PieceMenuPopup, True,
3849                          (unsigned)(ButtonPressMask|ButtonReleaseMask),
3850                          GrabModeAsync, GrabModeAsync);
3851
3852     XtSetArg(args[0], XtNlabel, _("Drop"));
3853     dropMenu = XtCreatePopupShell("menuD", simpleMenuWidgetClass,
3854                                   boardWidget, args, 1);
3855     for (i = 0; i < DROP_MENU_SIZE; i++) {
3856         String item = dropMenuStrings[i];
3857
3858         if (strcmp(item, "----") == 0) {
3859             entry = XtCreateManagedWidget(item, smeLineObjectClass,
3860                                           dropMenu, NULL, 0);
3861         } else {
3862           XtSetArg(args[0], XtNlabel, XtNewString(_(item)));
3863             entry = XtCreateManagedWidget(item, smeBSBObjectClass,
3864                                 dropMenu, args, 1);
3865             selection = dropMenuTranslation[i];
3866             XtAddCallback(entry, XtNcallback,
3867                           (XtCallbackProc) DropMenuSelect,
3868                           (caddr_t) selection);
3869         }
3870     }
3871 }
3872
3873 void SetupDropMenu()
3874 {
3875     int i, j, count;
3876     char label[32];
3877     Arg args[16];
3878     Widget entry;
3879     char* p;
3880
3881     for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
3882         entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
3883         p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
3884                    dmEnables[i].piece);
3885         XtSetSensitive(entry, p != NULL || !appData.testLegality
3886                        /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
3887                                        && !appData.icsActive));
3888         count = 0;
3889         while (p && *p++ == dmEnables[i].piece) count++;
3890         snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
3891         j = 0;
3892         XtSetArg(args[j], XtNlabel, label); j++;
3893         XtSetValues(entry, args, j);
3894     }
3895 }
3896
3897 void PieceMenuPopup(w, event, params, num_params)
3898      Widget w;
3899      XEvent *event;
3900      String *params;
3901      Cardinal *num_params;
3902 {
3903     String whichMenu; int menuNr;
3904     if (event->type == ButtonRelease)
3905         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3906     else if (event->type == ButtonPress)
3907         menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
3908     switch(menuNr) {
3909       case 0: whichMenu = params[0]; break;
3910       case 1: SetupDropMenu(); whichMenu = "menuD"; break;
3911       case 2:
3912       case -1: if (errorUp) ErrorPopDown();
3913       default: return;
3914     }
3915     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
3916 }
3917
3918 static void PieceMenuSelect(w, piece, junk)
3919      Widget w;
3920      ChessSquare piece;
3921      caddr_t junk;
3922 {
3923     if (pmFromX < 0 || pmFromY < 0) return;
3924     EditPositionMenuEvent(piece, pmFromX, pmFromY);
3925 }
3926
3927 static void DropMenuSelect(w, piece, junk)
3928      Widget w;
3929      ChessSquare piece;
3930      caddr_t junk;
3931 {
3932     if (pmFromX < 0 || pmFromY < 0) return;
3933     DropMenuEvent(piece, pmFromX, pmFromY);
3934 }
3935
3936 void WhiteClock(w, event, prms, nprms)
3937      Widget w;
3938      XEvent *event;
3939      String *prms;
3940      Cardinal *nprms;
3941 {
3942     if (gameMode == EditPosition || gameMode == IcsExamining) {
3943         SetWhiteToPlayEvent();
3944     } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
3945         CallFlagEvent();
3946     }
3947 }
3948
3949 void BlackClock(w, event, prms, nprms)
3950      Widget w;
3951      XEvent *event;
3952      String *prms;
3953      Cardinal *nprms;
3954 {
3955     if (gameMode == EditPosition || gameMode == IcsExamining) {
3956         SetBlackToPlayEvent();
3957     } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
3958         CallFlagEvent();
3959     }
3960 }
3961
3962
3963 /*
3964  * If the user selects on a border boundary, return -1; if off the board,
3965  *   return -2.  Otherwise map the event coordinate to the square.
3966  */
3967 int EventToSquare(x, limit)
3968      int x;
3969 {
3970     if (x <= 0)
3971       return -2;
3972     if (x < lineGap)
3973       return -1;
3974     x -= lineGap;
3975     if ((x % (squareSize + lineGap)) >= squareSize)
3976       return -1;
3977     x /= (squareSize + lineGap);
3978     if (x >= limit)
3979       return -2;
3980     return x;
3981 }
3982
3983 static void do_flash_delay(msec)
3984      unsigned long msec;
3985 {
3986     TimeDelay(msec);
3987 }
3988
3989 static void drawHighlight(file, rank, gc)
3990      int file, rank;
3991      GC gc;
3992 {
3993     int x, y;
3994
3995     if (lineGap == 0 || appData.blindfold) return;
3996
3997     if (flipView) {
3998         x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
3999           (squareSize + lineGap);
4000         y = lineGap/2 + rank * (squareSize + lineGap);
4001     } else {
4002         x = lineGap/2 + file * (squareSize + lineGap);
4003         y = lineGap/2 + ((BOARD_HEIGHT-1)-rank) *
4004           (squareSize + lineGap);
4005     }
4006
4007     XDrawRectangle(xDisplay, xBoardWindow, gc, x, y,
4008                    squareSize+lineGap, squareSize+lineGap);
4009 }
4010
4011 int hi1X = -1, hi1Y = -1, hi2X = -1, hi2Y = -1;
4012 int pm1X = -1, pm1Y = -1, pm2X = -1, pm2Y = -1;
4013
4014 void
4015 SetHighlights(fromX, fromY, toX, toY)
4016      int fromX, fromY, toX, toY;
4017 {
4018     if (hi1X != fromX || hi1Y != fromY) {
4019         if (hi1X >= 0 && hi1Y >= 0) {
4020             drawHighlight(hi1X, hi1Y, lineGC);
4021         }
4022     } // [HGM] first erase both, then draw new!
4023     if (hi2X != toX || hi2Y != toY) {
4024         if (hi2X >= 0 && hi2Y >= 0) {
4025             drawHighlight(hi2X, hi2Y, lineGC);
4026         }
4027     }
4028     if (hi1X != fromX || hi1Y != fromY) {
4029         if (fromX >= 0 && fromY >= 0) {
4030             drawHighlight(fromX, fromY, highlineGC);
4031         }
4032     }
4033     if (hi2X != toX || hi2Y != toY) {
4034         if (toX >= 0 && toY >= 0) {
4035             drawHighlight(toX, toY, highlineGC);
4036         }
4037     }
4038     hi1X = fromX;
4039     hi1Y = fromY;
4040     hi2X = toX;
4041     hi2Y = toY;
4042 }
4043
4044 void
4045 ClearHighlights()
4046 {
4047     SetHighlights(-1, -1, -1, -1);
4048 }
4049
4050
4051 void
4052 SetPremoveHighlights(fromX, fromY, toX, toY)
4053      int fromX, fromY, toX, toY;
4054 {
4055     if (pm1X != fromX || pm1Y != fromY) {
4056         if (pm1X >= 0 && pm1Y >= 0) {
4057             drawHighlight(pm1X, pm1Y, lineGC);
4058         }
4059         if (fromX >= 0 && fromY >= 0) {
4060             drawHighlight(fromX, fromY, prelineGC);
4061         }
4062     }
4063     if (pm2X != toX || pm2Y != toY) {
4064         if (pm2X >= 0 && pm2Y >= 0) {
4065             drawHighlight(pm2X, pm2Y, lineGC);
4066         }
4067         if (toX >= 0 && toY >= 0) {
4068             drawHighlight(toX, toY, prelineGC);
4069         }
4070     }
4071     pm1X = fromX;
4072     pm1Y = fromY;
4073     pm2X = toX;
4074     pm2Y = toY;
4075 }
4076
4077 void
4078 ClearPremoveHighlights()
4079 {
4080   SetPremoveHighlights(-1, -1, -1, -1);
4081 }
4082
4083 static int CutOutSquare(x, y, x0, y0, kind)
4084      int x, y, *x0, *y0, kind;
4085 {
4086     int W = BOARD_WIDTH, H = BOARD_HEIGHT;
4087     int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
4088     *x0 = 0; *y0 = 0;
4089     if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
4090     if(textureW[kind] < W*squareSize)
4091         *x0 = (textureW[kind] - squareSize) * nx/(W-1);
4092     else
4093         *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
4094     if(textureH[kind] < H*squareSize)
4095         *y0 = (textureH[kind] - squareSize) * ny/(H-1);
4096     else
4097         *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
4098     return 1;
4099 }
4100
4101 static void BlankSquare(x, y, color, piece, dest, fac)
4102      int x, y, color, fac;
4103      ChessSquare piece;
4104      Drawable dest;
4105 {   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
4106     int x0, y0;
4107     if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
4108         XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
4109                   squareSize, squareSize, x*fac, y*fac);
4110     } else
4111     if (useImages && useImageSqs) {
4112         Pixmap pm;
4113         switch (color) {
4114           case 1: /* light */
4115             pm = xpmLightSquare;
4116             break;
4117           case 0: /* dark */
4118             pm = xpmDarkSquare;
4119             break;
4120           case 2: /* neutral */
4121           default:
4122             pm = xpmJailSquare;
4123             break;
4124         }
4125         XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
4126                   squareSize, squareSize, x*fac, y*fac);
4127     } else {
4128         GC gc;
4129         switch (color) {
4130           case 1: /* light */
4131             gc = lightSquareGC;
4132             break;
4133           case 0: /* dark */
4134             gc = darkSquareGC;
4135             break;
4136           case 2: /* neutral */
4137           default:
4138             gc = jailSquareGC;
4139             break;
4140         }
4141         XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
4142     }
4143 }
4144
4145 /*
4146    I split out the routines to draw a piece so that I could
4147    make a generic flash routine.
4148 */
4149 static void monoDrawPiece_1bit(piece, square_color, x, y, dest)
4150      ChessSquare piece;
4151      int square_color, x, y;
4152      Drawable dest;
4153 {
4154     /* Avoid XCopyPlane on 1-bit screens to work around Sun bug */
4155     switch (square_color) {
4156       case 1: /* light */
4157       case 2: /* neutral */
4158       default:
4159         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4160                   ? *pieceToOutline(piece)
4161                   : *pieceToSolid(piece),
4162                   dest, bwPieceGC, 0, 0,
4163                   squareSize, squareSize, x, y);
4164         break;
4165       case 0: /* dark */
4166         XCopyArea(xDisplay, (int) piece < (int) BlackPawn
4167                   ? *pieceToSolid(piece)
4168                   : *pieceToOutline(piece),
4169                   dest, wbPieceGC, 0, 0,
4170                   squareSize, squareSize, x, y);
4171         break;
4172     }
4173 }
4174
4175 static void monoDrawPiece(piece, square_color, x, y, dest)
4176      ChessSquare piece;
4177      int square_color, x, y;
4178      Drawable dest;
4179 {
4180     switch (square_color) {
4181       case 1: /* light */
4182       case 2: /* neutral */
4183       default:
4184         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4185                    ? *pieceToOutline(piece)
4186                    : *pieceToSolid(piece),
4187                    dest, bwPieceGC, 0, 0,
4188                    squareSize, squareSize, x, y, 1);
4189         break;
4190       case 0: /* dark */
4191         XCopyPlane(xDisplay, (int) piece < (int) BlackPawn
4192                    ? *pieceToSolid(piece)
4193                    : *pieceToOutline(piece),
4194                    dest, wbPieceGC, 0, 0,
4195                    squareSize, squareSize, x, y, 1);
4196         break;
4197     }
4198 }
4199
4200 static void colorDrawPiece(piece, square_color, x, y, dest)
4201      ChessSquare piece;
4202      int square_color, x, y;
4203      Drawable dest;
4204 {
4205     if(pieceToSolid(piece) == NULL) return; // [HGM] bitmaps: make it non-fatal if we have no bitmap;
4206     switch (square_color) {
4207       case 1: /* light */
4208         XCopyPlane(xDisplay, *pieceToSolid(piece),
4209                    dest, (int) piece < (int) BlackPawn
4210                    ? wlPieceGC : blPieceGC, 0, 0,
4211                    squareSize, squareSize, x, y, 1);
4212         break;
4213       case 0: /* dark */
4214         XCopyPlane(xDisplay, *pieceToSolid(piece),
4215                    dest, (int) piece < (int) BlackPawn
4216                    ? wdPieceGC : bdPieceGC, 0, 0,
4217                    squareSize, squareSize, x, y, 1);
4218         break;
4219       case 2: /* neutral */
4220       default:
4221         XCopyPlane(xDisplay, *pieceToSolid(piece),
4222                    dest, (int) piece < (int) BlackPawn
4223                    ? wjPieceGC : bjPieceGC, 0, 0,
4224                    squareSize, squareSize, x, y, 1);
4225         break;
4226     }
4227 }
4228
4229 static void colorDrawPieceImage(piece, square_color, x, y, dest)
4230      ChessSquare piece;
4231      int square_color, x, y;
4232      Drawable dest;
4233 {
4234     int kind, p = piece;
4235
4236     switch (square_color) {
4237       case 1: /* light */
4238       case 2: /* neutral */
4239       default:
4240         if ((int)piece < (int) BlackPawn) {
4241             kind = 0;
4242         } else {
4243             kind = 2;
4244             piece -= BlackPawn;
4245         }
4246         break;
4247       case 0: /* dark */
4248         if ((int)piece < (int) BlackPawn) {
4249             kind = 1;
4250         } else {
4251             kind = 3;
4252             piece -= BlackPawn;
4253         }
4254         break;
4255     }
4256     if(appData.upsideDown && flipView) kind ^= 2; // swap white and black pieces
4257     if(useTexture & square_color+1) {
4258         BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
4259         XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
4260         XSetClipOrigin(xDisplay, wlPieceGC, x, y);
4261         XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
4262         XSetClipMask(xDisplay, wlPieceGC, None);
4263         XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
4264     } else
4265     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
4266               dest, wlPieceGC, 0, 0,
4267               squareSize, squareSize, x, y);
4268 }
4269
4270 typedef void (*DrawFunc)();
4271
4272 DrawFunc ChooseDrawFunc()
4273 {
4274     if (appData.monoMode) {
4275         if (DefaultDepth(xDisplay, xScreen) == 1) {
4276             return monoDrawPiece_1bit;
4277         } else {
4278             return monoDrawPiece;
4279         }
4280     } else {
4281         if (useImages)
4282           return colorDrawPieceImage;
4283         else
4284           return colorDrawPiece;
4285     }
4286 }
4287
4288 /* [HR] determine square color depending on chess variant. */
4289 static int SquareColor(row, column)
4290      int row, column;
4291 {
4292     int square_color;
4293
4294     if (gameInfo.variant == VariantXiangqi) {
4295         if (column >= 3 && column <= 5 && row >= 0 && row <= 2) {
4296             square_color = 1;
4297         } else if (column >= 3 && column <= 5 && row >= 7 && row <= 9) {
4298             square_color = 0;
4299         } else if (row <= 4) {
4300             square_color = 0;