Preserve copies of the X11 front-end in xboard directory
authorH.G. Muller <h.g.muller@hccnet.nl>
Thu, 25 Oct 2012 07:20:03 +0000 (09:20 +0200)
committerH.G. Muller <h.g.muller@hccnet.nl>
Tue, 6 Nov 2012 11:45:07 +0000 (12:45 +0100)
The X11 front-end has been moved to the xaw directory, but copies
are retained for modifying them to gtk versions.

12 files changed:
xboard.c [new file with mode: 0644]
xboard.h [new file with mode: 0644]
xedittags.c [new file with mode: 0644]
xedittags.h [new file with mode: 0644]
xengineoutput.c [new file with mode: 0644]
xevalgraph.c [new file with mode: 0644]
xevalgraph.h [new file with mode: 0644]
xgamelist.c [new file with mode: 0644]
xgamelist.h [new file with mode: 0644]
xhistory.c [new file with mode: 0644]
xhistory.h [new file with mode: 0644]
xoptions.c [new file with mode: 0644]

diff --git a/xboard.c b/xboard.c
new file mode 100644 (file)
index 0000000..69079be
--- /dev/null
+++ b/xboard.c
@@ -0,0 +1,2556 @@
+/*
+ * xboard.c -- X front end for XBoard
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,
+ * Massachusetts.
+ *
+ * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
+ * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * The following terms apply to the enhanced version of XBoard
+ * distributed by the Free Software Foundation:
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#define HIGHDRAG 1
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <math.h>
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
+
+#if !OMIT_SOCKETS
+# if HAVE_SYS_SOCKET_H
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <netdb.h>
+# else /* not HAVE_SYS_SOCKET_H */
+#  if HAVE_LAN_SOCKET_H
+#   include <lan/socket.h>
+#   include <lan/in.h>
+#   include <lan/netdb.h>
+#  else /* not HAVE_LAN_SOCKET_H */
+#   define OMIT_SOCKETS 1
+#  endif /* not HAVE_LAN_SOCKET_H */
+# endif /* not HAVE_SYS_SOCKET_H */
+#endif /* !OMIT_SOCKETS */
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+#  include <string.h>
+# else /* not HAVE_STRING_H */
+#  include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_SYS_FCNTL_H
+# include <sys/fcntl.h>
+#else /* not HAVE_SYS_FCNTL_H */
+# if HAVE_FCNTL_H
+#  include <fcntl.h>
+# endif /* HAVE_FCNTL_H */
+#endif /* not HAVE_SYS_FCNTL_H */
+
+#if HAVE_SYS_SYSTEMINFO_H
+# include <sys/systeminfo.h>
+#endif /* HAVE_SYS_SYSTEMINFO_H */
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+# define HAVE_DIR_STRUCT
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+#  include <sys/ndir.h>
+#  define HAVE_DIR_STRUCT
+# endif
+# if HAVE_SYS_DIR_H
+#  include <sys/dir.h>
+#  define HAVE_DIR_STRUCT
+# endif
+# if HAVE_NDIR_H
+#  include <ndir.h>
+#  define HAVE_DIR_STRUCT
+# endif
+#endif
+
+#if ENABLE_NLS
+#include <locale.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/cursorfont.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/Atoms.h>
+#if USE_XAW3D
+#include <X11/Xaw3d/Dialog.h>
+#include <X11/Xaw3d/Form.h>
+#include <X11/Xaw3d/List.h>
+#include <X11/Xaw3d/Label.h>
+#include <X11/Xaw3d/SimpleMenu.h>
+#include <X11/Xaw3d/SmeBSB.h>
+#include <X11/Xaw3d/SmeLine.h>
+#include <X11/Xaw3d/Box.h>
+#include <X11/Xaw3d/MenuButton.h>
+#include <X11/Xaw3d/Text.h>
+#include <X11/Xaw3d/AsciiText.h>
+#else
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#endif
+
+// [HGM] bitmaps: put before incuding the bitmaps / pixmaps, to know how many piece types there are.
+#include "common.h"
+
+#if HAVE_LIBXPM
+#include <X11/xpm.h>
+#define IMAGE_EXT "xpm"
+#else
+#define IMAGE_EXT "xim"
+#endif
+
+#include "bitmaps/icon_white.bm"
+#include "bitmaps/icon_black.bm"
+#include "bitmaps/checkmark.bm"
+
+#include "frontend.h"
+#include "backend.h"
+#include "backendz.h"
+#include "moves.h"
+#include "xboard.h"
+#include "childio.h"
+#include "xgamelist.h"
+#include "xhistory.h"
+#include "xevalgraph.h"
+#include "xedittags.h"
+#include "menus.h"
+#include "board.h"
+#include "dialogs.h"
+#include "engineoutput.h"
+#include "usystem.h"
+#include "gettext.h"
+#include "draw.h"
+
+
+#ifdef __EMX__
+#ifndef HAVE_USLEEP
+#define HAVE_USLEEP
+#endif
+#define usleep(t)   _sleep2(((t)+500)/1000)
+#endif
+
+#ifdef ENABLE_NLS
+# define  _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define  _(s) (s)
+# define N_(s)  s
+#endif
+
+int main P((int argc, char **argv));
+RETSIGTYPE CmailSigHandler P((int sig));
+RETSIGTYPE IntSigHandler P((int sig));
+RETSIGTYPE TermSizeSigHandler P((int sig));
+Widget CreateMenuBar P((Menu *mb, int boardWidth));
+#if ENABLE_NLS
+char *InsertPxlSize P((char *pattern, int targetPxlSize));
+XFontSet CreateFontSet P((char *base_fnt_lst));
+#else
+char *FindFont P((char *pattern, int targetPxlSize));
+#endif
+void ReadBitmap P((Pixmap *pm, String name, unsigned char bits[],
+                  u_int wreq, u_int hreq));
+void EventProc P((Widget widget, caddr_t unused, XEvent *event));
+void DelayedDrag P((void));
+static void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
+void HandlePV P((Widget w, XEvent * event,
+                    String * params, Cardinal * nParams));
+void DrawPositionProc P((Widget w, XEvent *event,
+                    String *prms, Cardinal *nprms));
+void CommentClick P((Widget w, XEvent * event,
+                  String * params, Cardinal * nParams));
+void ICSInputBoxPopUp P((void));
+void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
+void KeyBindingProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void QuitWrapper P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+static void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+static void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+static void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void TempBackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void TempForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+Boolean TempBackwardActive = False;
+void ManInner P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void DisplayMove P((int moveNumber));
+void ICSInitScript P((void));
+void SelectMove P((Widget w, XEvent * event, String * params, Cardinal * nParams));
+void update_ics_width P(());
+int CopyMemoProc P(());
+
+/*
+* XBoard depends on Xt R4 or higher
+*/
+int xtVersion = XtSpecificationRelease;
+
+int xScreen;
+Display *xDisplay;
+Window xBoardWindow;
+Pixel lowTimeWarningColor, dialogColor, buttonColor; // used in widgets
+Pixmap iconPixmap, wIconPixmap, bIconPixmap, xMarkPixmap;
+Widget shellWidget, formWidget, boardWidget, titleWidget, dropMenu, menuBarWidget;
+Option *optList; // contains all widgets of main window
+#if ENABLE_NLS
+XFontSet fontSet, clockFontSet;
+#else
+Font clockFontID;
+XFontStruct *clockFontStruct;
+#endif
+Font coordFontID, countFontID;
+XFontStruct *coordFontStruct, *countFontStruct;
+XtAppContext appContext;
+char *layoutName;
+
+char installDir[] = "."; // [HGM] UCI: needed for UCI; probably needs run-time initializtion
+
+Position commentX = -1, commentY = -1;
+Dimension commentW, commentH;
+typedef unsigned int BoardSize;
+BoardSize boardSize;
+Boolean chessProgram;
+
+int  minX, minY; // [HGM] placement: volatile limits on upper-left corner
+int smallLayout = 0, tinyLayout = 0,
+  marginW, marginH, // [HGM] for run-time resizing
+  fromX = -1, fromY = -1, toX, toY, commentUp = False,
+  errorExitStatus = -1, defaultLineGap;
+Dimension textHeight;
+Pixel timerForegroundPixel, timerBackgroundPixel;
+Pixel buttonForegroundPixel, buttonBackgroundPixel;
+char *chessDir, *programName, *programVersion;
+Boolean alwaysOnTop = False;
+char *icsTextMenuString;
+char *icsNames;
+char *firstChessProgramNames;
+char *secondChessProgramNames;
+
+WindowPlacement wpMain;
+WindowPlacement wpConsole;
+WindowPlacement wpComment;
+WindowPlacement wpMoveHistory;
+WindowPlacement wpEvalGraph;
+WindowPlacement wpEngineOutput;
+WindowPlacement wpGameList;
+WindowPlacement wpTags;
+
+
+/* This magic number is the number of intermediate frames used
+   in each half of the animation. For short moves it's reduced
+   by 1. The total number of frames will be factor * 2 + 1.  */
+#define kFactor           4
+
+SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
+
+typedef struct {
+    char piece;
+    char* widget;
+} DropMenuEnables;
+
+DropMenuEnables dmEnables[] = {
+    { 'P', "Pawn" },
+    { 'N', "Knight" },
+    { 'B', "Bishop" },
+    { 'R', "Rook" },
+    { 'Q', "Queen" }
+};
+
+Arg shellArgs[] = {
+    { XtNwidth, 0 },
+    { XtNheight, 0 },
+    { XtNminWidth, 0 },
+    { XtNminHeight, 0 },
+    { XtNmaxWidth, 0 },
+    { XtNmaxHeight, 0 }
+};
+
+XtResource clientResources[] = {
+    { "flashCount", "flashCount", XtRInt, sizeof(int),
+       XtOffset(AppDataPtr, flashCount), XtRImmediate,
+       (XtPointer) FLASH_COUNT  },
+};
+
+XrmOptionDescRec shellOptions[] = {
+    { "-flashCount", "flashCount", XrmoptionSepArg, NULL },
+    { "-flash", "flashCount", XrmoptionNoArg, "3" },
+    { "-xflash", "flashCount", XrmoptionNoArg, "0" },
+};
+
+XtActionsRec boardActions[] = {
+    { "DrawPosition", DrawPositionProc },
+    { "HandlePV", HandlePV },
+    { "SelectPV", SelectPV },
+    { "StopPV", StopPV },
+    { "MenuItem", KeyBindingProc }, // [HGM] generic handler for key bindings
+    { "QuitProc", QuitWrapper },
+    { "ManProc", ManInner },
+    { "TempBackwardProc", TempBackwardProc },
+    { "TempForwardProc", TempForwardProc },
+    { "CommentClick", (XtActionProc) CommentClick },
+    { "GenericPopDown", (XtActionProc) GenericPopDown },
+    { "ErrorPopDown", (XtActionProc) ErrorPopDown },
+    { "CopyMemoProc", (XtActionProc) CopyMemoProc },
+    { "SelectMove", (XtActionProc) SelectMove },
+    { "LoadSelectedProc", LoadSelectedProc },
+    { "SetFilterProc", SetFilterProc },
+    { "TypeInProc", TypeInProc },
+    { "EnterKeyProc", EnterKeyProc },
+    { "UpKeyProc", UpKeyProc },
+    { "DownKeyProc", DownKeyProc },
+    { "WheelProc", WheelProc },
+    { "TabProc", TabProc },
+};
+
+char globalTranslations[] =
+  ":<Key>F9: MenuItem(Actions.Resign) \n \
+   :Ctrl<Key>n: MenuItem(File.NewGame) \n \
+   :Meta<Key>V: MenuItem(File.NewVariant) \n \
+   :Ctrl<Key>o: MenuItem(File.LoadGame) \n \
+   :Meta<Key>Next: MenuItem(LoadNextGameProc) \n \
+   :Meta<Key>Prior: MenuItem(LoadPrevGameProc) \n \
+   :Ctrl<Key>Down: LoadSelectedProc(3) \n \
+   :Ctrl<Key>Up: LoadSelectedProc(-3) \n \
+   :Ctrl<Key>s: MenuItem(File.SaveGame) \n \
+   :Ctrl<Key>c: MenuItem(Edit.CopyGame) \n \
+   :Ctrl<Key>v: MenuItem(Edit.PasteGame) \n \
+   :Ctrl<Key>O: MenuItem(File.LoadPosition) \n \
+   :Shift<Key>Next: MenuItem(LoadNextPositionProc) \n \
+   :Shift<Key>Prior: MenuItem(LoadPrevPositionProc) \n \
+   :Ctrl<Key>S: MenuItem(File.SavePosition) \n \
+   :Ctrl<Key>C: MenuItem(Edit.CopyPosition) \n \
+   :Ctrl<Key>V: MenuItem(Edit.PastePosition) \n \
+   :Ctrl<Key>q: MenuItem(File.Quit) \n \
+   :Ctrl<Key>w: MenuItem(Mode.MachineWhite) \n \
+   :Ctrl<Key>b: MenuItem(Mode.MachineBlack) \n \
+   :Ctrl<Key>t: MenuItem(Mode.TwoMachines) \n \
+   :Ctrl<Key>a: MenuItem(Mode.AnalysisMode) \n \
+   :Ctrl<Key>g: MenuItem(Mode.AnalyzeFile) \n \
+   :Ctrl<Key>e: MenuItem(Mode.EditGame) \n \
+   :Ctrl<Key>E: MenuItem(Mode.EditPosition) \n \
+   :Meta<Key>O: MenuItem(View.EngineOutput) \n \
+   :Meta<Key>E: MenuItem(View.EvaluationGraph) \n \
+   :Meta<Key>G: MenuItem(View.GameList) \n \
+   :Meta<Key>H: MenuItem(View.MoveHistory) \n \
+   :<Key>Pause: MenuItem(Mode.Pause) \n \
+   :<Key>F3: MenuItem(Action.Accept) \n \
+   :<Key>F4: MenuItem(Action.Decline) \n \
+   :<Key>F12: MenuItem(Action.Rematch) \n \
+   :<Key>F5: MenuItem(Action.CallFlag) \n \
+   :<Key>F6: MenuItem(Action.Draw) \n \
+   :<Key>F7: MenuItem(Action.Adjourn) \n \
+   :<Key>F8: MenuItem(Action.Abort) \n \
+   :<Key>F10: MenuItem(Action.StopObserving) \n \
+   :<Key>F11: MenuItem(Action.StopExamining) \n \
+   :Ctrl<Key>d: MenuItem(DebugProc) \n \
+   :Meta Ctrl<Key>F12: MenuItem(DebugProc) \n \
+   :Meta<Key>End: MenuItem(Edit.ForwardtoEnd) \n \
+   :Meta<Key>Right: MenuItem(Edit.Forward) \n \
+   :Meta<Key>Home: MenuItem(Edit.BacktoStart) \n \
+   :Meta<Key>Left: MenuItem(Edit.Backward) \n \
+   :<Key>Left: MenuItem(Edit.Backward) \n \
+   :<Key>Right: MenuItem(Edit.Forward) \n \
+   :<Key>Home: MenuItem(Edit.Revert) \n \
+   :<Key>End: MenuItem(Edit.TruncateGame) \n \
+   :Ctrl<Key>m: MenuItem(Engine.MoveNow) \n \
+   :Ctrl<Key>x: MenuItem(Engine.RetractMove) \n \
+   :Meta<Key>J: MenuItem(Options.Adjudications) \n \
+   :Meta<Key>U: MenuItem(Options.CommonEngine) \n \
+   :Meta<Key>T: MenuItem(Options.TimeControl) \n \
+   :Ctrl<Key>P: MenuItem(PonderNextMove) \n "
+#ifndef OPTIONSDIALOG
+    "\
+   :Ctrl<Key>Q: MenuItem(AlwaysQueenProc) \n \
+   :Ctrl<Key>F: MenuItem(AutoflagProc) \n \
+   :Ctrl<Key>A: MenuItem(AnimateMovingProc) \n \
+   :Ctrl<Key>L: MenuItem(TestLegalityProc) \n \
+   :Ctrl<Key>H: MenuItem(HideThinkingProc) \n "
+#endif
+   "\
+   :<Key>F1: MenuItem(Help.ManXBoard) \n \
+   :<Key>F2: MenuItem(View.FlipView) \n \
+   :<KeyDown>Return: TempBackwardProc() \n \
+   :<KeyUp>Return: TempForwardProc() \n";
+
+char ICSInputTranslations[] =
+    "<Key>Up: UpKeyProc() \n "
+    "<Key>Down: DownKeyProc() \n "
+    "<Key>Return: EnterKeyProc() \n";
+
+// [HGM] vari: another hideous kludge: call extend-end first so we can be sure select-start works,
+//             as the widget is destroyed before the up-click can call extend-end
+char commentTranslations[] = "<Btn3Down>: extend-end() select-start() CommentClick() \n";
+
+String xboardResources[] = {
+    "*Error*translations: #override\\n <Key>Return: ErrorPopDown()",
+    NULL
+  };
+
+
+/* Max possible square size */
+#define MAXSQSIZE 256
+
+static int xpm_avail[MAXSQSIZE];
+
+#ifdef HAVE_DIR_STRUCT
+
+/* Extract piece size from filename */
+static int
+xpm_getsize (char *name, int len, char *ext)
+{
+    char *p, *d;
+    char buf[10];
+
+    if (len < 4)
+      return 0;
+
+    if ((p=strchr(name, '.')) == NULL ||
+       StrCaseCmp(p+1, ext) != 0)
+      return 0;
+
+    p = name + 3;
+    d = buf;
+
+    while (*p && isdigit(*p))
+      *(d++) = *(p++);
+
+    *d = 0;
+    return atoi(buf);
+}
+
+/* Setup xpm_avail */
+static int
+xpm_getavail (char *dirname, char *ext)
+{
+    DIR *dir;
+    struct dirent *ent;
+    int  i;
+
+    for (i=0; i<MAXSQSIZE; ++i)
+      xpm_avail[i] = 0;
+
+    if (appData.debugMode)
+      fprintf(stderr, "XPM dir:%s:ext:%s:\n", dirname, ext);
+
+    dir = opendir(dirname);
+    if (!dir)
+      {
+         fprintf(stderr, _("%s: Can't access XPM directory %s\n"),
+                 programName, dirname);
+         exit(1);
+      }
+
+    while ((ent=readdir(dir)) != NULL) {
+       i = xpm_getsize(ent->d_name, NAMLEN(ent), ext);
+       if (i > 0 && i < MAXSQSIZE)
+         xpm_avail[i] = 1;
+    }
+
+    closedir(dir);
+
+    return 0;
+}
+
+void
+xpm_print_avail (FILE *fp, char *ext)
+{
+    int i;
+
+    fprintf(fp, _("Available `%s' sizes:\n"), ext);
+    for (i=1; i<MAXSQSIZE; ++i) {
+       if (xpm_avail[i])
+         printf("%d\n", i);
+    }
+}
+
+/* Return XPM piecesize closest to size */
+int
+xpm_closest_to (char *dirname, int size, char *ext)
+{
+    int i;
+    int sm_diff = MAXSQSIZE;
+    int sm_index = 0;
+    int diff;
+
+    xpm_getavail(dirname, ext);
+
+    if (appData.debugMode)
+      xpm_print_avail(stderr, ext);
+
+    for (i=1; i<MAXSQSIZE; ++i) {
+       if (xpm_avail[i]) {
+           diff = size - i;
+           diff = (diff<0) ? -diff : diff;
+           if (diff < sm_diff) {
+               sm_diff = diff;
+               sm_index = i;
+           }
+       }
+    }
+
+    if (!sm_index) {
+       fprintf(stderr, _("Error: No `%s' files!\n"), ext);
+       exit(1);
+    }
+
+    return sm_index;
+}
+#else  /* !HAVE_DIR_STRUCT */
+/* If we are on a system without a DIR struct, we can't
+   read the directory, so we can't collect a list of
+   filenames, etc., so we can't do any size-fitting. */
+int
+xpm_closest_to (char *dirname, int size, char *ext)
+{
+    fprintf(stderr, _("\
+Warning: No DIR structure found on this system --\n\
+         Unable to autosize for XPM/XIM pieces.\n\
+   Please report this error to %s.\n\
+   Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
+    return size;
+}
+#endif /* HAVE_DIR_STRUCT */
+
+
+/* Arrange to catch delete-window events */
+Atom wm_delete_window;
+void
+CatchDeleteWindow (Widget w, String procname)
+{
+  char buf[MSG_SIZ];
+  XSetWMProtocols(xDisplay, XtWindow(w), &wm_delete_window, 1);
+  snprintf(buf, sizeof(buf), "<Message>WM_PROTOCOLS: %s() \n", procname);
+  XtAugmentTranslations(w, XtParseTranslationTable(buf));
+}
+
+void
+BoardToTop ()
+{
+  Arg args[16];
+  XtSetArg(args[0], XtNiconic, False);
+  XtSetValues(shellWidget, args, 1);
+
+  XtPopup(shellWidget, XtGrabNone); /* Raise if lowered  */
+}
+
+//---------------------------------------------------------------------------------------------------------
+// some symbol definitions to provide the proper (= XBoard) context for the code in args.h
+#define XBOARD True
+#define JAWS_ARGS
+#define CW_USEDEFAULT (1<<31)
+#define ICS_TEXT_MENU_SIZE 90
+#define DEBUG_FILE "xboard.debug"
+#define SetCurrentDirectory chdir
+#define GetCurrentDirectory(SIZE, NAME) getcwd(NAME, SIZE)
+#define OPTCHAR "-"
+#define SEPCHAR " "
+
+// The option definition and parsing code common to XBoard and WinBoard is collected in this file
+#include "args.h"
+
+// front-end part of option handling
+
+// [HGM] This platform-dependent table provides the location for storing the color info
+extern char *crWhite, * crBlack;
+
+void *
+colorVariable[] = {
+  &appData.whitePieceColor,
+  &appData.blackPieceColor,
+  &appData.lightSquareColor,
+  &appData.darkSquareColor,
+  &appData.highlightSquareColor,
+  &appData.premoveHighlightColor,
+  &appData.lowTimeWarningColor,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  &crWhite,
+  &crBlack,
+  NULL
+};
+
+// [HGM] font: keep a font for each square size, even non-stndard ones
+#define NUM_SIZES 18
+#define MAX_SIZE 130
+Boolean fontIsSet[NUM_FONTS], fontValid[NUM_FONTS][MAX_SIZE];
+char *fontTable[NUM_FONTS][MAX_SIZE];
+
+void
+ParseFont (char *name, int number)
+{ // in XBoard, only 2 of the fonts are currently implemented, and we just copy their name
+  int size;
+  if(sscanf(name, "size%d:", &size)) {
+    // [HGM] font: font is meant for specific boardSize (likely from settings file);
+    //       defer processing it until we know if it matches our board size
+    if(size >= 0 && size<MAX_SIZE) { // for now, fixed limit
+       fontTable[number][size] = strdup(strchr(name, ':')+1);
+       fontValid[number][size] = True;
+    }
+    return;
+  }
+  switch(number) {
+    case 0: // CLOCK_FONT
+       appData.clockFont = strdup(name);
+      break;
+    case 1: // MESSAGE_FONT
+       appData.font = strdup(name);
+      break;
+    case 2: // COORD_FONT
+       appData.coordFont = strdup(name);
+      break;
+    default:
+      return;
+  }
+  fontIsSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
+}
+
+void
+SetFontDefaults ()
+{ // only 2 fonts currently
+  appData.clockFont = CLOCK_FONT_NAME;
+  appData.coordFont = COORD_FONT_NAME;
+  appData.font  =   DEFAULT_FONT_NAME;
+}
+
+void
+CreateFonts ()
+{ // no-op, until we identify the code for this already in XBoard and move it here
+}
+
+void
+ParseColor (int n, char *name)
+{ // in XBoard, just copy the color-name string
+  if(colorVariable[n]) *(char**)colorVariable[n] = strdup(name);
+}
+
+void
+ParseTextAttribs (ColorClass cc, char *s)
+{
+    (&appData.colorShout)[cc] = strdup(s);
+}
+
+void
+ParseBoardSize (void *addr, char *name)
+{
+    appData.boardSize = strdup(name);
+}
+
+void
+LoadAllSounds ()
+{ // In XBoard the sound-playing program takes care of obtaining the actual sound
+}
+
+void
+SetCommPortDefaults ()
+{ // for now, this is a no-op, as the corresponding option does not exist in XBoard
+}
+
+// [HGM] args: these three cases taken out to stay in front-end
+void
+SaveFontArg (FILE *f, ArgDescriptor *ad)
+{
+  char *name;
+  int i, n = (int)(intptr_t)ad->argLoc;
+  switch(n) {
+    case 0: // CLOCK_FONT
+       name = appData.clockFont;
+      break;
+    case 1: // MESSAGE_FONT
+       name = appData.font;
+      break;
+    case 2: // COORD_FONT
+       name = appData.coordFont;
+      break;
+    default:
+      return;
+  }
+  for(i=0; i<NUM_SIZES; i++) // [HGM] font: current font becomes standard for current size
+    if(sizeDefaults[i].squareSize == squareSize) { // only for standard sizes!
+       fontTable[n][squareSize] = strdup(name);
+       fontValid[n][squareSize] = True;
+       break;
+  }
+  for(i=0; i<MAX_SIZE; i++) if(fontValid[n][i]) // [HGM] font: store all standard fonts
+    fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
+}
+
+void
+ExportSounds ()
+{ // nothing to do, as the sounds are at all times represented by their text-string names already
+}
+
+void
+SaveAttribsArg (FILE *f, ArgDescriptor *ad)
+{      // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
+       fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)(intptr_t)ad->argLoc]);
+}
+
+void
+SaveColor (FILE *f, ArgDescriptor *ad)
+{      // in WinBoard the color is an int and has to be converted to text. In X it would be a string already?
+       if(colorVariable[(int)(intptr_t)ad->argLoc])
+       fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)(intptr_t)ad->argLoc]);
+}
+
+void
+SaveBoardSize (FILE *f, char *name, void *addr)
+{ // wrapper to shield back-end from BoardSize & sizeInfo
+  fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
+}
+
+void
+ParseCommPortSettings (char *s)
+{ // no such option in XBoard (yet)
+}
+
+int frameX, frameY;
+
+void
+GetActualPlacement (Widget wg, WindowPlacement *wp)
+{
+  XWindowAttributes winAt;
+  Window win, dummy;
+  int rx, ry;
+
+  if(!wg) return;
+
+  win = XtWindow(wg);
+  XGetWindowAttributes(xDisplay, win, &winAt); // this works, where XtGetValues on XtNx, XtNy does not!
+  XTranslateCoordinates (xDisplay, win, winAt.root, -winAt.border_width, -winAt.border_width, &rx, &ry, &dummy);
+  wp->x = rx - winAt.x;
+  wp->y = ry - winAt.y;
+  wp->height = winAt.height;
+  wp->width = winAt.width;
+  frameX = winAt.x; frameY = winAt.y; // remember to decide if windows touch
+}
+
+void
+GetWindowCoords ()
+{ // wrapper to shield use of window handles from back-end (make addressible by number?)
+  // In XBoard this will have to wait until awareness of window parameters is implemented
+  GetActualPlacement(shellWidget, &wpMain);
+  if(shellUp[EngOutDlg]) GetActualPlacement(shells[EngOutDlg], &wpEngineOutput);
+  if(shellUp[HistoryDlg]) GetActualPlacement(shells[HistoryDlg], &wpMoveHistory);
+  if(shellUp[EvalGraphDlg]) GetActualPlacement(shells[EvalGraphDlg], &wpEvalGraph);
+  if(shellUp[GameListDlg]) GetActualPlacement(shells[GameListDlg], &wpGameList);
+  if(shellUp[CommentDlg]) GetActualPlacement(shells[CommentDlg], &wpComment);
+  if(shellUp[TagsDlg]) GetActualPlacement(shells[TagsDlg], &wpTags);
+}
+
+void
+PrintCommPortSettings (FILE *f, char *name)
+{ // This option does not exist in XBoard
+}
+
+void
+EnsureOnScreen (int *x, int *y, int minX, int minY)
+{
+  return;
+}
+
+int
+MainWindowUp ()
+{ // [HGM] args: allows testing if main window is realized from back-end
+  return xBoardWindow != 0;
+}
+
+void
+PopUpStartupDialog ()
+{  // start menu not implemented in XBoard
+}
+
+char *
+ConvertToLine (int argc, char **argv)
+{
+  static char line[128*1024], buf[1024];
+  int i;
+
+  line[0] = NULLCHAR;
+  for(i=1; i<argc; i++)
+    {
+      if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') || argv[i][0] == NULLCHAR)
+         && argv[i][0] != '{' )
+       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "{%s} ", argv[i]);
+      else
+       snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s ", argv[i]);
+      strncat(line, buf, 128*1024 - strlen(line) - 1 );
+    }
+
+  line[strlen(line)-1] = NULLCHAR;
+  return line;
+}
+
+//--------------------------------------------------------------------------------------------
+
+void
+ResizeBoardWindow (int w, int h, int inhibit)
+{
+    w += marginW + 1; // [HGM] not sure why the +1 is (sometimes) needed...
+    h += marginH;
+    shellArgs[0].value = w;
+    shellArgs[1].value = h;
+    shellArgs[4].value = shellArgs[2].value = w;
+    shellArgs[5].value = shellArgs[3].value = h;
+    XtSetValues(shellWidget, &shellArgs[0], inhibit ? 6 : 2);
+
+    XSync(xDisplay, False);
+}
+
+static int
+MakeOneColor (char *name, Pixel *color)
+{
+    XrmValue vFrom, vTo;
+    if (!appData.monoMode) {
+       vFrom.addr = (caddr_t) name;
+       vFrom.size = strlen(name);
+       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
+       if (vTo.addr == NULL) {
+         appData.monoMode = True;
+         return True;
+       } else {
+         *color = *(Pixel *) vTo.addr;
+       }
+    }
+    return False;
+}
+
+int
+MakeColors ()
+{   // [HGM] taken out of main(), so it can be called from BoardOptions dialog
+    int forceMono = False;
+
+    if (appData.lowTimeWarning)
+       forceMono |= MakeOneColor(appData.lowTimeWarningColor, &lowTimeWarningColor);
+    if(appData.dialogColor[0]) MakeOneColor(appData.dialogColor, &dialogColor);
+    if(appData.buttonColor[0]) MakeOneColor(appData.buttonColor, &buttonColor);
+
+    return forceMono;
+}
+
+void
+InitializeFonts (int clockFontPxlSize, int coordFontPxlSize, int fontPxlSize)
+{   // detervtomine what fonts to use, and create them
+    XrmValue vTo;
+    XrmDatabase xdb;
+
+    if(!fontIsSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
+       appData.clockFont = fontTable[CLOCK_FONT][squareSize];
+    if(!fontIsSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
+       appData.font = fontTable[MESSAGE_FONT][squareSize];
+    if(!fontIsSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
+       appData.coordFont = fontTable[COORD_FONT][squareSize];
+
+#if ENABLE_NLS
+    appData.font = InsertPxlSize(appData.font, fontPxlSize);
+    appData.clockFont = InsertPxlSize(appData.clockFont, clockFontPxlSize);
+    appData.coordFont = InsertPxlSize(appData.coordFont, coordFontPxlSize);
+    fontSet = CreateFontSet(appData.font);
+    clockFontSet = CreateFontSet(appData.clockFont);
+    {
+      /* For the coordFont, use the 0th font of the fontset. */
+      XFontSet coordFontSet = CreateFontSet(appData.coordFont);
+      XFontStruct **font_struct_list;
+      XFontSetExtents *fontSize;
+      char **font_name_list;
+      XFontsOfFontSet(coordFontSet, &font_struct_list, &font_name_list);
+      coordFontID = XLoadFont(xDisplay, font_name_list[0]);
+      coordFontStruct = XQueryFont(xDisplay, coordFontID);
+      fontSize = XExtentsOfFontSet(fontSet); // [HGM] figure out how much vertical space font takes
+      textHeight = fontSize->max_logical_extent.height + 5; // add borderWidth
+    }
+#else
+    appData.font = FindFont(appData.font, fontPxlSize);
+    appData.clockFont = FindFont(appData.clockFont, clockFontPxlSize);
+    appData.coordFont = FindFont(appData.coordFont, coordFontPxlSize);
+    clockFontID = XLoadFont(xDisplay, appData.clockFont);
+    clockFontStruct = XQueryFont(xDisplay, clockFontID);
+    coordFontID = XLoadFont(xDisplay, appData.coordFont);
+    coordFontStruct = XQueryFont(xDisplay, coordFontID);
+    // textHeight in !NLS mode!
+#endif
+    countFontID = coordFontID;  // [HGM] holdings
+    countFontStruct = coordFontStruct;
+
+    xdb = XtDatabase(xDisplay);
+#if ENABLE_NLS
+    XrmPutLineResource(&xdb, "*international: True");
+    vTo.size = sizeof(XFontSet);
+    vTo.addr = (XtPointer) &fontSet;
+    XrmPutResource(&xdb, "*fontSet", XtRFontSet, &vTo);
+#else
+    XrmPutStringResource(&xdb, "*font", appData.font);
+#endif
+}
+
+char *
+PrintArg (ArgType t)
+{
+  char *p="";
+  switch(t) {
+    case ArgZ:
+    case ArgInt:      p = " N"; break;
+    case ArgString:   p = " STR"; break;
+    case ArgBoolean:  p = " TF"; break;
+    case ArgSettingsFilename:
+    case ArgFilename: p = " FILE"; break;
+    case ArgX:        p = " Nx"; break;
+    case ArgY:        p = " Ny"; break;
+    case ArgAttribs:  p = " TEXTCOL"; break;
+    case ArgColor:    p = " COL"; break;
+    case ArgFont:     p = " FONT"; break;
+    case ArgBoardSize: p = " SIZE"; break;
+    case ArgFloat: p = " FLOAT"; break;
+    case ArgTrue:
+    case ArgFalse:
+    case ArgTwo:
+    case ArgNone:
+    case ArgCommSettings:
+      break;
+  }
+  return p;
+}
+
+void
+PrintOptions ()
+{
+  char buf[MSG_SIZ];
+  int len=0;
+  ArgDescriptor *q, *p = argDescriptors+5;
+  printf("\nXBoard accepts the following options:\n"
+         "(N = integer, TF = true or false, STR = text string, FILE = filename,\n"
+         " Nx, Ny = relative coordinates, COL = color, FONT = X-font spec,\n"
+         " SIZE = board-size spec(s)\n"
+         " Within parentheses are short forms, or options to set to true or false.\n"
+         " Persistent options (saved in the settings file) are marked with *)\n\n");
+  while(p->argName) {
+    if(p->argType == ArgCommSettings) { p++; continue; } // XBoard has no comm port
+    snprintf(buf+len, MSG_SIZ, "-%s%s", p->argName, PrintArg(p->argType));
+    if(p->save) strcat(buf+len, "*");
+    for(q=p+1; q->argLoc == p->argLoc; q++) {
+      if(q->argName[0] == '-') continue;
+      strcat(buf+len, q == p+1 ? " (" : " ");
+      sprintf(buf+strlen(buf), "-%s%s", q->argName, PrintArg(q->argType));
+    }
+    if(q != p+1) strcat(buf+len, ")");
+    len = strlen(buf);
+    if(len > 39) len = 0, printf("%s\n", buf); else while(len < 39) buf[len++] = ' ';
+    p = q;
+  }
+  if(len) buf[len] = NULLCHAR, printf("%s\n", buf);
+}
+
+int
+main (int argc, char **argv)
+{
+    int i, clockFontPxlSize, coordFontPxlSize, fontPxlSize;
+    XSetWindowAttributes window_attributes;
+    Arg args[16];
+    Dimension boardWidth, boardHeight, w, h;
+    char *p;
+    int forceMono = False;
+
+    srandom(time(0)); // [HGM] book: make random truly random
+
+    setbuf(stdout, NULL);
+    setbuf(stderr, NULL);
+    debugFP = stderr;
+
+    if(argc > 1 && (!strcmp(argv[1], "-v" ) || !strcmp(argv[1], "--version" ))) {
+       printf("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+       exit(0);
+    }
+
+    if(argc > 1 && !strcmp(argv[1], "--help" )) {
+       PrintOptions();
+       exit(0);
+    }
+
+    programName = strrchr(argv[0], '/');
+    if (programName == NULL)
+      programName = argv[0];
+    else
+      programName++;
+
+#ifdef ENABLE_NLS
+    XtSetLanguageProc(NULL, NULL, NULL);
+    if (appData.debugMode) {
+      fprintf(debugFP, "locale = %s\n", setlocale(LC_ALL, NULL));
+    }
+
+    bindtextdomain(PACKAGE, LOCALEDIR);
+    textdomain(PACKAGE);
+#endif
+
+    appData.boardSize = "";
+    InitAppData(ConvertToLine(argc, argv));
+    p = getenv("HOME");
+    if (p == NULL) p = "/tmp";
+    i = strlen(p) + strlen("/.xboardXXXXXx.pgn") + 1;
+    gameCopyFilename = (char*) malloc(i);
+    gamePasteFilename = (char*) malloc(i);
+    snprintf(gameCopyFilename,i, "%s/.xboard%05uc.pgn", p, getpid());
+    snprintf(gamePasteFilename,i, "%s/.xboard%05up.pgn", p, getpid());
+
+    { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
+       static char buf[MSG_SIZ];
+       EscapeExpand(buf, appData.firstInitString);
+       appData.firstInitString = strdup(buf);
+       EscapeExpand(buf, appData.secondInitString);
+       appData.secondInitString = strdup(buf);
+       EscapeExpand(buf, appData.firstComputerString);
+       appData.firstComputerString = strdup(buf);
+       EscapeExpand(buf, appData.secondComputerString);
+       appData.secondComputerString = strdup(buf);
+    }
+
+    if ((chessDir = (char *) getenv("CHESSDIR")) == NULL) {
+       chessDir = ".";
+    } else {
+       if (chdir(chessDir) != 0) {
+           fprintf(stderr, _("%s: can't cd to CHESSDIR: "), programName);
+           perror(chessDir);
+           exit(1);
+       }
+    }
+
+    if (appData.debugMode && appData.nameOfDebugFile && strcmp(appData.nameOfDebugFile, "stderr")) {
+       /* [DM] debug info to file [HGM] make the filename a command-line option, and allow it to remain stderr */
+        if ((debugFP = fopen(appData.nameOfDebugFile, "w")) == NULL)  {
+           printf(_("Failed to open file '%s'\n"), appData.nameOfDebugFile);
+           exit(errno);
+        }
+        setbuf(debugFP, NULL);
+    }
+
+    /* [HGM,HR] make sure board size is acceptable */
+    if(appData.NrFiles > BOARD_FILES ||
+       appData.NrRanks > BOARD_RANKS   )
+        DisplayFatalError(_("Recompile with larger BOARD_RANKS or BOARD_FILES to support this size"), 0, 2);
+
+#if !HIGHDRAG
+    /* This feature does not work; animation needs a rewrite */
+    appData.highlightDragging = FALSE;
+#endif
+    InitBackEnd1();
+
+       gameInfo.variant = StringToVariant(appData.variant);
+       InitPosition(FALSE);
+
+    shellWidget =
+      XtAppInitialize(&appContext, "XBoard", shellOptions,
+                     XtNumber(shellOptions),
+                     &argc, argv, xboardResources, NULL, 0);
+
+    XtGetApplicationResources(shellWidget, (XtPointer) &appData,
+                             clientResources, XtNumber(clientResources),
+                             NULL, 0);
+
+    xDisplay = XtDisplay(shellWidget);
+    xScreen = DefaultScreen(xDisplay);
+    wm_delete_window = XInternAtom(xDisplay, "WM_DELETE_WINDOW", True);
+
+    /*
+     * determine size, based on supplied or remembered -size, or screen size
+     */
+    if (isdigit(appData.boardSize[0])) {
+        i = sscanf(appData.boardSize, "%d,%d,%d,%d,%d,%d,%d", &squareSize,
+                  &lineGap, &clockFontPxlSize, &coordFontPxlSize,
+                  &fontPxlSize, &smallLayout, &tinyLayout);
+        if (i == 0) {
+           fprintf(stderr, _("%s: bad boardSize syntax %s\n"),
+                   programName, appData.boardSize);
+           exit(2);
+       }
+       if (i < 7) {
+           /* Find some defaults; use the nearest known size */
+           SizeDefaults *szd, *nearest;
+           int distance = 99999;
+           nearest = szd = sizeDefaults;
+           while (szd->name != NULL) {
+               if (abs(szd->squareSize - squareSize) < distance) {
+                   nearest = szd;
+                   distance = abs(szd->squareSize - squareSize);
+                   if (distance == 0) break;
+               }
+               szd++;
+           }
+           if (i < 2) lineGap = nearest->lineGap;
+           if (i < 3) clockFontPxlSize = nearest->clockFontPxlSize;
+           if (i < 4) coordFontPxlSize = nearest->coordFontPxlSize;
+           if (i < 5) fontPxlSize = nearest->fontPxlSize;
+           if (i < 6) smallLayout = nearest->smallLayout;
+           if (i < 7) tinyLayout = nearest->tinyLayout;
+       }
+    } else {
+        SizeDefaults *szd = sizeDefaults;
+        if (*appData.boardSize == NULLCHAR) {
+           while (DisplayWidth(xDisplay, xScreen) < szd->minScreenSize ||
+                  DisplayHeight(xDisplay, xScreen) < szd->minScreenSize) {
+             szd++;
+           }
+           if (szd->name == NULL) szd--;
+           appData.boardSize = strdup(szd->name); // [HGM] settings: remember name for saving settings
+       } else {
+           while (szd->name != NULL &&
+                  StrCaseCmp(szd->name, appData.boardSize) != 0) szd++;
+           if (szd->name == NULL) {
+               fprintf(stderr, _("%s: unrecognized boardSize name %s\n"),
+                       programName, appData.boardSize);
+               exit(2);
+           }
+       }
+       squareSize = szd->squareSize;
+       lineGap = szd->lineGap;
+       clockFontPxlSize = szd->clockFontPxlSize;
+       coordFontPxlSize = szd->coordFontPxlSize;
+       fontPxlSize = szd->fontPxlSize;
+       smallLayout = szd->smallLayout;
+       tinyLayout = szd->tinyLayout;
+       // [HGM] font: use defaults from settings file if available and not overruled
+    }
+
+    defaultLineGap = lineGap;
+    if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
+
+    /* [HR] height treated separately (hacked) */
+    boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
+    boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
+
+    /*
+     * Determine what fonts to use.
+     */
+    InitializeFonts(clockFontPxlSize, coordFontPxlSize, fontPxlSize);
+
+    /*
+     * Detect if there are not enough colors available and adapt.
+     */
+    if (DefaultDepth(xDisplay, xScreen) <= 2) {
+      appData.monoMode = True;
+    }
+
+    forceMono = MakeColors();
+
+    if (forceMono) {
+      fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
+             programName);
+       appData.monoMode = True;
+    }
+
+    if (appData.monoMode && appData.debugMode) {
+       fprintf(stderr, _("white pixel = 0x%lx, black pixel = 0x%lx\n"),
+               (unsigned long) XWhitePixel(xDisplay, xScreen),
+               (unsigned long) XBlackPixel(xDisplay, xScreen));
+    }
+
+    ParseIcsTextColors();
+
+    XtAppAddActions(appContext, boardActions, XtNumber(boardActions));
+
+    /*
+     * widget hierarchy
+     */
+    if (tinyLayout) {
+       layoutName = "tinyLayout";
+    } else if (smallLayout) {
+       layoutName = "smallLayout";
+    } else {
+       layoutName = "normalLayout";
+    }
+
+    optList = BoardPopUp(squareSize, lineGap, (void*)
+#if ENABLE_NLS
+                                               &clockFontSet);
+#else
+                                               clockFontStruct);
+#endif
+    InitDrawingHandle(optList + W_BOARD);
+    currBoard        = &optList[W_BOARD];
+    boardWidget      = optList[W_BOARD].handle;
+    menuBarWidget    = optList[W_MENU].handle;
+    dropMenu         = optList[W_DROP].handle;
+    titleWidget = optList[optList[W_TITLE].type != -1 ? W_TITLE : W_SMALL].handle;
+    formWidget  = XtParent(boardWidget);
+    XtSetArg(args[0], XtNbackground, &timerBackgroundPixel);
+    XtSetArg(args[1], XtNforeground, &timerForegroundPixel);
+    XtGetValues(optList[W_WHITE].handle, args, 2);
+    if (appData.showButtonBar) { // can't we use timer pixels for this? (Or better yet, just black & white?)
+      XtSetArg(args[0], XtNbackground, &buttonBackgroundPixel);
+      XtSetArg(args[1], XtNforeground, &buttonForegroundPixel);
+      XtGetValues(optList[W_PAUSE].handle, args, 2);
+    }
+    AppendEnginesToMenu(appData.recentEngineList);
+
+    xBoardWindow = XtWindow(boardWidget);
+
+    // [HGM] it seems the layout code ends here, but perhaps the color stuff is size independent and would
+    //       not need to go into InitDrawingSizes().
+
+    /*
+     * Create X checkmark bitmap and initialize option menu checks.
+     */
+    ReadBitmap(&xMarkPixmap, "checkmark.bm",
+              checkmark_bits, checkmark_width, checkmark_height);
+    InitMenuMarkers();
+
+    /*
+     * Create an icon.
+     */
+    ReadBitmap(&wIconPixmap, "icon_white.bm",
+              icon_white_bits, icon_white_width, icon_white_height);
+    ReadBitmap(&bIconPixmap, "icon_black.bm",
+              icon_black_bits, icon_black_width, icon_black_height);
+    iconPixmap = wIconPixmap;
+    i = 0;
+    XtSetArg(args[i], XtNiconPixmap, iconPixmap);  i++;
+    XtSetValues(shellWidget, args, i);
+
+    /*
+     * Create a cursor for the board widget.
+     */
+    window_attributes.cursor = XCreateFontCursor(xDisplay, XC_hand2);
+    XChangeWindowAttributes(xDisplay, xBoardWindow,
+                           CWCursor, &window_attributes);
+
+    /*
+     * Inhibit shell resizing.
+     */
+    shellArgs[0].value = (XtArgVal) &w;
+    shellArgs[1].value = (XtArgVal) &h;
+    XtGetValues(shellWidget, shellArgs, 2);
+    shellArgs[4].value = shellArgs[2].value = w;
+    shellArgs[5].value = shellArgs[3].value = h;
+//    XtSetValues(shellWidget, &shellArgs[2], 4);
+    marginW =  w - boardWidth; // [HGM] needed to set new shellWidget size when we resize board
+    marginH =  h - boardHeight;
+
+    CatchDeleteWindow(shellWidget, "QuitProc");
+
+    CreateAnyPieces();
+    CreateGrid();
+
+    if(appData.logoSize)
+    {   // locate and read user logo
+       char buf[MSG_SIZ];
+       snprintf(buf, MSG_SIZ, "%s/%s.png", appData.logoDir, UserName());
+       ASSIGN(userLogo, buf);
+    }
+
+    if (appData.animate || appData.animateDragging)
+      CreateAnimVars();
+
+    XtAugmentTranslations(formWidget,
+                         XtParseTranslationTable(globalTranslations));
+
+    XtAddEventHandler(formWidget, KeyPressMask, False,
+                     (XtEventHandler) MoveTypeInProc, NULL);
+    XtAddEventHandler(shellWidget, StructureNotifyMask, False,
+                     (XtEventHandler) EventProc, NULL);
+
+    /* [AS] Restore layout */
+    if( wpMoveHistory.visible ) {
+      HistoryPopUp();
+    }
+
+    if( wpEvalGraph.visible )
+      {
+       EvalGraphPopUp();
+      };
+
+    if( wpEngineOutput.visible ) {
+      EngineOutputPopUp();
+    }
+
+    InitBackEnd2();
+
+    if (errorExitStatus == -1) {
+       if (appData.icsActive) {
+           /* We now wait until we see "login:" from the ICS before
+              sending the logon script (problems with timestamp otherwise) */
+           /*ICSInitScript();*/
+           if (appData.icsInputBox) ICSInputBoxPopUp();
+       }
+
+    #ifdef SIGWINCH
+    signal(SIGWINCH, TermSizeSigHandler);
+    #endif
+       signal(SIGINT, IntSigHandler);
+       signal(SIGTERM, IntSigHandler);
+       if (*appData.cmailGameName != NULLCHAR) {
+           signal(SIGUSR1, CmailSigHandler);
+       }
+    }
+
+    gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
+    InitPosition(TRUE);
+    UpdateLogos(TRUE);
+//    XtSetKeyboardFocus(shellWidget, formWidget);
+    XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
+
+    XtAppMainLoop(appContext);
+    if (appData.debugMode) fclose(debugFP); // [DM] debug
+    return 0;
+}
+
+RETSIGTYPE
+TermSizeSigHandler (int sig)
+{
+    update_ics_width();
+}
+
+RETSIGTYPE
+IntSigHandler (int sig)
+{
+    ExitEvent(sig);
+}
+
+RETSIGTYPE
+CmailSigHandler (int sig)
+{
+    int dummy = 0;
+    int error;
+
+    signal(SIGUSR1, SIG_IGN);  /* suspend handler     */
+
+    /* Activate call-back function CmailSigHandlerCallBack()             */
+    OutputToProcess(cmailPR, (char *)(&dummy), sizeof(int), &error);
+
+    signal(SIGUSR1, CmailSigHandler); /* re-activate handler */
+}
+
+void
+CmailSigHandlerCallBack (InputSourceRef isr, VOIDSTAR closure, char *message, int count, int error)
+{
+    BoardToTop();
+    ReloadCmailMsgEvent(TRUE); /* Reload cmail msg  */
+}
+/**** end signal code ****/
+
+
+#define Abs(n) ((n)<0 ? -(n) : (n))
+
+#ifdef ENABLE_NLS
+char *
+InsertPxlSize (char *pattern, int targetPxlSize)
+{
+    char *base_fnt_lst, strInt[12], *p, *q;
+    int alternatives, i, len, strIntLen;
+
+    /*
+     * Replace the "*" (if present) in the pixel-size slot of each
+     * alternative with the targetPxlSize.
+     */
+    p = pattern;
+    alternatives = 1;
+    while ((p = strchr(p, ',')) != NULL) {
+      alternatives++;
+      p++;
+    }
+    snprintf(strInt, sizeof(strInt), "%d", targetPxlSize);
+    strIntLen = strlen(strInt);
+    base_fnt_lst = calloc(1, strlen(pattern) + strIntLen * alternatives + 1);
+
+    p = pattern;
+    q = base_fnt_lst;
+    while (alternatives--) {
+      char *comma = strchr(p, ',');
+      for (i=0; i<14; i++) {
+       char *hyphen = strchr(p, '-');
+       if (!hyphen) break;
+       if (comma && hyphen > comma) break;
+       len = hyphen + 1 - p;
+       if (i == 7 && *p == '*' && len == 2) {
+         p += len;
+         memcpy(q, strInt, strIntLen);
+         q += strIntLen;
+         *q++ = '-';
+       } else {
+         memcpy(q, p, len);
+         p += len;
+         q += len;
+       }
+      }
+      if (!comma) break;
+      len = comma + 1 - p;
+      memcpy(q, p, len);
+      p += len;
+      q += len;
+    }
+    strcpy(q, p);
+
+    return base_fnt_lst;
+}
+
+XFontSet
+CreateFontSet (char *base_fnt_lst)
+{
+    XFontSet fntSet;
+    char **missing_list;
+    int missing_count;
+    char *def_string;
+
+    fntSet = XCreateFontSet(xDisplay, base_fnt_lst,
+                           &missing_list, &missing_count, &def_string);
+    if (appData.debugMode) {
+      int i, count;
+      XFontStruct **font_struct_list;
+      char **font_name_list;
+      fprintf(debugFP, "Requested font set for list %s\n", base_fnt_lst);
+      if (fntSet) {
+       fprintf(debugFP, " got list %s, locale %s\n",
+               XBaseFontNameListOfFontSet(fntSet),
+               XLocaleOfFontSet(fntSet));
+       count = XFontsOfFontSet(fntSet, &font_struct_list, &font_name_list);
+       for (i = 0; i < count; i++) {
+         fprintf(debugFP, " got charset %s\n", font_name_list[i]);
+       }
+      }
+      for (i = 0; i < missing_count; i++) {
+       fprintf(debugFP, " missing charset %s\n", missing_list[i]);
+      }
+    }
+    if (fntSet == NULL) {
+      fprintf(stderr, _("Unable to create font set for %s.\n"), base_fnt_lst);
+      exit(2);
+    }
+    return fntSet;
+}
+#else // not ENABLE_NLS
+/*
+ * Find a font that matches "pattern" that is as close as
+ * possible to the targetPxlSize.  Prefer fonts that are k
+ * pixels smaller to fonts that are k pixels larger.  The
+ * pattern must be in the X Consortium standard format,
+ * e.g. "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*".
+ * The return value should be freed with XtFree when no
+ * longer needed.
+ */
+char *
+FindFont (char *pattern, int targetPxlSize)
+{
+    char **fonts, *p, *best, *scalable, *scalableTail;
+    int i, j, nfonts, minerr, err, pxlSize;
+
+    fonts = XListFonts(xDisplay, pattern, 999999, &nfonts);
+    if (nfonts < 1) {
+       fprintf(stderr, _("%s: no fonts match pattern %s\n"),
+               programName, pattern);
+       exit(2);
+    }
+
+    best = fonts[0];
+    scalable = NULL;
+    minerr = 999999;
+    for (i=0; i<nfonts; i++) {
+       j = 0;
+       p = fonts[i];
+       if (*p != '-') continue;
+       while (j < 7) {
+           if (*p == NULLCHAR) break;
+           if (*p++ == '-') j++;
+       }
+       if (j < 7) continue;
+       pxlSize = atoi(p);
+       if (pxlSize == 0) {
+           scalable = fonts[i];
+           scalableTail = p;
+       } else {
+           err = pxlSize - targetPxlSize;
+           if (Abs(err) < Abs(minerr) ||
+               (minerr > 0 && err < 0 && -err == minerr)) {
+               best = fonts[i];
+               minerr = err;
+           }
+       }
+    }
+    if (scalable && Abs(minerr) > appData.fontSizeTolerance) {
+        /* If the error is too big and there is a scalable font,
+          use the scalable font. */
+        int headlen = scalableTail - scalable;
+        p = (char *) XtMalloc(strlen(scalable) + 10);
+       while (isdigit(*scalableTail)) scalableTail++;
+       sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
+    } else {
+        p = (char *) XtMalloc(strlen(best) + 2);
+        safeStrCpy(p, best, strlen(best)+1 );
+    }
+    if (appData.debugMode) {
+        fprintf(debugFP, _("resolved %s at pixel size %d\n  to %s\n"),
+               pattern, targetPxlSize, p);
+    }
+    XFreeFontNames(fonts);
+    return p;
+}
+#endif
+
+void
+ReadBitmap (Pixmap *pm, String name, unsigned char bits[], u_int wreq, u_int hreq)
+{
+    if (bits != NULL) {
+       *pm = XCreateBitmapFromData(xDisplay, xBoardWindow, (char *) bits,
+                                   wreq, hreq);
+    }
+}
+
+void
+MarkMenuItem (char *menuRef, int state)
+{
+    MenuItem *item = MenuNameToItem(menuRef);
+
+    if(item) {
+       Arg args[2];
+       XtSetArg(args[0], XtNleftBitmap, state ? xMarkPixmap : None);
+       XtSetValues(item->handle, args, 1);
+    }
+}
+
+void
+EnableNamedMenuItem (char *menuRef, int state)
+{
+    MenuItem *item = MenuNameToItem(menuRef);
+
+    if(item) XtSetSensitive(item->handle, state);
+}
+
+void
+EnableButtonBar (int state)
+{
+    XtSetSensitive(optList[W_BUTTON].handle, state);
+}
+
+
+void
+SetMenuEnables (Enables *enab)
+{
+  while (enab->name != NULL) {
+    EnableNamedMenuItem(enab->name, enab->value);
+    enab++;
+  }
+}
+
+void
+KeyBindingProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{   // [HGM] new method of key binding: specify MenuItem(FlipView) in stead of FlipViewProc in translation string
+    MenuItem *item;
+    if(*nprms == 0) return;
+    item = MenuNameToItem(prms[0]);
+    if(item) ((MenuProc *) item->proc) ();
+}
+
+static void
+MenuEngineSelect (Widget w, caddr_t addr, caddr_t index)
+{
+    RecentEngineEvent((int) (intptr_t) addr);
+}
+
+void
+AppendMenuItem (char *msg, int n)
+{
+    CreateMenuItem((Widget) optList[W_ENGIN].textValue, msg, (XtCallbackProc) MenuEngineSelect, n);
+}
+
+void
+SetupDropMenu ()
+{
+    int i, j, count;
+    char label[32];
+    Arg args[16];
+    Widget entry;
+    char* p;
+
+    for (i=0; i<sizeof(dmEnables)/sizeof(DropMenuEnables); i++) {
+       entry = XtNameToWidget(dropMenu, dmEnables[i].widget);
+       p = strchr(gameMode == IcsPlayingWhite ? white_holding : black_holding,
+                  dmEnables[i].piece);
+       XtSetSensitive(entry, p != NULL || !appData.testLegality
+                      /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse
+                                      && !appData.icsActive));
+       count = 0;
+       while (p && *p++ == dmEnables[i].piece) count++;
+       snprintf(label, sizeof(label), "%s  %d", dmEnables[i].widget, count);
+       j = 0;
+       XtSetArg(args[j], XtNlabel, label); j++;
+       XtSetValues(entry, args, j);
+    }
+}
+
+static void
+do_flash_delay (unsigned long msec)
+{
+    TimeDelay(msec);
+}
+
+void
+FlashDelay (int flash_delay)
+{
+       XSync(xDisplay, False);
+       if(flash_delay) do_flash_delay(flash_delay);
+}
+
+double
+Fraction (int x, int start, int stop)
+{
+   double f = ((double) x - start)/(stop - start);
+   if(f > 1.) f = 1.; else if(f < 0.) f = 0.;
+   return f;
+}
+
+static WindowPlacement wpNew;
+
+void
+CoDrag (Widget sh, WindowPlacement *wp)
+{
+    Arg args[16];
+    int j=0, touch=0, fudge = 2;
+    GetActualPlacement(sh, wp);
+    if(abs(wpMain.x + wpMain.width + 2*frameX - wp->x)         < fudge) touch = 1; else // right touch
+    if(abs(wp->x + wp->width + 2*frameX - wpMain.x)            < fudge) touch = 2; else // left touch
+    if(abs(wpMain.y + wpMain.height + frameX + frameY - wp->y) < fudge) touch = 3; else // bottom touch
+    if(abs(wp->y + wp->height + frameX + frameY - wpMain.y)    < fudge) touch = 4;      // top touch
+    if(!touch ) return; // only windows that touch co-move
+    if(touch < 3 && wpNew.height != wpMain.height) { // left or right and height changed
+       int heightInc = wpNew.height - wpMain.height;
+       double fracTop = Fraction(wp->y, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
+       double fracBot = Fraction(wp->y + wp->height + frameX + frameY + 1, wpMain.y, wpMain.y + wpMain.height + frameX + frameY);
+       wp->y += fracTop * heightInc;
+       heightInc = (int) (fracBot * heightInc) - (int) (fracTop * heightInc);
+       if(heightInc) XtSetArg(args[j], XtNheight, wp->height + heightInc), j++;
+    } else if(touch > 2 && wpNew.width != wpMain.width) { // top or bottom and width changed
+       int widthInc = wpNew.width - wpMain.width;
+       double fracLeft = Fraction(wp->x, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
+       double fracRght = Fraction(wp->x + wp->width + 2*frameX + 1, wpMain.x, wpMain.x + wpMain.width + 2*frameX);
+       wp->y += fracLeft * widthInc;
+       widthInc = (int) (fracRght * widthInc) - (int) (fracLeft * widthInc);
+       if(widthInc) XtSetArg(args[j], XtNwidth, wp->width + widthInc), j++;
+    }
+    wp->x += wpNew.x - wpMain.x;
+    wp->y += wpNew.y - wpMain.y;
+    if(touch == 1) wp->x += wpNew.width - wpMain.width; else
+    if(touch == 3) wp->y += wpNew.height - wpMain.height;
+    XtSetArg(args[j], XtNx, wp->x); j++;
+    XtSetArg(args[j], XtNy, wp->y); j++;
+    XtSetValues(sh, args, j);
+}
+
+void
+ReSize (WindowPlacement *wp)
+{
+       int sqx, sqy, w, h;
+       if(wp->width == wpMain.width && wp->height == wpMain.height) return; // not sized
+       sqx = (wp->width  - lineGap - marginW) / BOARD_WIDTH - lineGap;
+       sqy = (wp->height - lineGap - marginH) / BOARD_HEIGHT - lineGap;
+       if(sqy < sqx) sqx = sqy;
+       if(sqx != squareSize) {
+           squareSize = sqx; // adopt new square size
+           CreatePNGPieces(); // make newly scaled pieces
+           InitDrawingSizes(0, 0); // creates grid etc.
+       } else ResizeBoardWindow(BOARD_WIDTH * (squareSize + lineGap) + lineGap, BOARD_HEIGHT * (squareSize + lineGap) + lineGap, 0);
+       w = BOARD_WIDTH * (squareSize + lineGap) + lineGap;
+       h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap;
+       if(optList[W_BOARD].max   > w) optList[W_BOARD].max = w;
+       if(optList[W_BOARD].value > h) optList[W_BOARD].value = h;
+}
+
+static XtIntervalId delayedDragID = 0;
+
+void
+DragProc ()
+{
+       static int busy;
+       if(busy) return;
+
+       busy = 1;
+       GetActualPlacement(shellWidget, &wpNew);
+       if(wpNew.x == wpMain.x && wpNew.y == wpMain.y && // not moved
+          wpNew.width == wpMain.width && wpNew.height == wpMain.height) { // not sized
+           busy = 0; return; // false alarm
+       }
+       ReSize(&wpNew);
+       if(shellUp[EngOutDlg]) CoDrag(shells[EngOutDlg], &wpEngineOutput);
+       if(shellUp[HistoryDlg]) CoDrag(shells[HistoryDlg], &wpMoveHistory);
+       if(shellUp[EvalGraphDlg]) CoDrag(shells[EvalGraphDlg], &wpEvalGraph);
+       if(shellUp[GameListDlg]) CoDrag(shells[GameListDlg], &wpGameList);
+       wpMain = wpNew;
+       DrawPosition(True, NULL);
+       delayedDragID = 0; // now drag executed, make sure next DelayedDrag will not cancel timer event (which could now be used by other)
+       busy = 0;
+}
+
+
+void
+DelayedDrag ()
+{
+    if(delayedDragID) XtRemoveTimeOut(delayedDragID); // cancel pending
+    delayedDragID =
+      XtAppAddTimeOut(appContext, 200, (XtTimerCallbackProc) DragProc, (XtPointer) 0); // and schedule new one 50 msec later
+}
+
+void
+EventProc (Widget widget, caddr_t unused, XEvent *event)
+{
+    if(XtIsRealized(widget) && event->type == ConfigureNotify || appData.useStickyWindows)
+       DelayedDrag(); // as long as events keep coming in faster than 50 msec, they destroy each other
+}
+
+/*
+ * event handler for redrawing the board
+ */
+void
+DrawPositionProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+    DrawPosition(True, NULL);
+}
+
+
+void
+HandlePV (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{   // [HGM] pv: walk PV
+    MovePV(event->xmotion.x, event->xmotion.y, lineGap + BOARD_HEIGHT * (squareSize + lineGap));
+}
+
+static int savedIndex;  /* gross that this is global */
+
+void
+CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{
+       String val;
+       XawTextPosition index, dummy;
+       Arg arg;
+
+       XawTextGetSelectionPos(w, &index, &dummy);
+       XtSetArg(arg, XtNstring, &val);
+       XtGetValues(w, &arg, 1);
+       ReplaceComment(savedIndex, val);
+       if(savedIndex != currentMove) ToNrEvent(savedIndex);
+       LoadVariation( index, val ); // [HGM] also does the actual moving to it, now
+}
+
+void
+EditCommentPopUp (int index, char *title, char *text)
+{
+    savedIndex = index;
+    if (text == NULL) text = "";
+    NewCommentPopup(title, text, index);
+}
+
+void
+CommentPopUp (char *title, char *text)
+{
+    savedIndex = currentMove; // [HGM] vari
+    NewCommentPopup(title, text, currentMove);
+}
+
+void
+CommentPopDown ()
+{
+    PopDown(CommentDlg);
+}
+
+
+/* Disable all user input other than deleting the window */
+static int frozen = 0;
+
+void
+FreezeUI ()
+{
+  if (frozen) return;
+  /* Grab by a widget that doesn't accept input */
+  XtAddGrab(optList[W_MESSG].handle, TRUE, FALSE);
+  frozen = 1;
+}
+
+/* Undo a FreezeUI */
+void
+ThawUI ()
+{
+  if (!frozen) return;
+  XtRemoveGrab(optList[W_MESSG].handle);
+  frozen = 0;
+}
+
+void
+ModeHighlight ()
+{
+    Arg args[16];
+    static int oldPausing = FALSE;
+    static GameMode oldmode = (GameMode) -1;
+    char *wname;
+
+    if (!boardWidget || !XtIsRealized(boardWidget)) return;
+
+    if (pausing != oldPausing) {
+       oldPausing = pausing;
+       MarkMenuItem("Mode.Pause", pausing);
+
+       if (appData.showButtonBar) {
+         /* Always toggle, don't set.  Previous code messes up when
+            invoked while the button is pressed, as releasing it
+            toggles the state again. */
+         {
+           Pixel oldbg, oldfg;
+           XtSetArg(args[0], XtNbackground, &oldbg);
+           XtSetArg(args[1], XtNforeground, &oldfg);
+           XtGetValues(optList[W_PAUSE].handle,
+                       args, 2);
+           XtSetArg(args[0], XtNbackground, oldfg);
+           XtSetArg(args[1], XtNforeground, oldbg);
+         }
+         XtSetValues(optList[W_PAUSE].handle, args, 2);
+       }
+    }
+
+    wname = ModeToWidgetName(oldmode);
+    if (wname != NULL) {
+       MarkMenuItem(wname, False);
+    }
+    wname = ModeToWidgetName(gameMode);
+    if (wname != NULL) {
+       MarkMenuItem(wname, True);
+    }
+    oldmode = gameMode;
+    MarkMenuItem("Mode.MachineMatch", matchMode && matchGame < appData.matchGames);
+
+    /* Maybe all the enables should be handled here, not just this one */
+    EnableNamedMenuItem("Mode.Training", gameMode == Training || gameMode == PlayFromGameFile);
+
+    DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
+}
+
+
+/*
+ * Button/menu procedures
+ */
+
+/* this variable is shared between CopyPositionProc and SendPositionSelection */
+char *selected_fen_position=NULL;
+
+Boolean
+SendPositionSelection (Widget w, Atom *selection, Atom *target,
+                      Atom *type_return, XtPointer *value_return,
+                      unsigned long *length_return, int *format_return)
+{
+  char *selection_tmp;
+
+//  if (!selected_fen_position) return False; /* should never happen */
+  if (*target == XA_STRING || *target == XA_UTF8_STRING(xDisplay)){
+   if (!selected_fen_position) { // since it never happens, we use it for indicating a game is being sent
+    FILE* f = fopen(gameCopyFilename, "r"); // This code, taken from SendGameSelection, now merges the two
+    long len;
+    size_t count;
+    if (f == NULL) return False;
+    fseek(f, 0, 2);
+    len = ftell(f);
+    rewind(f);
+    selection_tmp = XtMalloc(len + 1);
+    count = fread(selection_tmp, 1, len, f);
+    fclose(f);
+    if (len != count) {
+      XtFree(selection_tmp);
+      return False;
+    }
+    selection_tmp[len] = NULLCHAR;
+   } else {
+    /* note: since no XtSelectionDoneProc was registered, Xt will
+     * automatically call XtFree on the value returned.  So have to
+     * make a copy of it allocated with XtMalloc */
+    selection_tmp= XtMalloc(strlen(selected_fen_position)+16);
+    safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
+   }
+
+    *value_return=selection_tmp;
+    *length_return=strlen(selection_tmp);
+    *type_return=*target;
+    *format_return = 8; /* bits per byte */
+    return True;
+  } else if (*target == XA_TARGETS(xDisplay)) {
+    Atom *targets_tmp = (Atom *) XtMalloc(2 * sizeof(Atom));
+    targets_tmp[0] = XA_UTF8_STRING(xDisplay);
+    targets_tmp[1] = XA_STRING;
+    *value_return = targets_tmp;
+    *type_return = XA_ATOM;
+    *length_return = 2;
+#if 0
+    // This code leads to a read of value_return out of bounds on 64-bit systems.
+    // Other code which I have seen always sets *format_return to 32 independent of
+    // sizeof(Atom) without adjusting *length_return. For instance see TextConvertSelection()
+    // at http://cgit.freedesktop.org/xorg/lib/libXaw/tree/src/Text.c -- BJ
+    *format_return = 8 * sizeof(Atom);
+    if (*format_return > 32) {
+      *length_return *= *format_return / 32;
+      *format_return = 32;
+    }
+#else
+    *format_return = 32;
+#endif
+    return True;
+  } else {
+    return False;
+  }
+}
+
+/* note: when called from menu all parameters are NULL, so no clue what the
+ * Widget which was clicked on was, or what the click event was
+ */
+void
+CopySomething (char *src)
+{
+    selected_fen_position = src;
+    /*
+     * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
+     * have a notion of a position that is selected but not copied.
+     * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
+     */
+    XtOwnSelection(menuBarWidget, XA_PRIMARY,
+                  CurrentTime,
+                  SendPositionSelection,
+                  NULL/* lose_ownership_proc */ ,
+                  NULL/* transfer_done_proc */);
+    XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
+                  CurrentTime,
+                  SendPositionSelection,
+                  NULL/* lose_ownership_proc */ ,
+                  NULL/* transfer_done_proc */);
+}
+
+/* function called when the data to Paste is ready */
+static void
+PastePositionCB (Widget w, XtPointer client_data, Atom *selection,
+                Atom *type, XtPointer value, unsigned long *len, int *format)
+{
+  char *fenstr=value;
+  if (value==NULL || *len==0) return; /* nothing had been selected to copy */
+  fenstr[*len]='\0'; /* normally this string is terminated, but be safe */
+  EditPositionPasteFEN(fenstr);
+  XtFree(value);
+}
+
+/* called when Paste Position button is pressed,
+ * all parameters will be NULL */
+void
+PastePositionProc ()
+{
+    XtGetSelectionValue(menuBarWidget,
+      appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
+      /* (XtSelectionCallbackProc) */ PastePositionCB,
+      NULL, /* client_data passed to PastePositionCB */
+
+      /* better to use the time field from the event that triggered the
+       * call to this function, but that isn't trivial to get
+       */
+      CurrentTime
+    );
+    return;
+}
+
+/* note: when called from menu all parameters are NULL, so no clue what the
+ * Widget which was clicked on was, or what the click event was
+ */
+/* function called when the data to Paste is ready */
+static void
+PasteGameCB (Widget w, XtPointer client_data, Atom *selection,
+            Atom *type, XtPointer value, unsigned long *len, int *format)
+{
+  FILE* f;
+  if (value == NULL || *len == 0) {
+    return; /* nothing had been selected to copy */
+  }
+  f = fopen(gamePasteFilename, "w");
+  if (f == NULL) {
+    DisplayError(_("Can't open temp file"), errno);
+    return;
+  }
+  fwrite(value, 1, *len, f);
+  fclose(f);
+  XtFree(value);
+  LoadGameFromFile(gamePasteFilename, 0, gamePasteFilename, TRUE);
+}
+
+/* called when Paste Game button is pressed,
+ * all parameters will be NULL */
+void
+PasteGameProc ()
+{
+    XtGetSelectionValue(menuBarWidget,
+      appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
+      /* (XtSelectionCallbackProc) */ PasteGameCB,
+      NULL, /* client_data passed to PasteGameCB */
+
+      /* better to use the time field from the event that triggered the
+       * call to this function, but that isn't trivial to get
+       */
+      CurrentTime
+    );
+    return;
+}
+
+
+void
+QuitWrapper (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+    QuitProc();
+}
+
+int
+ShiftKeys ()
+{   // bassic primitive for determining if modifier keys are pressed
+    long int codes[] = { XK_Meta_L, XK_Meta_R, XK_Control_L, XK_Control_R, XK_Shift_L, XK_Shift_R };
+    char keys[32];
+    int i,j,  k=0;
+    XQueryKeymap(xDisplay,keys);
+    for(i=0; i<6; i++) {
+       k <<= 1;
+       j = XKeysymToKeycode(xDisplay, codes[i]);
+       k += ( (keys[j>>3]&1<<(j&7)) != 0 );
+    }
+    return k;
+}
+
+static void
+MoveTypeInProc (Widget widget, caddr_t unused, XEvent *event)
+{
+    char buf[10];
+    KeySym sym;
+    int n = XLookupString(&(event->xkey), buf, 10, &sym, NULL);
+    if ( n == 1 && *buf >= 32 // printable
+        && !(ShiftKeys() & 0x3C) // no Alt, Ctrl
+       ) BoxAutoPopUp (buf);
+}
+
+static void
+UpKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{   // [HGM] input: let up-arrow recall previous line from history
+    IcsKey(1);
+}
+
+static void
+DownKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{   // [HGM] input: let down-arrow recall next line from history
+    IcsKey(-1);
+}
+
+static void
+EnterKeyProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+    IcsKey(0);
+}
+
+void
+TempBackwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+       if (!TempBackwardActive) {
+               TempBackwardActive = True;
+               BackwardEvent();
+       }
+}
+
+void
+TempForwardProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+       /* Check to see if triggered by a key release event for a repeating key.
+        * If so the next queued event will be a key press of the same key at the same time */
+       if (XEventsQueued(xDisplay, QueuedAfterReading)) {
+               XEvent next;
+               XPeekEvent(xDisplay, &next);
+               if (next.type == KeyPress && next.xkey.time == event->xkey.time &&
+                       next.xkey.keycode == event->xkey.keycode)
+                               return;
+       }
+    ForwardEvent();
+       TempBackwardActive = False;
+}
+
+void
+ManInner (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{   // called as key binding
+    char buf[MSG_SIZ];
+    String name;
+    if (nprms && *nprms > 0)
+      name = prms[0];
+    else
+      name = "xboard";
+    snprintf(buf, sizeof(buf), "xterm -e man %s &", name);
+    system(buf);
+}
+
+void
+ManProc ()
+{   // called from menu
+    ManInner(NULL, NULL, NULL, NULL);
+}
+
+void
+SetWindowTitle (char *text, char *title, char *icon)
+{
+    Arg args[16];
+    int i;
+    if (appData.titleInWindow) {
+       i = 0;
+       XtSetArg(args[i], XtNlabel, text);   i++;
+       XtSetValues(titleWidget, args, i);
+    }
+    i = 0;
+    XtSetArg(args[i], XtNiconName, (XtArgVal) icon);    i++;
+    XtSetArg(args[i], XtNtitle, (XtArgVal) title);      i++;
+    XtSetValues(shellWidget, args, i);
+    XSync(xDisplay, False);
+}
+
+
+static int
+NullXErrorCheck (Display *dpy, XErrorEvent *error_event)
+{
+    return 0;
+}
+
+void
+DisplayIcsInteractionTitle (String message)
+{
+  if (oldICSInteractionTitle == NULL) {
+    /* Magic to find the old window title, adapted from vim */
+    char *wina = getenv("WINDOWID");
+    if (wina != NULL) {
+      Window win = (Window) atoi(wina);
+      Window root, parent, *children;
+      unsigned int nchildren;
+      int (*oldHandler)() = XSetErrorHandler(NullXErrorCheck);
+      for (;;) {
+       if (XFetchName(xDisplay, win, &oldICSInteractionTitle)) break;
+       if (!XQueryTree(xDisplay, win, &root, &parent,
+                       &children, &nchildren)) break;
+       if (children) XFree((void *)children);
+       if (parent == root || parent == 0) break;
+       win = parent;
+      }
+      XSetErrorHandler(oldHandler);
+    }
+    if (oldICSInteractionTitle == NULL) {
+      oldICSInteractionTitle = "xterm";
+    }
+  }
+  printf("\033]0;%s\007", message);
+  fflush(stdout);
+}
+
+
+XtIntervalId delayedEventTimerXID = 0;
+DelayedEventCallback delayedEventCallback = 0;
+
+void
+FireDelayedEvent ()
+{
+    delayedEventTimerXID = 0;
+    delayedEventCallback();
+}
+
+void
+ScheduleDelayedEvent (DelayedEventCallback cb, long millisec)
+{
+    if(delayedEventTimerXID && delayedEventCallback == cb)
+       // [HGM] alive: replace, rather than add or flush identical event
+       XtRemoveTimeOut(delayedEventTimerXID);
+    delayedEventCallback = cb;
+    delayedEventTimerXID =
+      XtAppAddTimeOut(appContext, millisec,
+                     (XtTimerCallbackProc) FireDelayedEvent, (XtPointer) 0);
+}
+
+DelayedEventCallback
+GetDelayedEvent ()
+{
+  if (delayedEventTimerXID) {
+    return delayedEventCallback;
+  } else {
+    return NULL;
+  }
+}
+
+void
+CancelDelayedEvent ()
+{
+  if (delayedEventTimerXID) {
+    XtRemoveTimeOut(delayedEventTimerXID);
+    delayedEventTimerXID = 0;
+  }
+}
+
+XtIntervalId loadGameTimerXID = 0;
+
+int
+LoadGameTimerRunning ()
+{
+    return loadGameTimerXID != 0;
+}
+
+int
+StopLoadGameTimer ()
+{
+    if (loadGameTimerXID != 0) {
+       XtRemoveTimeOut(loadGameTimerXID);
+       loadGameTimerXID = 0;
+       return TRUE;
+    } else {
+       return FALSE;
+    }
+}
+
+void
+LoadGameTimerCallback (XtPointer arg, XtIntervalId *id)
+{
+    loadGameTimerXID = 0;
+    AutoPlayGameLoop();
+}
+
+void
+StartLoadGameTimer (long millisec)
+{
+    loadGameTimerXID =
+      XtAppAddTimeOut(appContext, millisec,
+                     (XtTimerCallbackProc) LoadGameTimerCallback,
+                     (XtPointer) 0);
+}
+
+XtIntervalId analysisClockXID = 0;
+
+void
+AnalysisClockCallback (XtPointer arg, XtIntervalId *id)
+{
+    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
+         || appData.icsEngineAnalyze) { // [DM]
+       AnalysisPeriodicEvent(0);
+       StartAnalysisClock();
+    }
+}
+
+void
+StartAnalysisClock ()
+{
+    analysisClockXID =
+      XtAppAddTimeOut(appContext, 2000,
+                     (XtTimerCallbackProc) AnalysisClockCallback,
+                     (XtPointer) 0);
+}
+
+XtIntervalId clockTimerXID = 0;
+
+int
+ClockTimerRunning ()
+{
+    return clockTimerXID != 0;
+}
+
+int
+StopClockTimer ()
+{
+    if (clockTimerXID != 0) {
+       XtRemoveTimeOut(clockTimerXID);
+       clockTimerXID = 0;
+       return TRUE;
+    } else {
+       return FALSE;
+    }
+}
+
+void
+ClockTimerCallback (XtPointer arg, XtIntervalId *id)
+{
+    clockTimerXID = 0;
+    DecrementClocks();
+}
+
+void
+StartClockTimer (long millisec)
+{
+    clockTimerXID =
+      XtAppAddTimeOut(appContext, millisec,
+                     (XtTimerCallbackProc) ClockTimerCallback,
+                     (XtPointer) 0);
+}
+
+void
+DisplayTimerLabel (Option *opt, char *color, long timer, int highlight)
+{
+    char buf[MSG_SIZ];
+    Arg args[16];
+    Widget w = (Widget) opt->handle;
+
+    /* check for low time warning */
+    Pixel foregroundOrWarningColor = timerForegroundPixel;
+
+    if (timer > 0 &&
+        appData.lowTimeWarning &&
+        (timer / 1000) < appData.icsAlarmTime)
+      foregroundOrWarningColor = lowTimeWarningColor;
+
+    if (appData.clockMode) {
+      snprintf(buf, MSG_SIZ, "%s:%s%s", color, appData.logoSize && !partnerUp ? "\n" : " ", TimeString(timer));
+      XtSetArg(args[0], XtNlabel, buf);
+    } else {
+      snprintf(buf, MSG_SIZ, "%s  ", color);
+      XtSetArg(args[0], XtNlabel, buf);
+    }
+
+    if (highlight) {
+
+       XtSetArg(args[1], XtNbackground, foregroundOrWarningColor);
+       XtSetArg(args[2], XtNforeground, timerBackgroundPixel);
+    } else {
+       XtSetArg(args[1], XtNbackground, timerBackgroundPixel);
+       XtSetArg(args[2], XtNforeground, foregroundOrWarningColor);
+    }
+
+    XtSetValues(w, args, 3);
+}
+
+static Pixmap *clockIcons[] = { &wIconPixmap, &bIconPixmap };
+
+void
+SetClockIcon (int color)
+{
+    Arg args[16];
+    Pixmap pm = *clockIcons[color];
+    if (iconPixmap != pm) {
+       iconPixmap = pm;
+       XtSetArg(args[0], XtNiconPixmap, iconPixmap);
+       XtSetValues(shellWidget, args, 1);
+    }
+}
+
+void
+DoInputCallback (caddr_t closure, int *source, XtInputId *xid)
+{
+    InputSource *is = (InputSource *) closure;
+    int count;
+    int error;
+    char *p, *q;
+
+    if (is->lineByLine) {
+       count = read(is->fd, is->unused,
+                    INPUT_SOURCE_BUF_SIZE - (is->unused - is->buf));
+       if (count <= 0) {
+           (is->func)(is, is->closure, is->buf, count, count ? errno : 0);
+           return;
+       }
+       is->unused += count;
+       p = is->buf;
+       while (p < is->unused) {
+           q = memchr(p, '\n', is->unused - p);
+           if (q == NULL) break;
+           q++;
+           (is->func)(is, is->closure, p, q - p, 0);
+           p = q;
+       }
+       q = is->buf;
+       while (p < is->unused) {
+           *q++ = *p++;
+       }
+       is->unused = q;
+    } else {
+       count = read(is->fd, is->buf, INPUT_SOURCE_BUF_SIZE);
+       if (count == -1)
+         error = errno;
+       else
+         error = 0;
+       (is->func)(is, is->closure, is->buf, count, error);
+    }
+}
+
+InputSourceRef
+AddInputSource (ProcRef pr, int lineByLine, InputCallback func, VOIDSTAR closure)
+{
+    InputSource *is;
+    ChildProc *cp = (ChildProc *) pr;
+
+    is = (InputSource *) calloc(1, sizeof(InputSource));
+    is->lineByLine = lineByLine;
+    is->func = func;
+    if (pr == NoProc) {
+       is->kind = CPReal;
+       is->fd = fileno(stdin);
+    } else {
+       is->kind = cp->kind;
+       is->fd = cp->fdFrom;
+    }
+    if (lineByLine) {
+       is->unused = is->buf;
+    }
+
+    is->xid = XtAppAddInput(appContext, is->fd,
+                           (XtPointer) (XtInputReadMask),
+                           (XtInputCallbackProc) DoInputCallback,
+                           (XtPointer) is);
+    is->closure = closure;
+    return (InputSourceRef) is;
+}
+
+void
+RemoveInputSource (InputSourceRef isr)
+{
+    InputSource *is = (InputSource *) isr;
+
+    if (is->xid == 0) return;
+    XtRemoveInput(is->xid);
+    is->xid = 0;
+}
+
+#ifndef HAVE_USLEEP
+
+static Boolean frameWaiting;
+
+static RETSIGTYPE
+FrameAlarm (int sig)
+{
+  frameWaiting = False;
+  /* In case System-V style signals.  Needed?? */
+  signal(SIGALRM, FrameAlarm);
+}
+
+void
+FrameDelay (int time)
+{
+  struct itimerval delay;
+
+  XSync(xDisplay, False);
+
+  if (time > 0) {
+    frameWaiting = True;
+    signal(SIGALRM, FrameAlarm);
+    delay.it_interval.tv_sec =
+      delay.it_value.tv_sec = time / 1000;
+    delay.it_interval.tv_usec =
+      delay.it_value.tv_usec = (time % 1000) * 1000;
+    setitimer(ITIMER_REAL, &delay, NULL);
+    while (frameWaiting) pause();
+    delay.it_interval.tv_sec = delay.it_value.tv_sec = 0;
+    delay.it_interval.tv_usec = delay.it_value.tv_usec = 0;
+    setitimer(ITIMER_REAL, &delay, NULL);
+  }
+}
+
+#else
+
+void
+FrameDelay (int time)
+{
+  XSync(xDisplay, False);
+  if (time > 0)
+    usleep(time * 1000);
+}
+
+#endif
+
+static void
+LoadLogo (ChessProgramState *cps, int n, Boolean ics)
+{
+    char buf[MSG_SIZ], *logoName = buf;
+    if(appData.logo[n][0]) {
+       logoName = appData.logo[n];
+    } else if(appData.autoLogo) {
+       if(ics) { // [HGM] logo: in ICS mode second can be used for ICS
+           sprintf(buf, "%s/%s.png", appData.logoDir, appData.icsHost);
+       } else if(appData.directory[n] && appData.directory[n][0]) {
+           sprintf(buf, "%s/%s.png", appData.logoDir, cps->tidy);
+       }
+    }
+    if(logoName[0])
+       { ASSIGN(cps->programLogo, logoName); }
+}
+
+void
+UpdateLogos (int displ)
+{
+    if(optList[W_WHITE-1].handle == NULL) return;
+    LoadLogo(&first, 0, 0);
+    LoadLogo(&second, 1, appData.icsActive);
+    if(displ) DisplayLogos(&optList[W_WHITE-1], &optList[W_BLACK+1]);
+    return;
+}
+
diff --git a/xboard.h b/xboard.h
new file mode 100644 (file)
index 0000000..5a6587e
--- /dev/null
+++ b/xboard.h
@@ -0,0 +1,176 @@
+/*
+ * xboard.h -- Parameter definitions for X front end
+ *
+ * Copyright 1991 by Digital Equipment Corporation, Maynard,
+ * Massachusetts.
+ *
+ * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
+ * 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * The following terms apply to Digital Equipment Corporation's copyright
+ * interest in XBoard:
+ * ------------------------------------------------------------------------
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Digital not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+ * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * ------------------------------------------------------------------------
+ *
+ * The following terms apply to the enhanced version of XBoard
+ * distributed by the Free Software Foundation:
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#include <stdio.h>
+
+#define ICS_LOGON    ".icsrc"
+#define MANPAGE      "xboard.6"
+#if ENABLE_NLS
+#define CLOCK_FONT_NAME \
+  "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*," \
+  "-misc-fixed-bold-r-normal--*-*-*-*-*-*-*-*," \
+  "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
+#define COORD_FONT_NAME \
+  "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*," \
+  "-misc-fixed-bold-r-normal--*-*-*-*-*-*-*-*," \
+  "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
+#define DEFAULT_FONT_NAME \
+  "-*-helvetica-medium-r-normal--*-*-*-*-*-*-*-*," \
+  "-misc-fixed-medium-r-normal--*-*-*-*-*-*-*-*," \
+  "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
+#else
+#define CLOCK_FONT_NAME         "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*"
+#define COORD_FONT_NAME         "-*-helvetica-bold-r-normal--*-*-*-*-*-*-*-*"
+#define DEFAULT_FONT_NAME       "-*-helvetica-medium-r-normal--*-*-*-*-*-*-*-*"
+#endif
+#define COLOR_SHOUT             "green"
+#define COLOR_SSHOUT            "green,black,1"
+#define COLOR_CHANNEL1          "cyan"
+#define COLOR_CHANNEL           "cyan,black,1"
+#define COLOR_KIBITZ            "magenta,black,1"
+#define COLOR_TELL              "yellow,black,1"
+#define COLOR_CHALLENGE         "red,black,1"
+#define COLOR_REQUEST           "red"
+#define COLOR_SEEK              "blue"
+#define COLOR_NORMAL            "default"
+#define COLOR_LOWTIMEWARNING    "red"
+
+typedef struct {
+    char *name;
+    int squareSize;
+    int lineGap;
+    int clockFontPxlSize;
+    int coordFontPxlSize;
+    int fontPxlSize;
+    int smallLayout;
+    int tinyLayout;
+    int minScreenSize;
+} SizeDefaults;
+
+#define SIZE_DEFAULTS \
+{ { "Titanic",  129, 4, 34, 14, 14, 0, 0, 1200 }, \
+  { "Colossal", 116, 4, 34, 14, 14, 0, 0, 1200 }, \
+  { "Giant",    108, 3, 34, 14, 14, 0, 0, 1024 }, \
+  { "Huge",     95, 3, 34, 14, 14, 0, 0, 1024 }, \
+  { "Big",      87, 3, 34, 14, 14, 0, 0, 864 }, \
+  { "Large",    80, 3, 34, 14, 14, 0, 0, 864 }, \
+  { "Bulky",    72, 3, 34, 12, 14, 0, 0, 864 }, \
+  { "Medium",   64, 3, 34, 12, 14, 1, 0, 768 }, \
+  { "Moderate", 58, 3, 34, 12, 14, 1, 0, 768 }, \
+  { "Average",  54, 2, 30, 11, 12, 1, 0, 600 }, \
+  { "Middling", 49, 2, 24, 10, 12, 1, 0, 600 }, \
+  { "Mediocre", 45, 2, 20, 10, 12, 1, 0, 600 }, \
+  { "Small",    40, 2, 20, 10, 12, 1, 0, 480 }, \
+  { "Slim",     37, 2, 20, 10, 12, 1, 0, 480 }, \
+  { "Petite",   33, 1, 15, 9,  11, 1, 0, 480 }, \
+  { "Dinky",    29, 1, 15, 9,  11, 1, 0, 480 }, \
+  { "Teeny",    25, 1, 12, 8,  11, 1, 1, 480 }, \
+  { "Tiny",     21, 1, 12, 8,  11, 1, 1, 0 }, \
+  {   NULL,      0, 0,  0, 0,   0, 0, 0, 0 } }
+
+#define BORDER_X_OFFSET 3
+#define BORDER_Y_OFFSET 27
+#define FIRST_CHESS_PROGRAM    "fairymax"
+#define SECOND_CHESS_PROGRAM   "fairymax"
+#define FIRST_DIRECTORY         "."
+#define SECOND_DIRECTORY        "."
+#define SOUND_BELL              ""
+#define ICS_NAMES               ""
+#define FCP_NAMES               ""
+#define SCP_NAMES               ""
+#define ICS_TEXT_MENU_DEFAULT   ""
+#define SETTINGS_FILE           SYSCONFDIR"/xboard.conf"
+#define COLOR_BKGD              "white"
+
+void NewTagsPopup P((char *text, char *msg));
+int AppendText P((Option *opt, char *s));
+void NewCommentPopup P((char *title, char *text, int index));
+void CatchDeleteWindow(Widget w, String procname);
+void GenericPopDown P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void InitDrawingSizes P((int i, int j));
+void SendToICS P((char *buf));
+void SendToProgram P((char *message, ChessProgramState *cps));
+void SetFocus(Widget w, XtPointer data, XEvent *event, Boolean *b); // from xoptions.c
+void TypeInProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+Widget CreateMenuItem P((Widget menu, char *msg, XtCallbackProc CB, int n));
+void WheelProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void TabProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void GenericMenu P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+
+// from xengineoutput.c
+void SelectPV P((Widget w, XEvent * event, String * params, Cardinal * nParams));
+void StopPV P((Widget w, XEvent * event, String * params, Cardinal * nParams));
+
+extern char memoTranslations[];
+
+
+extern Widget shells[];
+extern int dialogError;
+extern Widget formWidget, shellWidget, boardWidget, menuBarWidget;
+extern Display *xDisplay;
+extern Window xBoardWindow;
+extern int squareSize;
+extern Pixmap xMarkPixmap, wIconPixmap, bIconPixmap;
+extern char *layoutName;
+extern Pixel timerForegroundPixel, timerBackgroundPixel, dialogColor, buttonColor;
+extern int searchTime;
+extern Atom wm_delete_window;
+extern int squareSize, lineGap, defaultLineGap, useImages, useImageSqs;
+extern int startedFromPositionFile;
+extern char *icsTextMenuString;
+extern char ICSInputTranslations[];
+extern char *selected_fen_position;
+extern GC coordGC;
+extern Dimension textHeight; // of message widget in board window
+
+
+#define TOPLEVEL 1 /* preference item; 1 = make popup windows toplevel */
+
diff --git a/xedittags.c b/xedittags.c
new file mode 100644 (file)
index 0000000..d27aca3
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * xedittags.c -- Tags edit window, part of X front end for XBoard
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+#  include <string.h>
+# else /* not HAVE_STRING_H */
+#  include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/cursorfont.h>
+#if USE_XAW3D
+#include <X11/Xaw3d/Dialog.h>
+#include <X11/Xaw3d/Form.h>
+#include <X11/Xaw3d/List.h>
+#include <X11/Xaw3d/Label.h>
+#include <X11/Xaw3d/SimpleMenu.h>
+#include <X11/Xaw3d/SmeBSB.h>
+#include <X11/Xaw3d/SmeLine.h>
+#include <X11/Xaw3d/Box.h>
+#include <X11/Xaw3d/MenuButton.h>
+#include <X11/Xaw3d/Text.h>
+#include <X11/Xaw3d/AsciiText.h>
+#include <X11/Xaw3d/Viewport.h>
+#else
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#endif
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "xboard.h"
+#include "xedittags.h"
+#include "dialogs.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define  _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define  _(s) (s)
+# define N_(s)  s
+#endif
+
+Position tagsX = -1, tagsY = -1;
+
+void
+TagsPopUp (char *tags, char *msg)
+{
+    NewTagsPopup(tags, cmailMsgLoaded ? msg : NULL);
+}
+
+
+void
+EditTagsPopUp (char *tags, char **dest)
+{
+    NewTagsPopup(tags, NULL);
+}
+
+void
+TagsPopDown()
+{
+    PopDown(TagsDlg);
+    bookUp = False;
+}
+
+void
+EditTagsProc ()
+{
+  if (bookUp || !PopDown(TagsDlg)) EditTagsEvent();
+}
diff --git a/xedittags.h b/xedittags.h
new file mode 100644 (file)
index 0000000..6245bfd
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * xedittags.h
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#ifndef XB_XEDITTAGS
+#define XB_XEDITTAGS
+
+void EditTagsProc P((void));
+
+extern Widget editTagsShell, tagsShell;
+#endif
diff --git a/xengineoutput.c b/xengineoutput.c
new file mode 100644 (file)
index 0000000..78269b8
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Engine output (PV)
+ *
+ * Author: Alessandro Scotti (Dec 2005)
+ *
+ * Copyright 2005 Alessandro Scotti
+ *
+ * Enhancements Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ *
+ * ------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+#  include <string.h>
+# else /* not HAVE_STRING_H */
+#  include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/Atoms.h>
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "dialogs.h"
+#include "xboard.h"
+#include "engineoutput.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define  _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define  _(s) (s)
+# define N_(s)  s
+#endif
+
+#include <X11/xpm.h>
+
+// [HGM] pixmaps of some ICONS used in the engine-outut window
+#include "pixmaps/WHITE_14.xpm"
+#include "pixmaps/BLACK_14.xpm"
+#include "pixmaps/CLEAR_14.xpm"
+#include "pixmaps/UNKNOWN_14.xpm"
+#include "pixmaps/THINKING_14.xpm"
+#include "pixmaps/PONDER_14.xpm"
+#include "pixmaps/ANALYZING_14.xpm"
+
+
+/* Module variables */
+static int currentPV, highTextStart[2], highTextEnd[2];
+static Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
+static Widget memoWidget;
+
+
+static void
+ReadIcon (char *pixData[], int iconNr, Widget w)
+{
+    int r;
+
+       if ((r=XpmCreatePixmapFromData(xDisplay, XtWindow(w),
+                                      pixData,
+                                      &(icons[iconNr]),
+                                      NULL, NULL /*&attr*/)) != 0) {
+         fprintf(stderr, _("Error %d loading icon image\n"), r);
+         exit(1);
+       }
+}
+
+void
+InitEngineOutput (Option *opt, Option *memo2)
+{      // front-end, because it must have access to the pixmaps
+       Widget w = opt->handle;
+       memoWidget = memo2->handle;
+
+        ReadIcon(WHITE_14,   nColorWhite, w);
+        ReadIcon(BLACK_14,   nColorBlack, w);
+        ReadIcon(UNKNOWN_14, nColorUnknown, w);
+
+        ReadIcon(CLEAR_14,   nClear, w);
+        ReadIcon(PONDER_14,  nPondering, w);
+        ReadIcon(THINK_14,   nThinking, w);
+        ReadIcon(ANALYZE_14, nAnalyzing, w);
+}
+
+void
+DrawWidgetIcon (Option *opt, int nIcon)
+{   // as we are already in X front-end, so do X-stuff here
+    Arg arg;
+    XtSetArg(arg, XtNleftBitmap, (XtArgVal) icons[nIcon]);
+    XtSetValues(opt->handle, &arg, 1);
+}
+
+void
+InsertIntoMemo (int which, char * text, int where)
+{
+       XawTextBlock t;
+       Widget edit;
+
+       /* the backend adds \r\n, which is needed for winboard,
+        * for xboard we delete them again over here */
+       if(t.ptr = strchr(text, '\r')) *t.ptr = ' ';
+
+       t.ptr = text; t.firstPos = 0; t.length = strlen(text); t.format = XawFmt8Bit;
+       edit = XtNameToWidget(shells[EngOutDlg], which ? "*paneB.text" : "*paneA.text");
+       XawTextReplace(edit, where, where, &t);
+       if(where < highTextStart[which]) { // [HGM] multiPVdisplay: move highlighting
+           int len = strlen(text);
+           highTextStart[which] += len; highTextEnd[which] += len;
+           XawTextSetSelection( edit, highTextStart[which], highTextEnd[which] );
+       }
+}
+
+//--------------------------------- PV walking ---------------------------------------
+
+char memoTranslations[] =
+":Ctrl<Key>c: CopyMemoProc() \n \
+<Btn3Motion>: HandlePV() \n \
+Shift<Btn3Down>: select-start() extend-end() SelectPV(1) \n \
+Any<Btn3Down>: select-start() extend-end() SelectPV(0) \n \
+<Btn3Up>: StopPV() \n";
+
+void
+SelectPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{      // [HGM] pv: translate click to PV line, and load it for display
+       String val;
+       int start, end;
+       XawTextPosition index, dummy;
+       int x, y;
+       Arg arg;
+
+       x = event->xmotion.x; y = event->xmotion.y;
+       currentPV = (w != memoWidget);
+       XawTextGetSelectionPos(w, &index, &dummy);
+       XtSetArg(arg, XtNstring, &val);
+       XtGetValues(w, &arg, 1);
+       shiftKey = strcmp(params[0], "0");
+       if(LoadMultiPV(x, y, val, index, &start, &end, currentPV)) {
+           XawTextSetSelection( w, start, end );
+           highTextStart[currentPV] = start; highTextEnd[currentPV] = end;
+       }
+}
+
+void
+StopPV (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{      // [HGM] pv: on right-button release, stop displaying PV
+        XawTextUnsetSelection( w );
+        highTextStart[currentPV] = highTextEnd[currentPV] = 0;
+        UnLoadPV();
+        XtCallActionProc(w, "beginning-of-file", event, NULL, 0);
+}
+
+//------------------------- Ctrl-C copying of memo texts ---------------------------
+
+// Awfull code: first read our own primary selection into selected_fen_position,
+//              and then transfer ownership of this to the clipboard, so that the
+//              copy-position callback can fetch it there when somebody pastes it
+// Worst of all is that I only added it because I did not know how to copy primary:
+// my laptop has no middle button. Ctrl-C might not be needed at all... [HGM]
+
+// cloned from CopyPositionProc. Abuse selected_fen_position to hold selection
+
+Boolean SendPositionSelection(Widget w, Atom *selection, Atom *target,
+                Atom *type_return, XtPointer *value_return,
+                unsigned long *length_return, int *format_return); // from xboard.c
+
+static void
+MemoCB (Widget w, XtPointer client_data, Atom *selection,
+       Atom *type, XtPointer value, unsigned long *len, int *format)
+{
+  if (value==NULL || *len==0) return; /* nothing had been selected to copy */
+  selected_fen_position = value;
+  selected_fen_position[*len]='\0'; /* normally this string is terminated, but be safe */
+    XtOwnSelection(menuBarWidget, XA_CLIPBOARD(xDisplay),
+                  CurrentTime,
+                  SendPositionSelection,
+                  NULL/* lose_ownership_proc */ ,
+                  NULL/* transfer_done_proc */);
+}
+
+void
+CopyMemoProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+    if(appData.pasteSelection) return;
+    if (selected_fen_position) free(selected_fen_position);
+    XtGetSelectionValue(menuBarWidget,
+      XA_PRIMARY, XA_STRING,
+      /* (XtSelectionCallbackProc) */ MemoCB,
+      NULL, /* client_data passed to PastePositionCB */
+
+      /* better to use the time field from the event that triggered the
+       * call to this function, but that isn't trivial to get
+       */
+      CurrentTime
+    );
+}
+
+//------------------------------- pane switching -----------------------------------
+
+void
+ResizeWindowControls (int mode)
+{   // another hideous kludge: to have only a single pane, we resize the
+    // second to 5 pixels (which makes it too small to display anything)
+    Widget form1, form2;
+    Arg args[16];
+    int j;
+    Dimension ew_height, tmp;
+    Widget shell = shells[EngOutDlg];
+
+    form1 = XtNameToWidget(shell, "*paneA");
+    form2 = XtNameToWidget(shell, "*paneB");
+
+    j = 0;
+    XtSetArg(args[j], XtNheight, (XtArgVal) &ew_height); j++;
+    XtGetValues(form1, args, j);
+    j = 0;
+    XtSetArg(args[j], XtNheight, (XtArgVal) &tmp); j++;
+    XtGetValues(form2, args, j);
+    ew_height += tmp; // total height
+
+    if(mode==0) {
+       j = 0;
+       XtSetArg(args[j], XtNheight, (XtArgVal) 5); j++;
+       XtSetValues(form2, args, j);
+       j = 0;
+       XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height-5)); j++;
+       XtSetValues(form1, args, j);
+    } else {
+       j = 0;
+       XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
+       XtSetValues(form1, args, j);
+       j = 0;
+       XtSetArg(args[j], XtNheight, (XtArgVal) (ew_height/2)); j++;
+       XtSetValues(form2, args, j);
+    }
+}
+
diff --git a/xevalgraph.c b/xevalgraph.c
new file mode 100644 (file)
index 0000000..f3cac1f
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Evaluation graph
+ *
+ * Author: Alessandro Scotti (Dec 2005)
+ * Translated to X by H.G.Muller (Nov 2009)
+ *
+ * Copyright 2005 Alessandro Scotti
+ *
+ * Enhancements Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ *
+ * ------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+#  include <string.h>
+# else /* not HAVE_STRING_H */
+#  include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "dialogs.h"
+#include "menus.h"
+#include "xboard.h"
+#include "evalgraph.h"
+#include "xevalgraph.h"
+#include "draw.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define  _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define  _(s) (s)
+# define N_(s)  s
+#endif
+
+#include <X11/xpm.h>
+
+#ifdef SNAP
+#include "wsnap.h"
+#endif
+
+#define _LL_ 100
+
+Pixmap icons[8]; // [HGM] this front-end array translates back-end icon indicator to handle
+Widget outputField[2][7]; // [HGM] front-end array to translate output field to window handle
+static char *title = N_("Evaluation graph");
+
+//extern WindowPlacement wpEvalGraph;
+
+Position evalGraphX = -1, evalGraphY = -1;
+Dimension evalGraphW, evalGraphH;
+
+/* Module variables */
+
+char *crWhite = "#FFFFB0";
+char *crBlack = "#AD5D3D";
+Option *disp;
+
+static Option *EvalCallback P((int button, int x, int y));
+
+static void
+ChoosePen(cairo_t *cr, int i)
+{
+  switch(i) {
+    case PEN_BLACK:
+      SetPen(cr, 1.0, "#000000", 0);
+      break;
+    case PEN_DOTTED:
+      SetPen(cr, 1.0, "#A0A0A0", 1);
+      break;
+    case PEN_BLUEDOTTED:
+      SetPen(cr, 1.0, "#0000FF", 1);
+      break;
+    case PEN_BOLDWHITE:
+      SetPen(cr, 3.0, crWhite, 0);
+      break;
+    case PEN_BOLDBLACK:
+      SetPen(cr, 3.0, crBlack, 0);
+      break;
+    case PEN_BACKGD:
+      SetPen(cr, 3.0, "#E0E0F0", 0);
+      break;
+  }
+}
+
+// [HGM] front-end, added as wrapper to avoid use of LineTo and MoveToEx in other routines (so they can be back-end)
+void
+DrawSegment (int x, int y, int *lastX, int *lastY, enum PEN penType)
+{
+  static int curX, curY;
+
+  if(penType != PEN_NONE) {
+    cairo_t *cr = cairo_create(DRAWABLE(disp));
+    cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+    cairo_move_to (cr, curX, curY);
+    cairo_line_to (cr, x,y);
+    ChoosePen(cr, penType);
+    cairo_stroke (cr);
+    cairo_destroy (cr);
+  }
+
+  if(lastX != NULL) { *lastX = curX; *lastY = curY; }
+  curX = x; curY = y;
+}
+
+// front-end wrapper for drawing functions to do rectangles
+void
+DrawRectangle (int left, int top, int right, int bottom, int side, int style)
+{
+  cairo_t *cr;
+
+  cr = cairo_create (DRAWABLE(disp));
+  cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+  cairo_rectangle (cr, left, top, right-left, bottom-top);
+  switch(side)
+    {
+    case 0: ChoosePen(cr, PEN_BOLDWHITE); break;
+    case 1: ChoosePen(cr, PEN_BOLDBLACK); break;
+    case 2: ChoosePen(cr, PEN_BACKGD); break;
+    }
+  cairo_fill (cr);
+
+  if(style != FILLED)
+    {
+      cairo_rectangle (cr, left, top, right-left-1, bottom-top-1);
+      ChoosePen(cr, PEN_BLACK);
+      cairo_stroke (cr);
+    }
+
+  cairo_destroy(cr);
+}
+
+// front-end wrapper for putting text in graph
+void
+DrawEvalText (char *buf, int cbBuf, int y)
+{
+    // the magic constants 8 and 5 should really be derived from the font size somehow
+  cairo_text_extents_t extents;
+  cairo_t *cr = cairo_create(DRAWABLE(disp));
+
+  /* GTK-TODO this has to go into the font-selection */
+  cairo_select_font_face (cr, "Sans",
+                         CAIRO_FONT_SLANT_NORMAL,
+                         CAIRO_FONT_WEIGHT_NORMAL);
+  cairo_set_font_size (cr, 12.0);
+
+
+  cairo_text_extents (cr, buf, &extents);
+
+  cairo_move_to (cr, MarginX - 2 - 8*cbBuf, y+5);
+  cairo_text_path (cr, buf);
+  cairo_set_source_rgb (cr, 0.0, 0.0, 0);
+  cairo_fill_preserve (cr);
+  cairo_set_source_rgb (cr, 0, 1.0, 0);
+  cairo_set_line_width (cr, 0.1);
+  cairo_stroke (cr);
+
+  /* free memory */
+  cairo_destroy (cr);
+}
+
+static int initDone = FALSE;
+
+static void
+InitializeEvalGraph (Option *opt, int w, int h)
+{
+  if(w == 0) {
+    Arg args[10];
+    XtSetArg(args[0], XtNwidth, &evalGraphW);
+    XtSetArg(args[1], XtNheight, &evalGraphH);
+    XtGetValues(opt->handle, args, 2);
+    nWidthPB = evalGraphW; nHeightPB = evalGraphH;
+  } else nWidthPB = w, nHeightPB = h;
+
+  initDone = TRUE;
+}
+
+// The following stuff is really back-end (but too little to bother with a separate file)
+
+static void
+EvalClick (int x, int y)
+{
+    int index = GetMoveIndexFromPoint( x, y );
+
+    if( index >= 0 && index < currLast ) ToNrEvent( index + 1 );
+}
+
+static Option graphOptions[] = {
+{ 150, 0x9C, 300, NULL, (void*) &EvalCallback, NULL, NULL, Graph , "" },
+{ 0, 2, 0, NULL, NULL, "", NULL, EndMark , "" }
+};
+
+static void
+DisplayEvalGraph ()
+{   // back-end painting; calls back front-end primitives for lines, rectangles and text
+    char *t = MakeEvalTitle(_(title));
+    nWidthPB = disp->max; nHeightPB = disp->value;
+    if(t != title && nWidthPB < 340) t = MakeEvalTitle(nWidthPB < 240 ? "" : _("Eval"));
+    PaintEvalGraph();
+    GraphExpose(graphOptions, 0, 0, nWidthPB, nHeightPB);
+    SetDialogTitle(EvalGraphDlg, t);
+}
+
+static Option *
+EvalCallback (int button, int x, int y)
+{
+    if(!initDone) return NULL;
+
+    switch(button) {
+       case 10: // expose event
+           /* Create or recreate paint box if needed */
+           if(x != nWidthPB || y != nHeightPB) {
+               InitializeEvalGraph(&graphOptions[0], x, y);
+           }
+           nWidthPB = x;
+           nHeightPB = y;
+           DisplayEvalGraph();
+           break;
+       case 1: EvalClick(x, y); // left button
+       default: break; // other buttons ignored
+    }
+    return NULL; // no context menu!
+}
+
+void
+EvalGraphPopUp ()
+{
+    if (GenericPopUp(graphOptions, _(title), EvalGraphDlg, BoardWindow, NONMODAL, 1)) {
+       InitializeEvalGraph(&graphOptions[0], 0, 0); // first time: add callbacks and initialize pens
+       disp = graphOptions;
+    } else {
+       SetDialogTitle(EvalGraphDlg, _(title));
+       SetIconName(EvalGraphDlg, _(title));
+    }
+
+    MarkMenu("View.EvaluationGraph", EvalGraphDlg);
+
+//    ShowThinkingEvent(); // [HGM] thinking: might need to prompt engine for thinking output
+}
+
+void
+EvalGraphPopDown ()
+{
+    PopDown(EvalGraphDlg);
+
+//    ShowThinkingEvent(); // [HGM] thinking: might need to shut off thinking output
+}
+
+Boolean
+EvalGraphIsUp ()
+{
+    return shellUp[EvalGraphDlg];
+}
+
+int
+EvalGraphDialogExists ()
+{
+    return DialogExists(EvalGraphDlg);
+}
+
+void
+EvalGraphProc ()
+{
+  if (!PopDown(EvalGraphDlg)) EvalGraphPopUp();
+}
+
+// This function is the interface to the back-end.
+
+void
+EvalGraphSet (int first, int last, int current, ChessProgramStats_Move * pvInfo)
+{
+    /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
+
+    currFirst = first;
+    currLast = last;
+    currCurrent = current;
+    currPvInfo = pvInfo;
+
+    if( DialogExists(EvalGraphDlg) ) {
+        DisplayEvalGraph();
+    }
+}
+
diff --git a/xevalgraph.h b/xevalgraph.h
new file mode 100644 (file)
index 0000000..e1227c6
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * xevalgraph.h
+ *
+ * Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#ifndef XB_XEVALGRAPH
+#define XB_XEVALGRAPH
+
+void EvalGraphSet P(( int first, int last, int current, ChessProgramStats_Move * pvInfo ));
+float Color P((char *col, int n));
+void SetPen P((cairo_t *cr, float w, char *col, int dash));
+
+#endif
diff --git a/xgamelist.c b/xgamelist.c
new file mode 100644 (file)
index 0000000..50367b1
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * xgamelist.c -- Game list window, part of X front end for XBoard
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+#  include <string.h>
+# else /* not HAVE_STRING_H */
+#  include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/cursorfont.h>
+#if USE_XAW3D
+#include <X11/Xaw3d/Dialog.h>
+#include <X11/Xaw3d/Form.h>
+#include <X11/Xaw3d/List.h>
+#include <X11/Xaw3d/Label.h>
+#include <X11/Xaw3d/SimpleMenu.h>
+#include <X11/Xaw3d/SmeBSB.h>
+#include <X11/Xaw3d/SmeLine.h>
+#include <X11/Xaw3d/Box.h>
+#include <X11/Xaw3d/MenuButton.h>
+#include <X11/Xaw3d/Text.h>
+#include <X11/Xaw3d/AsciiText.h>
+#include <X11/Xaw3d/Viewport.h>
+#else
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#endif
+
+#include "common.h"
+#include "backend.h"
+#include "xboard.h"
+#include "xgamelist.h"
+#include "dialogs.h"
+
+
+char gameListTranslations[] =
+  "<Btn4Down>: WheelProc(-3) \n \
+   <Btn5Down>: WheelProc(3) \n \
+   <Btn1Down>: LoadSelectedProc(100) Set() \n \
+   <Btn1Up>(2): LoadSelectedProc(0) \n \
+   <Key>Home: LoadSelectedProc(-2) \n \
+   <Key>End: LoadSelectedProc(2) \n \
+   Ctrl<Key>Up: LoadSelectedProc(-3) \n \
+   Ctrl<Key>Down: LoadSelectedProc(3) \n \
+   <Key>Up: LoadSelectedProc(-1) \n \
+   <Key>Down: LoadSelectedProc(1) \n \
+   <Key>Left: LoadSelectedProc(-1) \n \
+   <Key>Right: LoadSelectedProc(1) \n \
+   <Key>Prior: LoadSelectedProc(-4) \n \
+   <Key>Next: LoadSelectedProc(4) \n \
+   <Key>Return: LoadSelectedProc(0) \n";
+char filterTranslations[] =
+  "<Key>Return: SetFilterProc() \n";
+
+
+void
+LoadSelectedProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+    if(GameListClicks(atoi(prms[0]))) return; // if no game loaded, no focus transfer
+    XSync(xDisplay, False);
+    XSetInputFocus(xDisplay, XtWindow(boardWidget), RevertToPointerRoot, CurrentTime);
+}
+
+void
+SetFilterProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{
+    SetFilter();
+}
+
diff --git a/xgamelist.h b/xgamelist.h
new file mode 100644 (file)
index 0000000..fa7b41a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * xgamelist.h -- Game list window, part of X front end for XBoard
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#ifndef XB_XGAMELIST
+#define XB_XGAMELIST
+
+void LoadSelectedProc P((Widget w, XEvent *event,
+                        String *prms, Cardinal *nprms));
+void SetFilterProc P((Widget w, XEvent *event,
+                        String *prms, Cardinal *nprms));
+
+#endif /* XB_XGAMELIST */
diff --git a/xhistory.c b/xhistory.c
new file mode 100644 (file)
index 0000000..4277f82
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * New (WinBoard-style) Move history for XBoard
+ *
+ * Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/. 
+ *
+ * ------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/Atoms.h>
+
+#include "common.h"
+#include "frontend.h"
+#include "backend.h"
+#include "xhistory.h"
+#include "xboard.h"
+#include "dialogs.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define  _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define  _(s) (s)
+# define N_(s)  s
+#endif
+
+// templates for calls into back-end (= history.c; should be moved to history.h header shared with it!)
+void RefreshMemoContent P((void));
+void MemoContentUpdated P((void));
+void FindMoveByCharIndex P(( int char_index ));
+
+// variables in xoptions.c
+extern Option historyOptions[];
+
+// ------------- low-level front-end actions called by MoveHistory back-end -----------------
+
+void
+HighlightMove (int from, int to, Boolean highlight)
+{
+    if(highlight)
+       XawTextSetSelection( historyOptions[0].handle, from, to ); // for lack of a better method, use selection for highighting
+}
+
+void
+ClearHistoryMemo ()
+{
+    SetWidgetText(&historyOptions[0], "", HistoryDlg);
+}
+
+// the bold argument says 0 = normal, 1 = bold typeface
+// the colorNr argument says 0 = font-default, 1 = gray
+int
+AppendToHistoryMemo (char * text, int bold, int colorNr)
+{
+    return AppendText(&historyOptions[0], text); // for now ignore bold & color stuff, as Xaw cannot handle that
+}
+
+void
+ScrollToCurrent (int caretPos)
+{
+    Arg args[10];
+    char *s;
+    int len;
+    GetWidgetText(&historyOptions[0], &s);
+    len = strlen(s);
+    if(caretPos < 0 || caretPos > len) caretPos = len;
+    if(caretPos > len-30) { // scroll to end, which causes no flicker
+      static XEvent event;
+      XtCallActionProc(historyOptions[0].handle, "end-of-file", &event, NULL, 0);
+      return;
+    }
+    // the following leads to a very annoying flicker, even when no scrolling is done at all.
+    XtSetArg(args[0], XtNinsertPosition, caretPos); // this triggers scrolling in Xaw
+    XtSetArg(args[1], XtNdisplayCaret, False);
+    XtSetValues(historyOptions[0].handle, args, 2);
+}
+
+
+// ------------------------------ callbacks --------------------------
+
+char *historyText;
+char historyTranslations[] =
+"<Btn3Down>: select-start() \n \
+<Btn3Up>: extend-end() SelectMove() \n";
+
+void
+SelectMove (Widget w, XEvent * event, String * params, Cardinal * nParams)
+{
+       XawTextPosition index, dummy;
+
+       XawTextGetSelectionPos(w, &index, &dummy);
+       FindMoveByCharIndex( index ); // [HGM] also does the actual moving to it, now
+}
+
+Option historyOptions[] = {
+{ 200, T_VSCRL | T_FILL | T_WRAP | T_TOP, 400, NULL, (void*) &historyText, "", NULL, TextBox, "" },
+{   0,           NO_OK,             0, NULL, (void*) NULL, "", NULL, EndMark , "" }
+};
+
+// ------------ standard entry points into MoveHistory code -----------
+
+Boolean
+MoveHistoryIsUp ()
+{
+    return shellUp[HistoryDlg];
+}
+
+Boolean
+MoveHistoryDialogExists ()
+{
+    return DialogExists(HistoryDlg);
+}
+
+void
+HistoryPopUp ()
+{
+    if(GenericPopUp(historyOptions, _("Move list"), HistoryDlg, BoardWindow, NONMODAL, 1))
+       AddHandler(&historyOptions[0], 0);
+    MarkMenu("View.MoveHistory", HistoryDlg);
+}
+
+void
+HistoryShowProc ()
+{
+  if (!shellUp[HistoryDlg]) {
+    ASSIGN(historyText, "");
+    HistoryPopUp();
+    RefreshMemoContent();
+    MemoContentUpdated();
+  } else PopDown(HistoryDlg);
+  ToNrEvent(currentMove);
+}
diff --git a/xhistory.h b/xhistory.h
new file mode 100644 (file)
index 0000000..90f0c9b
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * xhistory.h -- Eval graph window, part of X front end for XBoard
+ *
+ * Copyright 1995, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+#ifndef XB_XHISTLIST
+#define XB_XHISTLIST
+
+void HistoryShowProc P((void));
+Boolean MoveHistoryIsUp P((void));
+
+#endif /* XB_XHISTLIST */
diff --git a/xoptions.c b/xoptions.c
new file mode 100644 (file)
index 0000000..a475c58
--- /dev/null
@@ -0,0 +1,1216 @@
+/*
+ * xoptions.c -- Move list window, part of X front end for XBoard
+ *
+ * Copyright 2000, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
+ * ------------------------------------------------------------------------
+ *
+ * GNU XBoard is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * GNU XBoard is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.  *
+ *
+ *------------------------------------------------------------------------
+ ** See the file ChangeLog for a revision history.  */
+
+// [HGM] this file is the counterpart of woptions.c, containing xboard popup menus
+// similar to those of WinBoard, to set the most common options interactively.
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#else /* not STDC_HEADERS */
+extern char *getenv();
+# if HAVE_STRING_H
+#  include <string.h>
+# else /* not HAVE_STRING_H */
+#  include <strings.h>
+# endif /* not HAVE_STRING_H */
+#endif /* not STDC_HEADERS */
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <stdint.h>
+
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xatom.h>
+#include <X11/Xaw/Dialog.h>
+#include <X11/Xaw/Form.h>
+#include <X11/Xaw/List.h>
+#include <X11/Xaw/Label.h>
+#include <X11/Xaw/SimpleMenu.h>
+#include <X11/Xaw/SmeBSB.h>
+#include <X11/Xaw/SmeLine.h>
+#include <X11/Xaw/Box.h>
+#include <X11/Xaw/Paned.h>
+#include <X11/Xaw/MenuButton.h>
+#include <X11/cursorfont.h>
+#include <X11/Xaw/Text.h>
+#include <X11/Xaw/AsciiText.h>
+#include <X11/Xaw/Viewport.h>
+#include <X11/Xaw/Toggle.h>
+#include <X11/Xaw/Scrollbar.h>
+
+#include <cairo/cairo.h>
+#include <cairo/cairo-xlib.h>
+
+#include "common.h"
+#include "backend.h"
+#include "xboard.h"
+#include "dialogs.h"
+#include "menus.h"
+#include "gettext.h"
+
+#ifdef ENABLE_NLS
+# define  _(s) gettext (s)
+# define N_(s) gettext_noop (s)
+#else
+# define  _(s) (s)
+# define N_(s)  s
+#endif
+
+// [HGM] the following code for makng menu popups was cloned from the FileNamePopUp routines
+
+static Widget previous = NULL;
+static Option *currentOption;
+
+void
+UnCaret ()
+{
+    Arg args[2];
+
+    if(previous) {
+       XtSetArg(args[0], XtNdisplayCaret, False);
+       XtSetValues(previous, args, 1);
+    }
+    previous = NULL;
+}
+
+void
+SetFocus (Widget w, XtPointer data, XEvent *event, Boolean *b)
+{
+    Arg args[2];
+    char *s;
+    int j;
+
+    UnCaret();
+    XtSetArg(args[0], XtNstring, &s);
+    XtGetValues(w, args, 1);
+    j = 1;
+    XtSetArg(args[0], XtNdisplayCaret, True);
+    if(!strchr(s, '\n') && strlen(s) < 80) XtSetArg(args[1], XtNinsertPosition, strlen(s)), j++;
+    XtSetValues(w, args, j);
+    XtSetKeyboardFocus((Widget) data, w);
+    previous = w;
+}
+
+void
+BoardFocus ()
+{
+    XtSetKeyboardFocus(shellWidget, formWidget);
+}
+
+//--------------------------- Engine-specific options menu ----------------------------------
+
+int dialogError;
+Option *dialogOptions[NrOfDialogs];
+
+static Arg layoutArgs[] = {
+    { XtNborderWidth, 0 },
+    { XtNdefaultDistance, 0 },
+};
+
+static Arg formArgs[] = {
+    { XtNborderWidth, 0 },
+    { XtNresizable, (XtArgVal) True },
+};
+
+void
+GetWidgetText (Option *opt, char **buf)
+{
+    Arg arg;
+    XtSetArg(arg, XtNstring, buf);
+    XtGetValues(opt->handle, &arg, 1);
+}
+
+void
+SetWidgetText (Option *opt, char *buf, int n)
+{
+    Arg arg;
+    XtSetArg(arg, XtNstring, buf);
+    XtSetValues(opt->handle, &arg, 1);
+    if(n >= 0) SetFocus(opt->handle, shells[n], NULL, False);
+}
+
+void
+GetWidgetState (Option *opt, int *state)
+{
+    Arg arg;
+    XtSetArg(arg, XtNstate, state);
+    XtGetValues(opt->handle, &arg, 1);
+}
+
+void
+SetWidgetState (Option *opt, int state)
+{
+    Arg arg;
+    XtSetArg(arg, XtNstate, state);
+    XtSetValues(opt->handle, &arg, 1);
+}
+
+void
+SetWidgetLabel (Option *opt, char *buf)
+{
+    Arg arg;
+    XtSetArg(arg, XtNlabel, (XtArgVal) buf);
+    XtSetValues(opt->handle, &arg, 1);
+}
+
+void
+SetDialogTitle (DialogClass dlg, char *title)
+{
+    Arg args[16];
+    XtSetArg(args[0], XtNtitle, title);
+    XtSetValues(shells[dlg], args, 1);
+}
+
+void
+LoadListBox (Option *opt, char *emptyText)
+{
+    static char *dummyList[2];
+    dummyList[0] = emptyText; // empty listboxes tend to crash X, so display user-supplied warning string instead
+    XawListChange(opt->handle, *(char*)opt->target ? opt->target : dummyList, 0, 0, True);
+}
+
+int
+ReadScroll (Option *opt, float *top, float *bottom)
+{   // retreives fractions of top and bottom of thumb
+    Arg args[16];
+    Widget w = XtParent(opt->handle); // viewport
+    Widget v = XtNameToWidget(w, "vertical");
+    int j=0;
+    float h;
+    if(!v) return FALSE; // no scroll bar
+    XtSetArg(args[j], XtNshown, &h); j++;
+    XtSetArg(args[j], XtNtopOfThumb, top); j++;
+    XtGetValues(v, args, j);
+    *bottom = *top + h;
+    return TRUE;
+}
+
+void
+SetScroll (Option *opt, float f)
+{   // sets top of thumb to given fraction
+    static char *params[3] = { "", "Continuous", "Proportional" };
+    static XEvent event;
+    Widget w = XtParent(opt->handle); // viewport
+    Widget v = XtNameToWidget(w, "vertical");
+    if(!v) return; // no scroll bar
+    XtCallActionProc(v, "StartScroll", &event, params+1, 1);
+    XawScrollbarSetThumb(v, f, -1.0);
+    XtCallActionProc(v, "NotifyThumb", &event, params, 0);
+//    XtCallActionProc(v, "NotifyScroll", &event, params+2, 1);
+    XtCallActionProc(v, "EndScroll", &event, params, 0);
+}
+
+void
+HighlightListBoxItem (Option *opt, int nr)
+{
+    XawListHighlight(opt->handle, nr);
+}
+
+void
+HighlightWithScroll (Option *opt, int sel, int max)
+{
+    float top, bottom, f, g;
+    HighlightListBoxItem(opt, sel);
+    if(!ReadScroll(opt, &top, &bottom)) return; // no scroll bar
+    bottom = bottom*max - 1.f;
+    f = g = top;
+    top *= max;
+    if(sel > (top + 3*bottom)/4) f = (sel - 0.75f*(bottom-top))/max; else
+    if(sel < (3*top + bottom)/4) f = (sel - 0.25f*(bottom-top))/max;
+    if(f < 0.f) f = 0.; if(f + 1.f/max > 1.f) f = 1. - 1./max;
+    if(f != g) SetScroll(opt, f);
+}
+
+int
+SelectedListBoxItem (Option *opt)
+{
+    XawListReturnStruct *rs;
+    rs = XawListShowCurrent(opt->handle);
+    return rs->list_index;
+}
+
+void
+FocusOnWidget (Option *opt, DialogClass dlg)
+{
+    UnCaret();
+    XtSetKeyboardFocus(shells[dlg], opt->handle);
+}
+
+void
+SetIconName (DialogClass dlg, char *name)
+{
+       Arg args[16];
+       int j = 0;
+       XtSetArg(args[j], XtNiconName, (XtArgVal) name);  j++;
+//     XtSetArg(args[j], XtNtitle, (XtArgVal) name);  j++;
+       XtSetValues(shells[dlg], args, j);
+}
+
+static void
+CheckCallback (Widget ww, XtPointer client_data, XEvent *event, Boolean *b)
+{
+    int s, data = (intptr_t) client_data;
+    Option *opt = dialogOptions[data >> 8] + (data & 255);
+
+    if(opt->type == Label) { ((ButtonCallback*) opt->target)(data&255); return; }
+
+    GetWidgetState(opt, &s);
+    SetWidgetState(opt, !s);
+}
+
+static void
+SpinCallback (Widget w, XtPointer client_data, XtPointer call_data)
+{
+    String name, val;
+    Arg args[16];
+    char buf[MSG_SIZ], *p;
+    int j = 0; // Initialisation is necessary because the text value may be non-numeric causing the scanf conversion to fail
+    int data = (intptr_t) client_data;
+    Option *opt = dialogOptions[data >> 8] + (data & 255);
+
+    XtSetArg(args[0], XtNlabel, &name);
+    XtGetValues(w, args, 1);
+
+    GetWidgetText(opt, &val);
+    sscanf(val, "%d", &j);
+    if (strcmp(name, _("browse")) == 0) {
+       char *q=val, *r;
+       for(r = ""; *q; q++) if(*q == '.') r = q; else if(*q == '/') r = ""; // last dot after last slash
+       if(!strcmp(r, "") && !currentCps && opt->type == FileName && opt->textValue)
+               r = opt->textValue;
+       Browse(data>>8, opt->name, NULL, r, opt->type == PathName, "", &p, (FILE**) opt);
+       return;
+    } else
+    if (strcmp(name, "+") == 0) {
+       if(++j > opt->max) return;
+    } else
+    if (strcmp(name, "-") == 0) {
+       if(--j < opt->min) return;
+    } else return;
+    snprintf(buf, MSG_SIZ,  "%d", j);
+    SetWidgetText(opt, buf, TransientDlg);
+}
+
+static void
+ComboSelect (Widget w, caddr_t addr, caddr_t index) // callback for all combo items
+{
+    Arg args[16];
+    Option *opt = dialogOptions[((intptr_t)addr)>>24]; // applicable option list
+    int i = ((intptr_t)addr)>>16 & 255; // option number
+    int j = 0xFFFF & (intptr_t) addr;
+
+    values[i] = j; // store selected value in Option struct, for retrieval at OK
+
+    if(opt[i].type == Graph || opt[i].min & COMBO_CALLBACK && (!currentCps || shellUp[BrowserDlg])) {
+       ((ButtonCallback*) opt[i].target)(i);
+       return;
+    }
+
+    if(opt[i].min & NO_GETTEXT)
+      XtSetArg(args[0], XtNlabel, ((char**)opt[i].choice)[j]);
+    else
+      XtSetArg(args[0], XtNlabel, _(((char**)opt[i].choice)[j]));
+
+    XtSetValues(opt[i].handle, args, 1);
+}
+
+Widget
+CreateMenuItem (Widget menu, char *msg, XtCallbackProc CB, int n)
+{
+    int j=0;
+    Widget entry;
+    Arg args[16];
+    XtSetArg(args[j], XtNleftMargin, 20);   j++;
+    XtSetArg(args[j], XtNrightMargin, 20);  j++;
+    if(!strcmp(msg, "----")) { XtCreateManagedWidget(msg, smeLineObjectClass, menu, args, j); return NULL; }
+    XtSetArg(args[j], XtNlabel, msg);
+    entry = XtCreateManagedWidget("item", smeBSBObjectClass, menu, args, j+1);
+    XtAddCallback(entry, XtNcallback, CB, (caddr_t)(intptr_t) n);
+    return entry;
+}
+
+static Widget
+CreateComboPopup (Widget parent, Option *opt, int n, int fromList, int def)
+{   // fromList determines if the item texts are taken from a list of strings, or from a menu table
+    int i;
+    Widget menu, entry;
+    Arg arg;
+    MenuItem *mb = (MenuItem *) opt->choice;
+    char **list = (char **) opt->choice;
+
+    if(list[0] == NULL) return NULL; // avoid empty menus, as they cause crash
+    menu = XtCreatePopupShell(opt->name, simpleMenuWidgetClass, parent, NULL, 0);
+
+    for (i=0; 1; i++) 
+      {
+       char *msg = fromList ? list[i] : mb[i].string;
+       if(!msg) break;
+       entry = CreateMenuItem(menu, opt->min & NO_GETTEXT ? msg : _(msg), (XtCallbackProc) ComboSelect, (n<<16)+i);
+       if(!fromList) mb[i].handle = (void*) entry; // save item ID, for enabling / checkmarking
+       if(i==def) {
+           XtSetArg(arg, XtNpopupOnEntry, entry);
+           XtSetValues(menu, &arg, 1);
+       }
+      }
+      return menu;
+}
+
+char moveTypeInTranslations[] =
+    "<Key>Return: TypeInProc(1) \n"
+    "<Key>Escape: TypeInProc(0) \n";
+extern char filterTranslations[];
+extern char gameListTranslations[];
+extern char memoTranslations[];
+
+
+char *translationTable[] = { // beware: order is essential!
+   historyTranslations, commentTranslations, moveTypeInTranslations, ICSInputTranslations,
+   filterTranslations, gameListTranslations, memoTranslations
+};
+
+void
+AddHandler (Option *opt, int nr)
+{
+    XtOverrideTranslations(opt->handle, XtParseTranslationTable(translationTable[nr]));
+}
+
+//----------------------------Generic dialog --------------------------------------------
+
+// cloned from Engine Settings dialog (and later merged with it)
+
+Widget shells[NrOfDialogs];
+DialogClass parents[NrOfDialogs];
+WindowPlacement *wp[NrOfDialogs] = { // Beware! Order must correspond to DialogClass enum
+    NULL, &wpComment, &wpTags, NULL, NULL, NULL, NULL, &wpMoveHistory, &wpGameList, &wpEngineOutput, &wpEvalGraph,
+    NULL, NULL, NULL, NULL, /*&wpMain*/ NULL
+};
+
+int
+DialogExists (DialogClass n)
+{   // accessor for use in back-end
+    return shells[n] != NULL;
+}
+
+void
+RaiseWindow (DialogClass dlg)
+{
+    static XEvent xev;
+    Window root = RootWindow(xDisplay, DefaultScreen(xDisplay));
+    Atom atom = XInternAtom (xDisplay, "_NET_ACTIVE_WINDOW", False);
+
+    xev.xclient.type = ClientMessage;
+    xev.xclient.serial = 0;
+    xev.xclient.send_event = True;
+    xev.xclient.display = xDisplay;
+    xev.xclient.window = XtWindow(shells[dlg]);
+    xev.xclient.message_type = atom;
+    xev.xclient.format = 32;
+    xev.xclient.data.l[0] = 1;
+    xev.xclient.data.l[1] = CurrentTime;
+
+    XSendEvent (xDisplay,
+          root, False,
+          SubstructureRedirectMask | SubstructureNotifyMask,
+          &xev);
+
+    XFlush(xDisplay); 
+    XSync(xDisplay, False);
+}
+
+int
+PopDown (DialogClass n)
+{   // pops down any dialog created by GenericPopUp (or returns False if it wasn't up), unmarks any associated marked menu
+    int j;
+    Arg args[10];
+    Dimension windowH, windowW; Position windowX, windowY;
+    if (!shellUp[n] || !shells[n]) return 0;
+    if(n && wp[n]) { // remember position
+       j = 0;
+       XtSetArg(args[j], XtNx, &windowX); j++;
+       XtSetArg(args[j], XtNy, &windowY); j++;
+       XtSetArg(args[j], XtNheight, &windowH); j++;
+       XtSetArg(args[j], XtNwidth, &windowW); j++;
+       XtGetValues(shells[n], args, j);
+       wp[n]->x = windowX;
+       wp[n]->x = windowY;
+       wp[n]->width  = windowW;
+       wp[n]->height = windowH;
+    }
+    previous = NULL;
+    XtPopdown(shells[n]);
+    shellUp[n]--; // count rather than clear
+    if(n == 0 || n >= PromoDlg) XtDestroyWidget(shells[n]), shells[n] = NULL;
+    if(marked[n]) {
+       MarkMenuItem(marked[n], False);
+       marked[n] = NULL;
+    }
+    if(!n && n != BrowserDlg) currentCps = NULL; // if an Engine Settings dialog was up, we must be popping it down now
+    currentOption = dialogOptions[TransientDlg]; // just in case a transient dialog was up (to allow its check and combo callbacks to work)
+    RaiseWindow(parents[n]);
+    if(parents[n] == BoardWindow) XtSetKeyboardFocus(shellWidget, formWidget);
+    return 1;
+}
+
+void
+GenericPopDown (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{   // to cause popdown through a translation (Delete Window button!)
+    int dlg = atoi(prms[0]);
+    Widget sh = shells[dlg];
+    if(shellUp[BrowserDlg] && dlg != BrowserDlg || dialogError) return; // prevent closing dialog when it has an open file-browse daughter
+    shells[dlg] = w;
+    PopDown(dlg);
+    shells[dlg] = sh; // restore
+}
+
+int
+AppendText (Option *opt, char *s)
+{
+    XawTextBlock t;
+    char *v;
+    int len;
+    GetWidgetText(opt, &v);
+    len = strlen(v);
+    t.ptr = s; t.firstPos = 0; t.length = strlen(s); t.format = XawFmt8Bit;
+    XawTextReplace(opt->handle, len, len, &t);
+    return len;
+}
+
+void
+SetColor (char *colorName, Option *box)
+{       // sets the color of a widget
+       Arg args[5];
+       Pixel buttonColor;
+       XrmValue vFrom, vTo;
+       if (!appData.monoMode) {
+           vFrom.addr = (caddr_t) colorName;
+           vFrom.size = strlen(colorName);
+           XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
+           if (vTo.addr == NULL) {
+               buttonColor = (Pixel) -1;
+           } else {
+               buttonColor = *(Pixel *) vTo.addr;
+           }
+       } else buttonColor = timerBackgroundPixel;
+       XtSetArg(args[0], XtNbackground, buttonColor);;
+       XtSetValues(box->handle, args, 1);
+}
+
+void
+ColorChanged (Widget w, XtPointer data, XEvent *event, Boolean *b)
+{   // for detecting a typed change in color
+    char buf[10];
+    if ( (XLookupString(&(event->xkey), buf, 2, NULL, NULL) == 1) && *buf == '\r' )
+       RefreshColor((int)(intptr_t) data, 0);
+}
+
+static void
+GraphEventProc(Widget widget, caddr_t client_data, XEvent *event)
+{   // handle expose and mouse events on Graph widget
+    Dimension w, h;
+    Arg args[16];
+    int j, button=10, f=1, sizing=0;
+    Option *opt, *graph = (Option *) client_data;
+    PointerCallback *userHandler = graph->target;
+
+    if (!XtIsRealized(widget)) return;
+
+    switch(event->type) {
+       case Expose: // make handling of expose events generic, just copying from memory buffer (->choice) to display (->textValue)
+           /* Get window size */
+           j = 0;
+           XtSetArg(args[j], XtNwidth, &w); j++;
+           XtSetArg(args[j], XtNheight, &h); j++;
+           XtGetValues(widget, args, j);
+
+           if(w < graph->max || w > graph->max + 1 || h != graph->value) { // use width fudge of 1 pixel
+               if(((XExposeEvent*)event)->count >= 0) { // suppress sizing on expose for ordered redraw in response to sizing.
+                   sizing = 1;
+                   graph->max = w; graph->value = h; // note: old values are kept if we we don't exceed width fudge
+               }
+           } else w = graph->max;
+
+           if(sizing && ((XExposeEvent*)event)->count > 0) { graph->max = 0; return; } // don't bother if further exposure is pending during resize
+           if(!graph->textValue || sizing) { // create surfaces of new size for display widget
+               if(graph->textValue) cairo_surface_destroy((cairo_surface_t *)graph->textValue);
+               graph->textValue = (char*) cairo_xlib_surface_create(xDisplay, XtWindow(widget), DefaultVisual(xDisplay, 0), w, h);
+           }
+           if(sizing) { // the memory buffer was already created in GenericPopup(),
+                        // to give drawing routines opportunity to use it before first expose event
+                        // (which are only processed when main gets to the event loop, so after all init!)
+                        // so only change when size is no longer good
+               if(graph->choice) cairo_surface_destroy((cairo_surface_t *) graph->choice);
+               graph->choice = (char**) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
+               break;
+           }
+           w = ((XExposeEvent*)event)->width;
+           if(((XExposeEvent*)event)->x + w > graph->max) w--; // cut off fudge pixel
+           if(w) ExposeRedraw(graph, ((XExposeEvent*)event)->x, ((XExposeEvent*)event)->y, w, ((XExposeEvent*)event)->height);
+           return;
+       case MotionNotify:
+           f = 0;
+           w = ((XButtonEvent*)event)->x; h = ((XButtonEvent*)event)->y;
+           break;
+       case ButtonRelease:
+           f = -1; // release indicated by negative button numbers
+       case ButtonPress:
+           w = ((XButtonEvent*)event)->x; h = ((XButtonEvent*)event)->y;
+           switch(((XButtonEvent*)event)->button) {
+               case Button1: button = 1; break;
+               case Button2: button = 2; break;
+               case Button3: button = 3; break;
+               case Button4: button = 4; break;
+               case Button5: button = 5; break;
+           }
+    }
+    button *= f;
+    opt = userHandler(button, w, h);
+    if(opt) { // user callback specifies a context menu; pop it up
+       XUngrabPointer(xDisplay, CurrentTime);
+       XtCallActionProc(widget, "XawPositionSimpleMenu", event, &(opt->name), 1);
+       XtPopupSpringLoaded(opt->handle);
+    }
+    XSync(xDisplay, False);
+}
+
+void
+GraphExpose (Option *opt, int x, int y, int w, int h)
+{
+  XExposeEvent e;
+  if(!opt->handle) return;
+  e.x = x; e.y = y; e.width = w; e.height = h; e.count = -1; e.type = Expose; // count = -1: kludge to suppress sizing
+  GraphEventProc(opt->handle, (caddr_t) opt, (XEvent *) &e); // fake expose event
+}
+
+static void
+GenericCallback (Widget w, XtPointer client_data, XtPointer call_data)
+{   // all Buttons in a dialog (including OK, cancel) invoke this
+    String name;
+    Arg args[16];
+    char buf[MSG_SIZ];
+    int data = (intptr_t) client_data;
+    DialogClass dlg;
+    Widget sh = XtParent(XtParent(XtParent(w))), oldSh;
+
+    currentOption = dialogOptions[dlg=data>>16]; data &= 0xFFFF;
+    oldSh = shells[dlg]; shells[dlg] = sh; // bow to reality
+    if (data == 30000) { // cancel
+        PopDown(dlg); 
+    } else
+    if (data == 30001) { // save buttons imply OK
+        if(GenericReadout(currentOption, -1)) PopDown(dlg); // calls OK-proc after full readout, but no popdown if it returns false
+    } else
+
+    if(currentCps && dlg != BrowserDlg) {
+       XtSetArg(args[0], XtNlabel, &name);
+       XtGetValues(w, args, 1);
+       if(currentOption[data].type == SaveButton) GenericReadout(currentOption, -1);
+       snprintf(buf, MSG_SIZ,  "option %s\n", name);
+       SendToProgram(buf, currentCps);
+    } else ((ButtonCallback*) currentOption[data].target)(data);
+
+    shells[dlg] = oldSh; // in case of multiple instances, restore previous (as this one could be popped down now)
+}
+
+void
+TabProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{   // for transfering focus to the next text-edit
+    Option *opt;
+    for(opt = currentOption; opt->type != EndMark; opt++) {
+       if(opt->handle == w) {
+           while(++opt) {
+               if(opt->type == EndMark) opt = currentOption; // wrap
+               if(opt->handle == w) return; // full circle
+               if(opt->type == TextBox || opt->type == Spin || opt->type == Fractional || opt->type == FileName || opt->type == PathName) {
+                   SetFocus(opt->handle, XtParent(XtParent(XtParent(w))), NULL, 0);
+                   return;
+               }
+           }
+       }
+    }
+}
+
+void
+WheelProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{   // for scrolling a widget seen through a viewport with the mouse wheel (ListBox!)
+    int j=0, n = atoi(prms[0]);
+    static char *params[3] = { "", "Continuous", "Proportional" };
+    Arg args[16];
+    float h, top;
+    Widget v;
+    if(!n) { // transient dialogs also use this for list-selection callback
+       n = prms[1][0]-'0';
+       Option *opt=dialogOptions[prms[2][0]-'A'] + n;
+       if(opt->textValue) ((ListBoxCallback*) opt->textValue)(n, SelectedListBoxItem(opt));
+       return;
+    }
+    v = XtNameToWidget(XtParent(w), "vertical");
+    if(!v) return;
+    XtSetArg(args[j], XtNshown, &h); j++;
+    XtSetArg(args[j], XtNtopOfThumb, &top); j++;
+    XtGetValues(v, args, j);
+    top += 0.1f*h*n; if(top < 0.f) top = 0.;
+    XtCallActionProc(v, "StartScroll", event, params+1, 1);
+    XawScrollbarSetThumb(v, top, -1.0);
+    XtCallActionProc(v, "NotifyThumb", event, params, 0);
+//    XtCallActionProc(w, "NotifyScroll", event, params+2, 1);
+    XtCallActionProc(v, "EndScroll", event, params, 0);
+}
+
+static char *oneLiner  =
+   "<Key>Return: redraw-display() \n \
+    <Key>Tab: TabProc() \n ";
+static char scrollTranslations[] =
+   "<Btn1Up>(2): WheelProc(0 0 A) \n \
+    <Btn4Down>: WheelProc(-1) \n \
+    <Btn5Down>: WheelProc(1) \n ";
+
+static void
+SqueezeIntoBox (Option *opt, int nr, int width)
+{   // size buttons in bar to fit, clipping button names where necessary
+    int i, wtot = 0;
+    Dimension widths[20], oldWidths[20];
+    Arg arg;
+    for(i=1; i<nr; i++) {
+       XtSetArg(arg, XtNwidth, &widths[i]);
+       XtGetValues(opt[i].handle, &arg, 1);
+       wtot +=  oldWidths[i] = widths[i];
+    }
+    opt->min = wtot;
+    if(width <= 0) return;
+    while(wtot > width) {
+       int wmax=0, imax=0;
+       for(i=1; i<nr; i++) if(widths[i] > wmax) wmax = widths[imax=i];
+       widths[imax]--;
+       wtot--;
+    }
+    for(i=1; i<nr; i++) if(widths[i] != oldWidths[i]) {
+       XtSetArg(arg, XtNwidth, widths[i]);
+       XtSetValues(opt[i].handle, &arg, 1);
+    }
+    opt->min = wtot;
+}
+
+int
+SetPositionAndSize (Arg *args, Widget leftNeigbor, Widget topNeigbor, int b, int w, int h, int chaining)
+{   // sizing and positioning most widgets have in common
+    int j = 0;
+    // first position the widget w.r.t. earlier ones
+    if(chaining & 1) { // same row: position w.r.t. last (on current row) and lastrow
+       XtSetArg(args[j], XtNfromVert, topNeigbor); j++;
+       XtSetArg(args[j], XtNfromHoriz, leftNeigbor); j++;
+    } else // otherwise it goes at left margin (which is default), below the previous element
+       XtSetArg(args[j], XtNfromVert, leftNeigbor),  j++;
+    // arrange chaining ('2'-bit indicates top and bottom chain the same)
+    if((chaining & 14) == 6) XtSetArg(args[j], XtNtop,    XtChainBottom), j++;
+    if((chaining & 14) == 10) XtSetArg(args[j], XtNbottom, XtChainTop ), j++;
+    if(chaining & 4) XtSetArg(args[j], XtNbottom, XtChainBottom ), j++;
+    if(chaining & 8) XtSetArg(args[j], XtNtop,    XtChainTop), j++;
+    if(chaining & 0x10) XtSetArg(args[j], XtNright, XtChainRight), j++;
+    if(chaining & 0x20) XtSetArg(args[j], XtNleft,  XtChainRight), j++;
+    if(chaining & 0x40) XtSetArg(args[j], XtNright, XtChainLeft ), j++;
+    if(chaining & 0x80) XtSetArg(args[j], XtNleft,  XtChainLeft ), j++;
+    // set size (if given)
+    if(w) XtSetArg(args[j], XtNwidth, w), j++;
+    if(h) XtSetArg(args[j], XtNheight, h),  j++;
+    // color
+    if(!appData.monoMode) {
+       if(!b && appData.dialogColor[0]) XtSetArg(args[j], XtNbackground, dialogColor),  j++;
+       if(b == 3 && appData.buttonColor[0]) XtSetArg(args[j], XtNbackground, buttonColor),  j++;
+    }
+    if(b == 3) b = 1;
+    // border
+    XtSetArg(args[j], XtNborderWidth, b);  j++;
+    return j;
+}
+
+int
+GenericPopUp (Option *option, char *title, DialogClass dlgNr, DialogClass parent, int modal, int top)
+{
+    Arg args[24];
+    Widget popup, layout, dialog=NULL, edit=NULL, form,  last, b_ok, b_cancel, previousPane = NULL, textField = NULL, oldForm, oldLastRow, oldForeLast;
+    Window root, child;
+    int x, y, i, j, height=999, width=1, h, c, w, shrink=FALSE, stack = 0, box, chain;
+    int win_x, win_y, maxWidth, maxTextWidth;
+    unsigned int mask;
+    char def[MSG_SIZ], *msg, engineDlg = (currentCps != NULL && dlgNr != BrowserDlg);
+    static char pane[6] = "paneX";
+    Widget texts[100], forelast = NULL, anchor, widest, lastrow = NULL, browse = NULL;
+    Dimension bWidth = 50;
+
+    if(dlgNr < PromoDlg && shellUp[dlgNr]) return 0; // already up
+    if(dlgNr && dlgNr < PromoDlg && shells[dlgNr]) { // reusable, and used before (but popped down)
+       XtPopup(shells[dlgNr], XtGrabNone);
+       shellUp[dlgNr] = True;
+       return 0;
+    }
+
+    dialogOptions[dlgNr] = option; // make available to callback
+    // post currentOption globally, so Spin and Combo callbacks can already use it
+    // WARNING: this kludge does not work for persistent dialogs, so that these cannot have spin or combo controls!
+    currentOption = option;
+
+    if(engineDlg) { // Settings popup for engine: format through heuristic
+       int n = currentCps->nrOptions;
+       if(!n) { DisplayNote(_("Engine has no options")); currentCps = NULL; return 0; }
+       if(n > 50) width = 4; else if(n>24) width = 2; else width = 1;
+       height = n / width + 1;
+       if(n && (currentOption[n-1].type == Button || currentOption[n-1].type == SaveButton)) currentOption[n].min = SAME_ROW; // OK on same line
+       currentOption[n].type = EndMark; currentOption[n].target = NULL; // delimit list by callback-less end mark
+    }
+     i = 0;
+    XtSetArg(args[i], XtNresizable, True); i++;
+    shells[BoardWindow] = shellWidget; parents[dlgNr] = parent;
+
+    if(dlgNr == BoardWindow) popup = shellWidget; else
+    popup = shells[dlgNr] =
+      XtCreatePopupShell(title, !top || !appData.topLevel ? transientShellWidgetClass : topLevelShellWidgetClass,
+                                                           shells[parent], args, i);
+
+    layout =
+      XtCreateManagedWidget(layoutName, formWidgetClass, popup,
+                           layoutArgs, XtNumber(layoutArgs));
+    if(!appData.monoMode && appData.dialogColor[0]) XtSetArg(args[0], XtNbackground, dialogColor);
+    XtSetValues(layout, args, 1);
+
+  for(c=0; c<width; c++) {
+    pane[4] = 'A'+c;
+    form =
+      XtCreateManagedWidget(pane, formWidgetClass, layout,
+                           formArgs, XtNumber(formArgs));
+    j=0;
+    XtSetArg(args[j], stack ? XtNfromVert : XtNfromHoriz, previousPane);  j++;
+    if(!appData.monoMode && appData.dialogColor[0]) XtSetArg(args[j], XtNbackground, dialogColor),  j++;
+    XtSetValues(form, args, j);
+    lastrow = forelast = NULL;
+    previousPane = form;
+
+    last = widest = NULL; anchor = lastrow;
+    for(h=0; h<height || c == width-1; h++) {
+       i = h + c*height;
+       if(option[i].type == EndMark) break;
+       if(option[i].type == -1) continue;
+       lastrow = forelast;
+       forelast = last;
+       switch(option[i].type) {
+         case Fractional:
+           snprintf(def, MSG_SIZ,  "%.2f", *(float*)option[i].target);
+           option[i].value = *(float*)option[i].target;
+           goto tBox;
+         case Spin:
+           if(!engineDlg) option[i].value = *(int*)option[i].target;
+           snprintf(def, MSG_SIZ,  "%d", option[i].value);
+         case TextBox:
+         case FileName:
+         case PathName:
+          tBox:
+           if(option[i].name[0]) { // prefixed by label with option name
+               j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+                                      0 /* w */, textHeight /* h */, 0xC0 /* chain to left edge */);
+               XtSetArg(args[j], XtNjustify, XtJustifyLeft);  j++;
+               XtSetArg(args[j], XtNlabel, _(option[i].name));  j++;
+               texts[h] = dialog = XtCreateManagedWidget(option[i].name, labelWidgetClass, form, args, j);
+           } else texts[h] = dialog = NULL; // kludge to position from left margin
+           w = option[i].type == Spin || option[i].type == Fractional ? 70 : option[i].max ? option[i].max : 205;
+           if(option[i].type == FileName || option[i].type == PathName) w -= 55;
+           j = SetPositionAndSize(args, dialog, last, 1 /* border */,
+                                  w /* w */, option[i].type == TextBox ? option[i].value : 0 /* h */, 0x91 /* chain full width */);
+           if(option[i].type == TextBox) { // decorations for multi-line text-edits
+               if(option[i].min & T_VSCRL) { XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++; }
+               if(option[i].min & T_HSCRL) { XtSetArg(args[j], XtNscrollHorizontal, XawtextScrollAlways);  j++; }
+               if(option[i].min & T_FILL)  { XtSetArg(args[j], XtNautoFill, True);  j++; }
+               if(option[i].min & T_WRAP)  { XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++; }
+               if(option[i].min & T_TOP)   { XtSetArg(args[j], XtNtop, XtChainTop); j++;
+                   if(!option[i].value) {    XtSetArg(args[j], XtNbottom, XtChainTop); j++;
+                                             XtSetValues(dialog, args+j-2, 2);
+                   }
+               }
+           } else shrink = TRUE;
+           XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
+           XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
+           XtSetArg(args[j], XtNdisplayCaret, False);  j++;
+           XtSetArg(args[j], XtNresizable, True);  j++;
+           XtSetArg(args[j], XtNinsertPosition, 9999);  j++;
+           XtSetArg(args[j], XtNstring, option[i].type==Spin || option[i].type==Fractional ? def : 
+                               engineDlg ? option[i].textValue : *(char**)option[i].target);  j++;
+           edit = last;
+           option[i].handle = (void*)
+               (textField = last = XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j));
+           XtAddEventHandler(last, ButtonPressMask, False, SetFocus, (XtPointer) popup); // gets focus on mouse click
+           if(option[i].min == 0 || option[i].type != TextBox)
+               XtOverrideTranslations(last, XtParseTranslationTable(oneLiner)); // standard handler for <Enter> and <Tab>
+
+           if(option[i].type == TextBox || option[i].type == Fractional) break;
+
+           // add increment and decrement controls for spin
+           if(option[i].type == FileName || option[i].type == PathName) {
+               msg = _("browse"); w = 0; // automatically scale to width of text
+               j = textHeight ? textHeight : 0;
+           } else {
+               w = 20; msg = "+"; j = textHeight/2; // spin button
+           }
+           j = SetPositionAndSize(args, last, edit, 3 /* border */,
+                                  w /* w */, j /* h */, 0x31 /* chain to right edge */);
+           edit = XtCreateManagedWidget(msg, commandWidgetClass, form, args, j);
+           XtAddCallback(edit, XtNcallback, SpinCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
+           if(w == 0) browse = edit;
+
+           if(option[i].type != Spin) break;
+
+           j = SetPositionAndSize(args, last, edit, 3 /* border */,
+                                  20 /* w */, textHeight/2 /* h */, 0x31 /* chain to right edge */);
+           XtSetArg(args[j], XtNvertDistance, -1);  j++;
+           last = XtCreateManagedWidget("-", commandWidgetClass, form, args, j);
+           XtAddCallback(last, XtNcallback, SpinCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
+           break;
+         case CheckBox:
+           if(!engineDlg) option[i].value = *(Boolean*)option[i].target; // where checkbox callback uses it
+           j = SetPositionAndSize(args, last, lastrow, 1 /* border */,
+                                  textHeight/2 /* w */, textHeight/2 /* h */, 0xC0 /* chain both to left edge */);
+           XtSetArg(args[j], XtNvertDistance, (textHeight+2)/4 + 3);  j++;
+           XtSetArg(args[j], XtNstate, option[i].value);  j++;
+           lastrow  = last;
+           option[i].handle = (void*)
+               (last = XtCreateManagedWidget(" ", toggleWidgetClass, form, args, j));
+           j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+                                  option[i].max /* w */, textHeight /* h */, 0xC1 /* chain */);
+           XtSetArg(args[j], XtNjustify, XtJustifyLeft);  j++;
+           XtSetArg(args[j], XtNlabel, _(option[i].name));  j++;
+           last = XtCreateManagedWidget("label", commandWidgetClass, form, args, j);
+           // make clicking the text toggle checkbox
+           XtAddEventHandler(last, ButtonPressMask, False, CheckCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
+           shrink = TRUE; // following buttons must get text height
+           break;
+         case Label:
+           msg = option[i].name;
+           if(!msg) break;
+           chain = option[i].min;
+           if(chain & SAME_ROW) forelast = lastrow; else shrink = FALSE;
+           j = SetPositionAndSize(args, last, lastrow, (chain & 2) != 0 /* border */,
+                                  option[i].max /* w */, shrink ? textHeight : 0 /* h */, chain | 2 /* chain */);
+#if ENABLE_NLS
+           if(option[i].choice) XtSetArg(args[j], XtNfontSet, *(XFontSet*)option[i].choice), j++;
+#else
+           if(option[i].choice) XtSetArg(args[j], XtNfont, (XFontStruct*)option[i].choice), j++;
+#endif
+           XtSetArg(args[j], XtNresizable, False);  j++;
+           XtSetArg(args[j], XtNjustify, XtJustifyLeft);  j++;
+           XtSetArg(args[j], XtNlabel, _(msg));  j++;
+           option[i].handle = (void*) (last = XtCreateManagedWidget("label", labelWidgetClass, form, args, j));
+           if(option[i].target) // allow user to specify event handler for button presses
+               XtAddEventHandler(last, ButtonPressMask, False, CheckCallback, (XtPointer)(intptr_t) i + 256*dlgNr);
+           break;
+         case SaveButton:
+         case Button:
+           if(option[i].min & SAME_ROW) {
+               chain = 0x31; // 0011.0001 = both left and right side to right edge
+               forelast = lastrow;
+           } else chain = 0, shrink = FALSE;
+           j = SetPositionAndSize(args, last, lastrow, 3 /* border */,
+                                  option[i].max /* w */, shrink ? textHeight : 0 /* h */, option[i].min & 0xE | chain /* chain */);
+           XtSetArg(args[j], XtNlabel, _(option[i].name));  j++;
+           if(option[i].textValue) { // special for buttons of New Variant dialog
+               XtSetArg(args[j], XtNsensitive, appData.noChessProgram || option[i].value < 0
+                                        || strstr(first.variants, VariantName(option[i].value))); j++;
+               XtSetArg(args[j], XtNborderWidth, (gameInfo.variant == option[i].value)+1); j++;
+           }
+           option[i].handle = (void*)
+               (dialog = last = XtCreateManagedWidget(option[i].name, commandWidgetClass, form, args, j));
+           if(option[i].choice && ((char*)option[i].choice)[0] == '#' && !engineDlg) { // for the color picker default-reset
+               SetColor( *(char**) option[i-1].target, &option[i]);
+               XtAddEventHandler(option[i-1].handle, KeyReleaseMask, False, ColorChanged, (XtPointer)(intptr_t) i-1);
+           }
+           XtAddCallback(last, XtNcallback, GenericCallback, (XtPointer)(intptr_t) i + (dlgNr<<16)); // invokes user callback
+           if(option[i].textValue) SetColor( option[i].textValue, &option[i]); // for new-variant buttons
+           break;
+         case ComboBox:
+           j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+                                  0 /* w */, textHeight /* h */, 0xC0 /* chain both sides to left edge */);
+           XtSetArg(args[j], XtNjustify, XtJustifyLeft);  j++;
+           XtSetArg(args[j], XtNlabel, _(option[i].name));  j++;
+           texts[h] = dialog = XtCreateManagedWidget(option[i].name, labelWidgetClass, form, args, j);
+
+           if(option[i].min & COMBO_CALLBACK) msg = _(option[i].name); else {
+             if(!engineDlg) SetCurrentComboSelection(option+i);
+             msg=_(((char**)option[i].choice)[option[i].value]);
+           }
+
+           j = SetPositionAndSize(args, dialog, last, (option[i].min & 2) == 0 /* border */,
+                                  option[i].max && !engineDlg ? option[i].max : 100 /* w */,
+                                  textHeight /* h */, 0x91 /* chain */); // same row as its label!
+           XtSetArg(args[j], XtNmenuName, XtNewString(option[i].name));  j++;
+           XtSetArg(args[j], XtNlabel, msg);  j++;
+           shrink = TRUE;
+           option[i].handle = (void*)
+               (last = XtCreateManagedWidget(" ", menuButtonWidgetClass, form, args, j));
+           CreateComboPopup(last, option + i, i + 256*dlgNr, TRUE, -1);
+           values[i] = option[i].value;
+           break;
+         case ListBox:
+           // Listbox goes in viewport, as needed for game list
+           if(option[i].min & SAME_ROW) forelast = lastrow;
+           j = SetPositionAndSize(args, last, lastrow, 1 /* border */,
+                                  option[i].max /* w */, option[i].value /* h */, option[i].min /* chain */);
+           XtSetArg(args[j], XtNresizable, False);  j++;
+           XtSetArg(args[j], XtNallowVert, True); j++; // scoll direction
+           last =
+             XtCreateManagedWidget("viewport", viewportWidgetClass, form, args, j);
+           j = 0; // now list itself
+           XtSetArg(args[j], XtNdefaultColumns, 1);  j++;
+           XtSetArg(args[j], XtNforceColumns, True);  j++;
+           XtSetArg(args[j], XtNverticalList, True);  j++;
+           option[i].handle = (void*)
+               (edit = XtCreateManagedWidget("list", listWidgetClass, last, args, j));
+           XawListChange(option[i].handle, option[i].target, 0, 0, True);
+           XawListHighlight(option[i].handle, 0);
+           scrollTranslations[25] = '0' + i;
+           scrollTranslations[27] = 'A' + dlgNr;
+           XtOverrideTranslations(edit, XtParseTranslationTable(scrollTranslations)); // for mouse-wheel
+           break;
+         case Graph:
+           j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+                                  option[i].max /* w */, option[i].value /* h */, option[i].min /* chain */);
+           option[i].handle = (void*)
+               (last = XtCreateManagedWidget("graph", widgetClass, form, args, j));
+           XtAddEventHandler(last, ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask, False,
+                     (XtEventHandler) GraphEventProc, &option[i]); // mandatory user-supplied expose handler
+           if(option[i].min & SAME_ROW) last = forelast, forelast = lastrow;
+           option[i].choice = (char**) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, option[i].max, option[i].value); // image buffer
+           break;
+         case PopUp: // note: used only after Graph, so 'last' refers to the Graph widget
+           option[i].handle = (void*) CreateComboPopup(last, option + i, i + 256*dlgNr, TRUE, option[i].value);
+           break;
+         case BoxBegin:
+           if(option[i].min & SAME_ROW) forelast = lastrow;
+           j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+                                  0 /* w */, 0 /* h */, option[i].min /* chain */);
+           XtSetArg(args[j], XtNorientation, XtorientHorizontal);  j++;
+           XtSetArg(args[j], XtNvSpace, 0);                        j++;
+           option[box=i].handle = (void*)
+               (last = XtCreateWidget("box", boxWidgetClass, form, args, j));
+           oldForm = form; form = last; oldLastRow = lastrow; oldForeLast = forelast;
+           lastrow = NULL; last = NULL;
+           break;
+         case DropDown:
+           j = SetPositionAndSize(args, last, lastrow, 0 /* border */,
+                                  0 /* w */, 0 /* h */, 1 /* chain (always on same row) */);
+           forelast = lastrow;
+           msg = _(option[i].name); // write name on the menu button
+           XtSetArg(args[j], XtNmenuName, XtNewString(option[i].name));  j++;
+           XtSetArg(args[j], XtNlabel, msg);  j++;
+           option[i].handle = (void*)
+               (last = XtCreateManagedWidget(option[i].name, menuButtonWidgetClass, form, args, j));
+           option[i].textValue = (char*) CreateComboPopup(last, option + i, i + 256*dlgNr, FALSE, -1);
+           break;
+         case BoxEnd:
+           XtManageChildren(&form, 1);
+           SqueezeIntoBox(&option[box], i-box, option[box].max);
+           if(option[i].target) ((ButtonCallback*)option[i].target)(box); // callback that can make sizing decisions
+           last = form; lastrow = oldLastRow; form = oldForm; forelast = oldForeLast;
+           break;
+         case Break:
+           width++;
+           height = i+1;
+           stack = !(option[i].min & SAME_ROW);
+           break;
+       default:
+           printf("GenericPopUp: unexpected case in switch.\n");
+           break;
+       }
+    }
+
+    // make an attempt to align all spins and textbox controls
+    maxWidth = maxTextWidth = 0;
+    if(browse != NULL) {
+       j=0;
+       XtSetArg(args[j], XtNwidth, &bWidth);  j++;
+       XtGetValues(browse, args, j);
+    }
+    for(h=0; h<height || c == width-1; h++) {
+       i = h + c*height;
+       if(option[i].type == EndMark) break;
+       if(option[i].type == Spin || option[i].type == TextBox || option[i].type == ComboBox
+                                 || option[i].type == PathName || option[i].type == FileName) {
+           Dimension w;
+           if(!texts[h]) continue;
+           j=0;
+           XtSetArg(args[j], XtNwidth, &w);  j++;
+           XtGetValues(texts[h], args, j);
+           if(option[i].type == Spin) {
+               if(w > maxWidth) maxWidth = w;
+               widest = texts[h];
+           } else {
+               if(w > maxTextWidth) maxTextWidth = w;
+               if(!widest) widest = texts[h];
+           }
+       }
+    }
+    if(maxTextWidth + 110 < maxWidth)
+        maxTextWidth = maxWidth - 110;
+    else maxWidth = maxTextWidth + 110;
+    for(h=0; h<height || c == width-1; h++) {
+       i = h + c*height;
+       if(option[i].type == EndMark) break;
+       if(!texts[h]) continue; // Note: texts[h] can be undefined (giving errors in valgrind), but then both if's below will be false.
+       j=0;
+       if(option[i].type == Spin) {
+           XtSetArg(args[j], XtNwidth, maxWidth);  j++;
+           XtSetValues(texts[h], args, j);
+       } else
+       if(option[i].type == TextBox || option[i].type == ComboBox || option[i].type == PathName || option[i].type == FileName) {
+           XtSetArg(args[j], XtNwidth, maxTextWidth);  j++;
+           XtSetValues(texts[h], args, j);
+           if(bWidth != 50 && (option[i].type == FileName || option[i].type == PathName)) {
+               int tWidth = (option[i].max ? option[i].max : 205) - 5 - bWidth;
+               j = 0;
+               XtSetArg(args[j], XtNwidth, tWidth);  j++;
+               XtSetValues(option[i].handle, args, j);
+           }
+       }
+    }
+  }
+
+    if(option[i].min & SAME_ROW) { // even when OK suppressed this EndMark bit can request chaining of last row to bottom
+       for(j=i-1; option[j+1].min & SAME_ROW; j--) {
+           XtSetArg(args[0], XtNtop, XtChainBottom);
+           XtSetArg(args[1], XtNbottom, XtChainBottom);
+           XtSetValues(option[j].handle, args, 2);
+       }
+       if((option[j].type == TextBox || option[j].type == ListBox) && option[j].name[0] == NULLCHAR) {
+           Widget w = option[j].handle;
+           if(option[j].type == ListBox) w = XtParent(w); // for listbox we must chain viewport
+           XtSetArg(args[0], XtNbottom, XtChainBottom);
+           XtSetValues(w, args, 1);
+       }
+       lastrow = forelast;
+    } else shrink = FALSE, lastrow = last, last = widest ? widest : dialog;
+    j = SetPositionAndSize(args, last, anchor ? anchor : lastrow, 3 /* border */,
+                          0 /* w */, shrink ? textHeight : 0 /* h */, 0x37 /* chain: right, bottom and use both neighbors */);
+
+  if(!(option[i].min & NO_OK)) {
+    option[i].handle = b_ok = XtCreateManagedWidget(_("OK"), commandWidgetClass, form, args, j);
+    XtAddCallback(b_ok, XtNcallback, GenericCallback, (XtPointer)(intptr_t) (30001 + (dlgNr<<16)));
+    if(!(option[i].min & NO_CANCEL)) {
+      XtSetArg(args[1], XtNfromHoriz, b_ok); // overwrites!
+      b_cancel = XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
+      XtAddCallback(b_cancel, XtNcallback, GenericCallback, (XtPointer)(intptr_t) (30000 + (dlgNr<<16)));
+    }
+  }
+
+    XtRealizeWidget(popup);
+    if(dlgNr != BoardWindow) { // assign close button, and position w.r.t. pointer, if not main window
+       XSetWMProtocols(xDisplay, XtWindow(popup), &wm_delete_window, 1);
+       snprintf(def, MSG_SIZ, "<Message>WM_PROTOCOLS: GenericPopDown(\"%d\") \n", dlgNr);
+       XtAugmentTranslations(popup, XtParseTranslationTable(def));
+       XQueryPointer(xDisplay, xBoardWindow, &root, &child,
+                       &x, &y, &win_x, &win_y, &mask);
+
+       XtSetArg(args[0], XtNx, x - 10);
+       XtSetArg(args[1], XtNy, y - 30);
+       XtSetValues(popup, args, 2);
+    }
+    XtPopup(popup, modal ? XtGrabExclusive : XtGrabNone);
+    shellUp[dlgNr]++; // count rather than flag
+    previous = NULL;
+    if(textField) SetFocus(textField, popup, (XEvent*) NULL, False);
+    if(dlgNr && wp[dlgNr] && wp[dlgNr]->width > 0) { // if persistent window-info available, reposition
+       j = 0;
+       XtSetArg(args[j], XtNheight, (Dimension) (wp[dlgNr]->height));  j++;
+       XtSetArg(args[j], XtNwidth,  (Dimension) (wp[dlgNr]->width));  j++;
+       XtSetArg(args[j], XtNx, (Position) (wp[dlgNr]->x));  j++;
+       XtSetArg(args[j], XtNy, (Position) (wp[dlgNr]->y));  j++;
+       XtSetValues(popup, args, j);
+    }
+    RaiseWindow(dlgNr);
+    return 1; // tells caller he must do initialization (e.g. add specific event handlers)
+}
+
+
+/* function called when the data to Paste is ready */
+static void
+SendTextCB (Widget w, XtPointer client_data, Atom *selection,
+           Atom *type, XtPointer value, unsigned long *len, int *format)
+{
+  char buf[MSG_SIZ], *p = (char*) textOptions[(int)(intptr_t) client_data].choice, *name = (char*) value, *q;
+  if (value==NULL || *len==0) return; /* nothing selected, abort */
+  name[*len]='\0';
+  strncpy(buf, p, MSG_SIZ);
+  q = strstr(p, "$name");
+  snprintf(buf + (q-p), MSG_SIZ -(q-p), "%s%s", name, q+5);
+  SendString(buf);
+  XtFree(value);
+}
+
+void
+SendText (int n)
+{
+    char *p = (char*) textOptions[n].choice;
+    if(strstr(p, "$name")) {
+       XtGetSelectionValue(menuBarWidget,
+         XA_PRIMARY, XA_STRING,
+         /* (XtSelectionCallbackProc) */ SendTextCB,
+         (XtPointer) (intptr_t) n, /* client_data passed to PastePositionCB */
+         CurrentTime
+       );
+    } else SendString(p);
+}
+
+void
+SetInsertPos (Option *opt, int pos)
+{
+    Arg args[16];
+    XtSetArg(args[0], XtNinsertPosition, pos);
+    XtSetValues(opt->handle, args, 1);
+//    SetFocus(opt->handle, shells[InputBoxDlg], NULL, False); // No idea why this does not work, and the following is needed:
+//    XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime);
+}
+
+void
+TypeInProc (Widget w, XEvent *event, String *prms, Cardinal *nprms)
+{   // can be used as handler for any text edit in any dialog (from GenericPopUp, that is)
+    int n = prms[0][0] - '0';
+    Widget sh = XtParent(XtParent(XtParent(w))); // popup shell
+
+    if(n<2) { // Enter or Esc typed from primed text widget: treat as if dialog OK or cancel button hit.
+       int dlgNr; // figure out what the dialog number is by comparing shells (because we must pass it :( )
+       for(dlgNr=0; dlgNr<NrOfDialogs; dlgNr++) if(shellUp[dlgNr] && shells[dlgNr] == sh)
+           GenericCallback (w, (XtPointer)(intptr_t) (30000 + n + (dlgNr<<16)), NULL);
+    }
+}
+
+void
+HardSetFocus (Option *opt)
+{
+    XSetInputFocus(xDisplay, XtWindow(opt->handle), RevertToPointerRoot, CurrentTime);
+}
+
+