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