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