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