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