Updated copyright notice to 2011
[xboard.git] / xboard.c
index b99438e..7a8a7ac 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -2,10 +2,10 @@
  * xboard.c -- X front end for XBoard
  *
  * Copyright 1991 by Digital Equipment Corporation, Maynard,
- * Massachusetts. 
+ * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2008, 2009 Free Software Foundation, Inc.
+ * 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
  *
  * The following terms apply to Digital Equipment Corporation's copyright
  * interest in XBoard:
@@ -49,6 +49,8 @@
  *------------------------------------------------------------------------
  ** See the file ChangeLog for a revision history.  */
 
+#define HIGHDRAG 1
+
 #include "config.h"
 
 #include <stdio.h>
@@ -58,6 +60,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <pwd.h>
+#include <math.h>
 
 #if !OMIT_SOCKETS
 # if HAVE_SYS_SOCKET_H
@@ -189,6 +192,7 @@ extern char *getenv();
 
 #include "frontend.h"
 #include "backend.h"
+#include "backendz.h"
 #include "moves.h"
 #include "xboard.h"
 #include "childio.h"
@@ -200,7 +204,9 @@ extern char *getenv();
 // must be moved to xengineoutput.h
 
 void EngineOutputProc P((Widget w, XEvent *event,
- String *prms, Cardinal *nprms));
+                        String *prms, Cardinal *nprms));
+void EvalGraphProc P((Widget w, XEvent *event,
+                     String *prms, Cardinal *nprms));
 
 
 #ifdef __EMX__
@@ -220,21 +226,26 @@ void EngineOutputProc P((Widget w, XEvent *event,
 
 typedef struct {
     String string;
+    String ref;
     XtActionProc proc;
 } MenuItem;
 
 typedef struct {
     String name;
+    String ref;
     MenuItem *mi;
 } Menu;
 
 int main P((int argc, char **argv));
+FILE * XsraSelFile P((Widget w, char *prompt, char *ok, char *cancel, char *failed,
+               char *init_path, char *mode, int (*show_entry)(), char **name_return));
 RETSIGTYPE CmailSigHandler P((int sig));
 RETSIGTYPE IntSigHandler P((int sig));
 RETSIGTYPE TermSizeSigHandler P((int sig));
 void CreateGCs P((void));
 void CreateXIMPieces P((void));
 void CreateXPMPieces P((void));
+void CreateXPMBoard P((char *s, int n));
 void CreatePieces P((void));
 void CreatePieceMenus P((void));
 Widget CreateMenuBar P((Menu *mb));
@@ -254,6 +265,12 @@ void HandleUserMove P((Widget w, XEvent *event,
                     String *prms, Cardinal *nprms));
 void AnimateUserMove P((Widget w, XEvent * event,
                     String * params, Cardinal * nParams));
+void HandlePV P((Widget w, XEvent * event,
+                    String * params, Cardinal * nParams));
+void SelectPV P((Widget w, XEvent * event,
+                    String * params, Cardinal * nParams));
+void StopPV P((Widget w, XEvent * event,
+                    String * params, Cardinal * nParams));
 void WhiteClock P((Widget w, XEvent *event,
                   String *prms, Cardinal *nprms));
 void BlackClock P((Widget w, XEvent *event,
@@ -262,6 +279,8 @@ void DrawPositionProc P((Widget w, XEvent *event,
                     String *prms, Cardinal *nprms));
 void XDrawPosition P((Widget w, /*Boolean*/int repaint,
                     Board board));
+void CommentClick P((Widget w, XEvent * event,
+                  String * params, Cardinal * nParams));
 void CommentPopUp P((char *title, char *label));
 void CommentPopDown P((void));
 void CommentCallback P((Widget w, XtPointer client_data,
@@ -349,15 +368,19 @@ void AdjuWhiteProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void AdjuBlackProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void AdjuDrawProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void EnterKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void UpKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void DownKeyProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void StopObservingProc P((Widget w, XEvent *event, String *prms,
                          Cardinal *nprms));
 void StopExaminingProc P((Widget w, XEvent *event, String *prms,
                          Cardinal *nprms));
+void UploadProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void BackwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void ForwardProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void ToStartProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void ToEndProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void RevertProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void AnnotateProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void TruncateGameProc P((Widget w, XEvent *event, String *prms,
                         Cardinal *nprms));
 void RetractMoveProc P((Widget w, XEvent *event, String *prms,
@@ -387,10 +410,11 @@ void HighlightDraggingProc P((Widget w, XEvent *event, String *prms,
                              Cardinal *nprms));
 void HighlightLastMoveProc P((Widget w, XEvent *event, String *prms,
                              Cardinal *nprms));
+void HighlightArrowProc P((Widget w, XEvent *event, String *prms,
+                             Cardinal *nprms));
 void MoveSoundProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void IcsAlarmProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
-void OldSaveStyleProc P((Widget w, XEvent *event, String *prms,
-                        Cardinal *nprms));
+void OneClickProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void PeriodicUpdatesProc P((Widget w, XEvent *event, String *prms,
                         Cardinal *nprms));
 void PonderNextMoveProc P((Widget w, XEvent *event, String *prms,
@@ -438,6 +462,8 @@ void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms))
 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void FirstSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void SecondSettingsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void GameListOptionsPopUp P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void GameListOptionsPopDown P(());
 void ShufflePopDown P(());
 void EnginePopDown P(());
 void UciPopDown P(());
@@ -446,6 +472,10 @@ void NewVariantPopDown P(());
 void SettingsPopDown P(());
 void update_ics_width P(());
 int get_term_width P(());
+int CopyMemoProc P(());
+void DrawArrowHighlight P((int fromX, int fromY, int toX,int toY));
+Boolean IsDrawArrowEnabled P(());
+
 /*
 * XBoard depends on Xt R4 or higher
 */
@@ -467,6 +497,8 @@ Widget shellWidget, layoutWidget, formWidget, boardWidget, messageWidget,
   menuBarWidget, buttonBarWidget, editShell, errorShell, analysisShell,
   ICSInputShell, fileNameShell, askQuestionShell;
 Widget historyShell, evalGraphShell, gameListShell;
+int hOffset; // [HGM] dual
+XSegment secondSegments[BOARD_RANKS + BOARD_FILES + 2];
 XSegment gridSegments[BOARD_RANKS + BOARD_FILES + 2];
 XSegment jailGridSegments[BOARD_RANKS + BOARD_FILES + 6];
 Font clockFontID, coordFontID, countFontID;
@@ -520,7 +552,8 @@ Pixmap pieceBitmap2[2][(int)BlackPawn+4];       /* [HGM] pieces */
 Pixmap xpmPieceBitmap[4][(int)BlackPawn];      /* LL, LD, DL, DD actually used*/
 Pixmap xpmPieceBitmap2[4][(int)BlackPawn+4];   /* LL, LD, DL, DD set to select from */
 Pixmap xpmLightSquare, xpmDarkSquare, xpmJailSquare;
-int useImages, useImageSqs;
+Pixmap xpmBoardBitmap[2];
+int useImages, useImageSqs, useTexture, textureW[2], textureH[2];
 XImage *ximPieceBitmap[4][(int)BlackPawn+4];   /* LL, LD, DL, DD */
 Pixmap ximMaskPm[(int)BlackPawn];               /* clipmasks, used for XIM pieces */
 Pixmap ximMaskPm2[(int)BlackPawn+4];            /* clipmasks, used for XIM pieces */
@@ -564,185 +597,205 @@ static Pixmap xpmMask[BlackKing + 1];
 SizeDefaults sizeDefaults[] = SIZE_DEFAULTS;
 
 MenuItem fileMenu[] = {
-    {N_("New Game"), ResetProc},
-    {N_("New Shuffle Game ..."), ShuffleMenuProc},
-    {N_("New Variant ..."), NewVariantProc},      // [HGM] variant: not functional yet
-    {"----", NothingProc},
-    {N_("Load Game"), LoadGameProc},
-    {N_("Load Next Game"), LoadNextGameProc},
-    {N_("Load Previous Game"), LoadPrevGameProc},
-    {N_("Reload Same Game"), ReloadGameProc},
-    {N_("Save Game"), SaveGameProc},
-    {"----", NothingProc},
-    {N_("Copy Game"), CopyGameProc},
-    {N_("Paste Game"), PasteGameProc},
-    {"----", NothingProc},
-    {N_("Load Position"), LoadPositionProc},
-    {N_("Load Next Position"), LoadNextPositionProc},
-    {N_("Load Previous Position"), LoadPrevPositionProc},
-    {N_("Reload Same Position"), ReloadPositionProc},
-    {N_("Save Position"), SavePositionProc},
-    {"----", NothingProc},
-    {N_("Copy Position"), CopyPositionProc},
-    {N_("Paste Position"), PastePositionProc},
-    {"----", NothingProc},
-    {N_("Mail Move"), MailMoveProc},
-    {N_("Reload CMail Message"), ReloadCmailMsgProc},
-    {"----", NothingProc},
-    {N_("Exit"), QuitProc},
-    {NULL, NULL}
+    {N_("New Game        Ctrl+N"),        "New Game", ResetProc},
+    {N_("New Shuffle Game ..."),          "New Shuffle Game", ShuffleMenuProc},
+    {N_("New Variant ...   Alt+Shift+V"), "New Variant", NewVariantProc},      // [HGM] variant: not functional yet
+    {"----", NULL, NothingProc},
+    {N_("Load Game       Ctrl+O"),        "Load Game", LoadGameProc},
+    {N_("Load Position    Ctrl+Shift+O"), "Load Position", LoadPositionProc},
+//    {N_("Load Next Game"), "Load Next Game", LoadNextGameProc},
+//    {N_("Load Previous Game"), "Load Previous Game", LoadPrevGameProc},
+//    {N_("Reload Same Game"), "Reload Same Game", ReloadGameProc},
+    {N_("Next Position     Shift+PgDn"), "Load Next Position", LoadNextPositionProc},
+    {N_("Prev Position     Shift+PgUp"), "Load Previous Position", LoadPrevPositionProc},
+    {"----", NULL, NothingProc},
+//    {N_("Reload Same Position"), "Reload Same Position", ReloadPositionProc},
+    {N_("Save Game       Ctrl+S"),        "Save Game", SaveGameProc},
+    {N_("Save Position    Ctrl+Shift+S"), "Save Position", SavePositionProc},
+    {"----", NULL, NothingProc},
+    {N_("Mail Move"),            "Mail Move", MailMoveProc},
+    {N_("Reload CMail Message"), "Reload CMail Message", ReloadCmailMsgProc},
+    {"----", NULL, NothingProc},
+    {N_("Quit                 Ctr+Q"), "Exit", QuitProc},
+    {NULL, NULL, NULL}
+};
+
+MenuItem editMenu[] = {
+    {N_("Copy Game    Ctrl+C"),        "Copy Game", CopyGameProc},
+    {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
+    {"----", NULL, NothingProc},
+    {N_("Paste Game    Ctrl+V"),        "Paste Game", PasteGameProc},
+    {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
+    {"----", NULL, NothingProc},
+    {N_("Edit Game      Ctrl+E"),        "Edit Game", EditGameProc},
+    {N_("Edit Position   Ctrl+Shift+E"), "Edit Position", EditPositionProc},
+    {N_("Edit Tags"),                    "Edit Tags", EditTagsProc},
+    {N_("Edit Comment"),                 "Edit Comment", EditCommentProc},
+    {"----", NULL, NothingProc},
+    {N_("Revert              Home"), "Revert", RevertProc},
+    {N_("Annotate"),                 "Annotate", AnnotateProc},
+    {N_("Truncate Game  End"),       "Truncate Game", TruncateGameProc},
+    {"----", NULL, NothingProc},
+    {N_("Backward         Alt+Left"),   "Backward", BackwardProc},
+    {N_("Forward           Alt+Right"), "Forward", ForwardProc},
+    {N_("Back to Start     Alt+Home"),  "Back to Start", ToStartProc},
+    {N_("Forward to End Alt+End"),      "Forward to End", ToEndProc},
+    {NULL, NULL, NULL}
+};
+
+MenuItem viewMenu[] = {
+    {N_("Flip View             F2"),         "Flip View", FlipViewProc},
+    {"----", NULL, NothingProc},
+    {N_("Engine Output      Alt+Shift+O"),   "Show Engine Output", EngineOutputProc},
+    {N_("Move History       Alt+Shift+H"),   "Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
+    {N_("Evaluation Graph  Alt+Shift+E"),    "Show Evaluation Graph", EvalGraphProc},
+    {N_("Game List            Alt+Shift+G"), "Show Game List", ShowGameListProc},
+    {"----", NULL, NothingProc},
+    {N_("Tags"),             "Show Tags", EditTagsProc},
+    {N_("Comments"),         "Show Comments", EditCommentProc},
+    {N_("ICS Input Box"),    "ICS Input Box", IcsInputBoxProc},
+    {NULL, NULL, NULL}
 };
 
 MenuItem modeMenu[] = {
-    {N_("Machine White"), MachineWhiteProc},
-    {N_("Machine Black"), MachineBlackProc},
-    {N_("Two Machines"), TwoMachinesProc},
-    {N_("Analysis Mode"), AnalyzeModeProc},
-    {N_("Analyze File"), AnalyzeFileProc },
-    {N_("ICS Client"), IcsClientProc},
-    {N_("Edit Game"), EditGameProc},
-    {N_("Edit Position"), EditPositionProc},
-    {N_("Training"), TrainingProc},
-    {"----", NothingProc},
-    {N_("Show Engine Output"), EngineOutputProc},
-    {N_("Show Evaluation Graph"), NothingProc}, // [HGM] evalgr: not functional yet
-    {N_("Show Game List"), ShowGameListProc},
-    {"Show Move History", HistoryShowProc}, // [HGM] hist: activate 4.2.7 code
-    {"----", NothingProc},
-    {N_("Edit Tags"), EditTagsProc},
-    {N_("Edit Comment"), EditCommentProc},
-    {N_("ICS Input Box"), IcsInputBoxProc},
-    {N_("Pause"), PauseProc},
-    {NULL, NULL}
+    {N_("Machine White  Ctrl+W"), "Machine White", MachineWhiteProc},
+    {N_("Machine Black  Ctrl+B"), "Machine Black", MachineBlackProc},
+    {N_("Two Machines   Ctrl+T"), "Two Machines", TwoMachinesProc},
+    {N_("Analysis Mode  Ctrl+A"), "Analysis Mode", AnalyzeModeProc},
+    {N_("Analyze File      Ctrl+F"), "Analyze File", AnalyzeFileProc },
+    {N_("Edit Game         Ctrl+E"), "Edit Game", EditGameProc},
+    {N_("Edit Position      Ctrl+Shift+E"), "Edit Position", EditPositionProc},
+    {N_("Training"),      "Training", TrainingProc},
+    {N_("ICS Client"),    "ICS Client", IcsClientProc},
+    {"----", NULL, NothingProc},
+    {N_("Pause               Pause"),         "Pause", PauseProc},
+    {NULL, NULL, NULL}
 };
 
 MenuItem actionMenu[] = {
-    {N_("Accept"), AcceptProc},
-    {N_("Decline"), DeclineProc},
-    {N_("Rematch"), RematchProc},
-    {"----", NothingProc},
-    {N_("Call Flag"), CallFlagProc},
-    {N_("Draw"), DrawProc},
-    {N_("Adjourn"), AdjournProc},
-    {N_("Abort"), AbortProc},
-    {N_("Resign"), ResignProc},
-    {"----", NothingProc},
-    {N_("Stop Observing"), StopObservingProc},
-    {N_("Stop Examining"), StopExaminingProc},
-    {"----", NothingProc},
-    {N_("Adjudicate to White"), AdjuWhiteProc},
-    {N_("Adjudicate to Black"), AdjuBlackProc},
-    {N_("Adjudicate Draw"), AdjuDrawProc},
-    {NULL, NULL}
+    {N_("Accept             F3"), "Accept", AcceptProc},
+    {N_("Decline            F4"), "Decline", DeclineProc},
+    {N_("Rematch           F12"), "Rematch", RematchProc},
+    {"----", NULL, NothingProc},
+    {N_("Call Flag          F5"), "Call Flag", CallFlagProc},
+    {N_("Draw                F6"), "Draw", DrawProc},
+    {N_("Adjourn            F7"),  "Adjourn", AdjournProc},
+    {N_("Abort                F8"),"Abort", AbortProc},
+    {N_("Resign              F9"), "Resign", ResignProc},
+    {"----", NULL, NothingProc},
+    {N_("Stop Observing  F10"), "Stop Observing", StopObservingProc},
+    {N_("Stop Examining  F11"), "Stop Examining", StopExaminingProc},
+    {N_("Upload to Examine"),   "Upload to Examine", UploadProc},
+    {"----", NULL, NothingProc},
+    {N_("Adjudicate to White"), "Adjudicate to White", AdjuWhiteProc},
+    {N_("Adjudicate to Black"), "Adjudicate to Black", AdjuBlackProc},
+    {N_("Adjudicate Draw"),     "Adjudicate Draw", AdjuDrawProc},
+    {NULL, NULL, NULL}
 };
 
-MenuItem stepMenu[] = {
-    {N_("Backward"), BackwardProc},
-    {N_("Forward"), ForwardProc},
-    {N_("Back to Start"), ToStartProc},
-    {N_("Forward to End"), ToEndProc},
-    {N_("Revert"), RevertProc},
-    {N_("Truncate Game"), TruncateGameProc},
-    {"----", NothingProc},
-    {N_("Move Now"), MoveNowProc},
-    {N_("Retract Move"), RetractMoveProc},
-    {NULL, NULL}
+MenuItem engineMenu[] = {
+    {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
+    {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
+    {"----", NULL, NothingProc},
+    {N_("Hint"), "Hint", HintProc},
+    {N_("Book"), "Book", BookProc},
+    {"----", NULL, NothingProc},
+    {N_("Move Now     Ctrl+M"),     "Move Now", MoveNowProc},
+    {N_("Retract Move  Ctrl+X"), "Retract Move", RetractMoveProc},
+    {NULL, NULL, NULL}
 };
 
 MenuItem optionsMenu[] = {
-    {N_("Flip View"), FlipViewProc},
-    {"----", NothingProc},
-    {N_("Adjudications ..."), EngineMenuProc},
-    {N_("General Settings ..."), UciMenuProc},
-    {N_("Engine #1 Settings ..."), FirstSettingsProc},
-    {N_("Engine #2 Settings ..."), SecondSettingsProc},
-    {N_("Time Control ..."), TimeControlProc},
-    {"----", NothingProc},
-    {N_("Always Queen"), AlwaysQueenProc},
-    {N_("Animate Dragging"), AnimateDraggingProc},
-    {N_("Animate Moving"), AnimateMovingProc},
-    {N_("Auto Comment"), AutocommProc},
-    {N_("Auto Flag"), AutoflagProc},
-    {N_("Auto Flip View"), AutoflipProc},
-    {N_("Auto Observe"), AutobsProc},
-    {N_("Auto Raise Board"), AutoraiseProc},
-    {N_("Auto Save"), AutosaveProc},
-    {N_("Blindfold"), BlindfoldProc},
-    {N_("Flash Moves"), FlashMovesProc},
-    {N_("Get Move List"), GetMoveListProc},
+    {N_("Time Control ...       Alt+Shift+T"), "Time Control", TimeControlProc},
+    {N_("Common Engine ...  Alt+Shift+U"),     "Common Engine", UciMenuProc},
+    {N_("Adjudications ...      Alt+Shift+J"), "Adjudications", EngineMenuProc},
+    {N_("Game List ..."),    "Game List", GameListOptionsPopUp},
+    {"----", NULL, NothingProc},
+    {N_("Always Queen        Ctrl+Shift+Q"),   "Always Queen", AlwaysQueenProc},
+    {N_("Animate Dragging"), "Animate Dragging", AnimateDraggingProc},
+    {N_("Animate Moving      Ctrl+Shift+A"),   "Animate Moving", AnimateMovingProc},
+    {N_("Auto Comment"),     "Auto Comment", AutocommProc},
+    {N_("Auto Flag               Ctrl+Shift+F"), "Auto Flag", AutoflagProc},
+    {N_("Auto Flip View"),   "Auto Flip View", AutoflipProc},
+    {N_("Auto Observe"),     "Auto Observe", AutobsProc},
+    {N_("Auto Raise Board"), "Auto Raise Board", AutoraiseProc},
+    {N_("Auto Save"),        "Auto Save", AutosaveProc},
+    {N_("Blindfold"),        "Blindfold", BlindfoldProc},
+    {N_("Flash Moves"),      "Flash Moves", FlashMovesProc},
+    {N_("Get Move List"),    "Get Move List", GetMoveListProc},
 #if HIGHDRAG
-    {N_("Highlight Dragging"), HighlightDraggingProc},
+    {N_("Highlight Dragging"),    "Highlight Dragging", HighlightDraggingProc},
 #endif
-    {N_("Highlight Last Move"), HighlightLastMoveProc},
-    {N_("Move Sound"), MoveSoundProc},
-    {N_("ICS Alarm"), IcsAlarmProc},
-    {N_("Old Save Style"), OldSaveStyleProc},
-    {N_("Periodic Updates"), PeriodicUpdatesProc},
-    {N_("Ponder Next Move"), PonderNextMoveProc},
-    {N_("Popup Exit Message"), PopupExitMessageProc},
-    {N_("Popup Move Errors"), PopupMoveErrorsProc},
-    {N_("Premove"), PremoveProc},
-    {N_("Quiet Play"), QuietPlayProc},
-    {N_("Show Coords"), ShowCoordsProc},
-    {N_("Hide Thinking"), HideThinkingProc},
-    {N_("Test Legality"), TestLegalityProc},
-    {"----", NothingProc},
-    {N_("Save Settings Now"), SaveSettingsProc},
-    {N_("Save Settings on Exit"), SaveOnExitProc},
-    {NULL, NULL}
+    {N_("Highlight Last Move"),   "Highlight Last Move", HighlightLastMoveProc},
+    {N_("Highlight With Arrow"),  "Arrow", HighlightArrowProc},
+    {N_("Move Sound"),            "Move Sound", MoveSoundProc},
+    {N_("ICS Alarm"),             "ICS Alarm", IcsAlarmProc},
+    {N_("One-Click Moving"),      "OneClick", OneClickProc},
+    {N_("Periodic Updates"),      "Periodic Updates", PeriodicUpdatesProc},
+    {N_("Ponder Next Move  Ctrl+Shift+P"), "Ponder Next Move", PonderNextMoveProc},
+    {N_("Popup Exit Message"),    "Popup Exit Message", PopupExitMessageProc},
+    {N_("Popup Move Errors"),     "Popup Move Errors", PopupMoveErrorsProc},
+    {N_("Premove"),               "Premove", PremoveProc},
+    {N_("Quiet Play"),            "Quiet Play", QuietPlayProc},
+    {N_("Show Coords"),           "Show Coords", ShowCoordsProc},
+    {N_("Hide Thinking        Ctrl+Shift+H"),   "Hide Thinking", HideThinkingProc},
+    {N_("Test Legality          Ctrl+Shift+L"), "Test Legality", TestLegalityProc},
+    {"----", NULL, NothingProc},
+    {N_("Save Settings Now"),     "Save Settings Now", SaveSettingsProc},
+    {N_("Save Settings on Exit"), "Save Settings on Exit", SaveOnExitProc},
+    {NULL, NULL, NULL}
 };
 
 MenuItem helpMenu[] = {
-    {N_("Info XBoard"), InfoProc},
-    {N_("Man XBoard"), ManProc},
-    {"----", NothingProc},
-    {N_("Hint"), HintProc},
-    {N_("Book"), BookProc},
-    {"----", NothingProc},
-    {N_("About XBoard"), AboutProc},
-    {NULL, NULL}
+    {N_("Info XBoard"),     "Info XBoard", InfoProc},
+    {N_("Man XBoard   F1"), "Man XBoard", ManProc},
+    {"----", NULL, NothingProc},
+    {N_("About XBoard"), "About XBoard", AboutProc},
+    {NULL, NULL, NULL}
 };
 
 Menu menuBar[] = {
-    {N_("File"), fileMenu},
-    {N_("Mode"), modeMenu},
-    {N_("Action"), actionMenu},
-    {N_("Step"), stepMenu},
-    {N_("Options"), optionsMenu},
-    {N_("Help"), helpMenu},
-    {NULL, NULL}
+    {N_("File"),    "File", fileMenu},
+    {N_("Edit"),    "Edit", editMenu},
+    {N_("View"),    "View", viewMenu},
+    {N_("Mode"),    "Mode", modeMenu},
+    {N_("Action"),  "Action", actionMenu},
+    {N_("Engine"),  "Engine", engineMenu},
+    {N_("Options"), "Options", optionsMenu},
+    {N_("Help"),    "Help", helpMenu},
+    {NULL, NULL, NULL}
 };
 
-#define PAUSE_BUTTON N_("P")
+#define PAUSE_BUTTON "P"
 MenuItem buttonBar[] = {
-    {"<<", ToStartProc},
-    {"<", BackwardProc},
-    {PAUSE_BUTTON, PauseProc},
-    {">", ForwardProc},
-    {">>", ToEndProc},
-    {NULL, NULL}
+    {"<<", "<<", ToStartProc},
+    {"<", "<", BackwardProc},
+    {PAUSE_BUTTON, PAUSE_BUTTON, PauseProc},
+    {">", ">", ForwardProc},
+    {">>", ">>", ToEndProc},
+    {NULL, NULL, NULL}
 };
 
 #define PIECE_MENU_SIZE 18
 String pieceMenuStrings[2][PIECE_MENU_SIZE] = {
     { N_("White"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
-      N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
-      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
+      N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
+      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
       N_("Empty square"), N_("Clear board") },
     { N_("Black"), "----", N_("Pawn"), N_("Knight"), N_("Bishop"), N_("Rook"),
-      N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"), 
-      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"), 
+      N_("Queen"), N_("King"), "----", N_("Elephant"), N_("Cannon"),
+      N_("Archbishop"), N_("Chancellor"), "----", N_("Promote"), N_("Demote"),
       N_("Empty square"), N_("Clear board") }
 };
 /* must be in same order as PieceMenuStrings! */
 ChessSquare pieceMenuTranslation[2][PIECE_MENU_SIZE] = {
     { WhitePlay, (ChessSquare) 0, WhitePawn, WhiteKnight, WhiteBishop,
        WhiteRook, WhiteQueen, WhiteKing, (ChessSquare) 0, WhiteAlfil,
-       WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0, 
+       WhiteCannon, WhiteAngel, WhiteMarshall, (ChessSquare) 0,
        PromotePiece, DemotePiece, EmptySquare, ClearBoard },
     { BlackPlay, (ChessSquare) 0, BlackPawn, BlackKnight, BlackBishop,
        BlackRook, BlackQueen, BlackKing, (ChessSquare) 0, BlackAlfil,
-       BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0, 
+       BlackCannon, BlackAngel, BlackMarshall, (ChessSquare) 0,
        PromotePiece, DemotePiece, EmptySquare, ClearBoard },
 };
 
@@ -829,6 +882,9 @@ XtActionsRec boardActions[] = {
     { "DrawPosition", DrawPositionProc },
     { "HandleUserMove", HandleUserMove },
     { "AnimateUserMove", AnimateUserMove },
+    { "HandlePV", HandlePV },
+    { "SelectPV", SelectPV },
+    { "StopPV", StopPV },
     { "FileNameAction", FileNameAction },
     { "AskQuestionProc", AskQuestionProc },
     { "AskQuestionReplyAction", AskQuestionReplyAction },
@@ -837,10 +893,12 @@ XtActionsRec boardActions[] = {
     { "BlackClock", BlackClock },
     { "Iconify", Iconify },
     { "ResetProc", ResetProc },
+    { "NewVariantProc", NewVariantProc },
     { "LoadGameProc", LoadGameProc },
     { "LoadNextGameProc", LoadNextGameProc },
     { "LoadPrevGameProc", LoadPrevGameProc },
     { "LoadSelectedProc", LoadSelectedProc },
+    { "SetFilterProc", SetFilterProc },
     { "ReloadGameProc", ReloadGameProc },
     { "LoadPositionProc", LoadPositionProc },
     { "LoadNextPositionProc", LoadNextPositionProc },
@@ -865,6 +923,7 @@ XtActionsRec boardActions[] = {
     { "EditPositionProc", EditPositionProc },
     { "TrainingProc", EditPositionProc },
     { "EngineOutputProc", EngineOutputProc}, // [HGM] Winboard_x engine-output window
+    { "EvalGraphProc", EvalGraphProc},       // [HGM] Winboard_x avaluation graph window
     { "ShowGameListProc", ShowGameListProc },
     { "ShowMoveListProc", HistoryShowProc},
     { "EditTagsProc", EditCommentProc },
@@ -884,16 +943,23 @@ XtActionsRec boardActions[] = {
     { "AdjuBlackProc", AdjuBlackProc },
     { "AdjuDrawProc", AdjuDrawProc },
     { "EnterKeyProc", EnterKeyProc },
+    { "UpKeyProc", UpKeyProc },
+    { "DownKeyProc", DownKeyProc },
     { "StopObservingProc", StopObservingProc },
     { "StopExaminingProc", StopExaminingProc },
+    { "UploadProc", UploadProc },
     { "BackwardProc", BackwardProc },
     { "ForwardProc", ForwardProc },
     { "ToStartProc", ToStartProc },
     { "ToEndProc", ToEndProc },
     { "RevertProc", RevertProc },
+    { "AnnotateProc", AnnotateProc },
     { "TruncateGameProc", TruncateGameProc },
     { "MoveNowProc", MoveNowProc },
     { "RetractMoveProc", RetractMoveProc },
+    { "EngineMenuProc", (XtActionProc) EngineMenuProc },
+    { "UciMenuProc", (XtActionProc) UciMenuProc },
+    { "TimeControlProc", (XtActionProc) TimeControlProc },
     { "AlwaysQueenProc", AlwaysQueenProc },
     { "AnimateDraggingProc", AnimateDraggingProc },
     { "AnimateMovingProc", AnimateMovingProc },
@@ -912,7 +978,6 @@ XtActionsRec boardActions[] = {
     { "HighlightLastMoveProc", HighlightLastMoveProc },
     { "IcsAlarmProc", IcsAlarmProc },
     { "MoveSoundProc", MoveSoundProc },
-    { "OldSaveStyleProc", OldSaveStyleProc },
     { "PeriodicUpdatesProc", PeriodicUpdatesProc },
     { "PonderNextMoveProc", PonderNextMoveProc },
     { "PopupExitMessageProc", PopupExitMessageProc },
@@ -933,6 +998,7 @@ XtActionsRec boardActions[] = {
     { "AboutProc", AboutProc },
     { "DebugProc", DebugProc },
     { "NothingProc", NothingProc },
+    { "CommentClick", (XtActionProc) CommentClick },
     { "CommentPopDown", (XtActionProc) CommentPopDown },
     { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
     { "TagsPopDown", (XtActionProc) TagsPopDown },
@@ -941,47 +1007,93 @@ XtActionsRec boardActions[] = {
     { "FileNamePopDown", (XtActionProc) FileNamePopDown },
     { "AskQuestionPopDown", (XtActionProc) AskQuestionPopDown },
     { "GameListPopDown", (XtActionProc) GameListPopDown },
+    { "GameListOptionsPopDown", (XtActionProc) GameListOptionsPopDown },
     { "PromotionPopDown", (XtActionProc) PromotionPopDown },
     { "HistoryPopDown", (XtActionProc) HistoryPopDown },
     { "EngineOutputPopDown", (XtActionProc) EngineOutputPopDown },
+    { "EvalGraphPopDown", (XtActionProc) EvalGraphPopDown },
     { "ShufflePopDown", (XtActionProc) ShufflePopDown },
     { "EnginePopDown", (XtActionProc) EnginePopDown },
     { "UciPopDown", (XtActionProc) UciPopDown },
     { "TimeControlPopDown", (XtActionProc) TimeControlPopDown },
     { "NewVariantPopDown", (XtActionProc) NewVariantPopDown },
     { "SettingsPopDown", (XtActionProc) SettingsPopDown },
+    { "CopyMemoProc", (XtActionProc) CopyMemoProc },
 };
 
 char globalTranslations[] =
-  ":<Key>R: ResignProc() \n \
-   :<Key>r: ResetProc() \n \
-   :<Key>g: LoadGameProc() \n \
-   :<Key>N: LoadNextGameProc() \n \
-   :<Key>P: LoadPrevGameProc() \n \
-   :<Key>Q: QuitProc() \n \
-   :<Key>F: ToEndProc() \n \
-   :<Key>f: ForwardProc() \n \
-   :<Key>B: ToStartProc() \n \
-   :<Key>b: BackwardProc() \n \
-   :<Key>p: PauseProc() \n \
-   :<Key>d: DrawProc() \n \
-   :<Key>t: CallFlagProc() \n \
-   :<Key>i: Iconify() \n \
-   :<Key>c: Iconify() \n \
-   :<Key>v: FlipViewProc() \n \
-   <KeyDown>Control_L: BackwardProc() \n \
-   <KeyUp>Control_L: ForwardProc() \n \
-   <KeyDown>Control_R: BackwardProc() \n \
-   <KeyUp>Control_R: ForwardProc() \n \
+  ":<Key>F9: ResignProc() \n \
+   :Ctrl<Key>n: ResetProc() \n \
+   :Meta<Key>V: NewVariantProc() \n \
+   :Ctrl<Key>o: LoadGameProc() \n \
+   :Meta<Key>Next: LoadNextGameProc() \n \
+   :Meta<Key>Prior: LoadPrevGameProc() \n \
+   :Ctrl<Key>s: SaveGameProc() \n \
+   :Ctrl<Key>c: CopyGameProc() \n \
+   :Ctrl<Key>v: PasteGameProc() \n \
+   :Ctrl<Key>O: LoadPositionProc() \n \
+   :Shift<Key>Next: LoadNextPositionProc() \n \
+   :Shift<Key>Prior: LoadPrevPositionProc() \n \
+   :Ctrl<Key>S: SavePositionProc() \n \
+   :Ctrl<Key>C: CopyPositionProc() \n \
+   :Ctrl<Key>V: PastePositionProc() \n \
+   :Ctrl<Key>q: QuitProc() \n \
+   :Ctrl<Key>w: MachineWhiteProc() \n \
+   :Ctrl<Key>b: MachineBlackProc() \n \
+   :Ctrl<Key>t: TwoMachinesProc() \n \
+   :Ctrl<Key>a: AnalysisModeProc() \n \
+   :Ctrl<Key>f: AnalyzeFileProc() \n \
+   :Ctrl<Key>e: EditGameProc() \n \
+   :Ctrl<Key>E: EditPositionProc() \n \
+   :Meta<Key>O: EngineOutputProc() \n \
+   :Meta<Key>E: EvalGraphProc() \n \
+   :Meta<Key>G: ShowGameListProc() \n \
+   :Meta<Key>H: ShowMoveListProc() \n \
+   :<Key>Pause: PauseProc() \n \
+   :<Key>F3: AcceptProc() \n \
+   :<Key>F4: DeclineProc() \n \
+   :<Key>F12: RematchProc() \n \
+   :<Key>F5: CallFlagProc() \n \
+   :<Key>F6: DrawProc() \n \
+   :<Key>F7: AdjournProc() \n \
+   :<Key>F8: AbortProc() \n \
+   :<Key>F10: StopObservingProc() \n \
+   :<Key>F11: StopExaminingProc() \n \
+   :Meta Ctrl<Key>F12: DebugProc() \n \
+   :Meta<Key>End: ToEndProc() \n \
+   :Meta<Key>Right: ForwardProc() \n \
+   :Meta<Key>Home: ToStartProc() \n \
+   :Meta<Key>Left: BackwardProc() \n \
+   :<Key>Home: RevertProc() \n \
+   :<Key>End: TruncateGameProc() \n \
+   :Ctrl<Key>m: MoveNowProc() \n \
+   :Ctrl<Key>x: RetractMoveProc() \n \
+   :Meta<Key>J: EngineMenuProc() \n \
+   :Meta<Key>U: UciMenuProc() \n \
+   :Meta<Key>T: TimeControlProc() \n \
+   :Ctrl<Key>Q: AlwaysQueenProc() \n \
+   :Ctrl<Key>F: AutoflagProc() \n \
+   :Ctrl<Key>A: AnimateMovingProc() \n \
+   :Ctrl<Key>P: PonderNextMoveProc() \n \
+   :Ctrl<Key>L: TestLegalityProc() \n \
+   :Ctrl<Key>H: HideThinkingProc() \n \
+   :<Key>-: Iconify() \n \
+   :<Key>F1: ManProc() \n \
+   :<Key>F2: FlipViewProc() \n \
+   <KeyDown>.: BackwardProc() \n \
+   <KeyUp>.: ForwardProc() \n \
    Shift<Key>1: AskQuestionProc(\"Direct command\",\
                                 \"Send to chess program:\",,1) \n \
    Shift<Key>2: AskQuestionProc(\"Direct command\",\
                                 \"Send to second chess program:\",,2) \n";
 
 char boardTranslations[] =
-   "<Btn1Down>: HandleUserMove() \n \
-   <Btn1Up>: HandleUserMove() \n \
+   "<Btn1Down>: HandleUserMove(0) \n \
+   Shift<Btn1Up>: HandleUserMove(1) \n \
+   <Btn1Up>: HandleUserMove(0) \n \
    <Btn1Motion>: AnimateUserMove() \n \
+   <Btn3Motion>: HandlePV() \n \
+   <Btn3Up>: PieceMenuPopup(menuB) \n \
    Shift<Btn2Down>: XawPositionSimpleMenu(menuB) XawPositionSimpleMenu(menuD)\
                  PieceMenuPopup(menuB) \n \
    Any<Btn2Down>: XawPositionSimpleMenu(menuW) XawPositionSimpleMenu(menuD) \
@@ -995,8 +1107,14 @@ char whiteTranslations[] = "<BtnDown>: WhiteClock()\n";
 char blackTranslations[] = "<BtnDown>: BlackClock()\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[] = {
     "*fileName*value.translations: #override\\n <Key>Return: FileNameAction()",
     "*question*value.translations: #override\\n <Key>Return: AskQuestionReplyAction()",
@@ -1245,12 +1363,13 @@ BoardToTop()
 #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 " "
 
 // these two must some day move to frontend.h, when they are implemented
-Boolean EvalGraphIsUp();
-Boolean MoveHistoryIsUp();
 Boolean GameListIsUp();
 
 // The option definition and parsing code common to XBoard and WinBoard is collected in this file
@@ -1259,28 +1378,46 @@ Boolean GameListIsUp();
 // 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.whitePieceColor,
+  &appData.blackPieceColor,
   &appData.lightSquareColor,
-  &appData.darkSquareColor, 
+  &appData.darkSquareColor,
   &appData.highlightSquareColor,
   &appData.premoveHighlightColor,
+  &appData.lowTimeWarningColor,
   NULL,
   NULL,
   NULL,
   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 fontSet[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);
@@ -1294,6 +1431,7 @@ ParseFont(char *name, int number)
     default:
       return;
   }
+  fontSet[number] = True; // [HGM] font: indicate a font was specified (not from settings file)
 }
 
 void
@@ -1317,7 +1455,7 @@ ParseColor(int n, char *name)
 
 void
 ParseTextAttribs(ColorClass cc, char *s)
-{   
+{
     (&appData.colorShout)[cc] = strdup(s);
 }
 
@@ -1342,7 +1480,8 @@ void
 SaveFontArg(FILE *f, ArgDescriptor *ad)
 {
   char *name;
-  switch((int)ad->argLoc) {
+  int i, n = (int)ad->argLoc;
+  switch(n) {
     case 0: // CLOCK_FONT
        name = appData.clockFont;
       break;
@@ -1355,7 +1494,14 @@ SaveFontArg(FILE *f, ArgDescriptor *ad)
     default:
       return;
   }
-  fprintf(f, "/%s=%s\n", ad->argName, name);
+  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
@@ -1366,20 +1512,20 @@ ExportSounds()
 void
 SaveAttribsArg(FILE *f, ArgDescriptor *ad)
 {      // here the "argLoc" defines a table index. It could have contained the 'ta' pointer itself, though
-       fprintf(f, "/%s=%s\n", ad->argName, (&appData.colorShout)[(int)ad->argLoc]);
+       fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, (&appData.colorShout)[(int)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)ad->argLoc])
-       fprintf(f, "/%s=%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
+       fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", ad->argName, *(char**)colorVariable[(int)ad->argLoc]);
 }
 
 void
 SaveBoardSize(FILE *f, char *name, void *addr)
 { // wrapper to shield back-end from BoardSize & sizeInfo
-  fprintf(f, "/%s=%s\n", name, appData.boardSize);
+  fprintf(f, OPTCHAR "%s" SEPCHAR "%s\n", name, appData.boardSize);
 }
 
 void
@@ -1398,7 +1544,7 @@ GetActualPlacement(Widget wg, WindowPlacement *wp)
   int i;
 
   if(!wg) return;
-  
+
     i = 0;
     XtSetArg(args[i], XtNx, &x); i++;
     XtSetArg(args[i], XtNy, &y); i++;
@@ -1418,15 +1564,12 @@ GetWindowCoords()
   GetActualPlacement(shellWidget, &wpMain);
   if(EngineOutputIsUp()) GetActualPlacement(engineOutputShell, &wpEngineOutput); else
   if(MoveHistoryIsUp()) GetActualPlacement(historyShell, &wpMoveHistory);
+  if(EvalGraphIsUp()) GetActualPlacement(evalGraphShell, &wpEvalGraph);
   if(GameListIsUp()) GetActualPlacement(gameListShell, &wpGameList);
   if(commentShell) GetActualPlacement(commentShell, &wpComment);
   else             GetActualPlacement(editShell,    &wpComment);
   if(tagsShell) GetActualPlacement(tagsShell, &wpTags);
   else      GetActualPlacement(editTagsShell, &wpTags);
-#if 0
-  GetActualPlacement(hwndConsole, &wpConsole);
-  GetActualPlacement(evalGraphDialog, &wpEvalGraph);
-#endif
 }
 
 void
@@ -1438,7 +1581,9 @@ int
 MySearchPath(char *installDir, char *name, char *fullname)
 { // just append installDir and name. Perhaps ExpandPath should be used here?
   name = ExpandPathName(name);
-  if(name && name[0] == '/') strcpy(fullname, name); else {
+  if(name && name[0] == '/')
+    safeStrCpy(fullname, name, MSG_SIZ );
+  else {
     sprintf(fullname, "%s%c%s", installDir, '/', name);
   }
   return 1;
@@ -1448,7 +1593,7 @@ int
 MyGetFullPathName(char *name, char *fullname)
 { // should use ExpandPath?
   name = ExpandPathName(name);
-  strcpy(fullname, name);
+  safeStrCpy(fullname, name, MSG_SIZ );
   return 1;
 }
 
@@ -1458,12 +1603,6 @@ EnsureOnScreen(int *x, int *y, int minX, int minY)
   return;
 }
 
-Boolean
-EvalGraphIsUp()
-{
-  return False;
-}
-
 int
 MainWindowUp()
 { // [HGM] args: allows testing if main window is realized from back-end
@@ -1474,6 +1613,7 @@ void
 PopUpStartupDialog()
 {  // start menu not implemented in XBoard
 }
+
 char *
 ConvertToLine(int argc, char **argv)
 {
@@ -1481,19 +1621,24 @@ ConvertToLine(int argc, char **argv)
   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] != '{' )
-         sprintf(buf, "{%s} ", argv[i]);
-    else sprintf(buf, "%s ", argv[i]);
-    strcat(line, buf);
-  }
-    line[strlen(line)-1] = NULLCHAR;
+  for(i=1; i<argc; i++)
+    {
+      if( (strchr(argv[i], ' ') || strchr(argv[i], '\n') ||strchr(argv[i], '\t') )
+         && 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;
 }
 
 //--------------------------------------------------------------------------------------------
 
+extern Boolean twoBoards, partnerUp;
+
 #ifdef IDSIZES
   // eventually, all layout determining code should go into a subroutine, but until then IDSIZE remains undefined
 #else
@@ -1514,7 +1659,7 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
     shellArgs[1].value = (XtArgVal) &h;
     XtGetValues(shellWidget, shellArgs, 2);
 
-    shellArgs[4].value = 2*w; shellArgs[2].value = 10;
+    shellArgs[4].value = 3*w; shellArgs[2].value = 10;
     shellArgs[5].value = 2*h; shellArgs[3].value = 10;
     XtSetValues(shellWidget, &shellArgs[2], 4);
 
@@ -1524,6 +1669,12 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
     boardHeight = lineGap + BOARD_HEIGHT * (squareSize + lineGap);
     CreateGrid();
+    hOffset = boardWidth + 10;
+    for(i=0; i<BOARD_WIDTH+BOARD_HEIGHT+2; i++) { // [HGM] dual: grid for second board
+       secondSegments[i] = gridSegments[i];
+       secondSegments[i].x1 += hOffset;
+       secondSegments[i].x2 += hOffset;
+    }
 
     XtSetArg(args[0], XtNwidth, boardWidth);
     XtSetArg(args[1], XtNheight, boardHeight);
@@ -1562,7 +1713,7 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
     /*
      * Inhibit shell resizing.
      */
-    shellArgs[0].value = w = (XtArgVal) boardWidth + marginW;
+    shellArgs[0].value = w = (XtArgVal) boardWidth + marginW + twoBoards*hOffset; // [HGM] dual
     shellArgs[1].value = h = (XtArgVal) boardHeight + marginH;
     shellArgs[4].value = shellArgs[2].value = w;
     shellArgs[5].value = shellArgs[3].value = h;
@@ -1587,6 +1738,10 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
           xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteSilver];
        }
 #endif
+       if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
+          xpmPieceBitmap[i][(int)WhiteAngel]    = xpmPieceBitmap2[i][(int)WhiteFalcon];
+          xpmPieceBitmap[i][(int)WhiteMarshall] = xpmPieceBitmap2[i][(int)WhiteAlfil];
+       }
 #if !HAVE_LIBXPM
        // [HGM] why are thee ximMasks used at all? the ximPieceBitmaps seem to be never used!
        for(p=0; p<=(int)WhiteKing; p++)
@@ -1603,6 +1758,10 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
            ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
        }
 #endif
+       if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
+           ximMaskPm[(int)WhiteAngel]    = ximMaskPm2[(int)WhiteFalcon];
+           ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteAlfil];
+       }
 #endif
       }
     } else {
@@ -1622,6 +1781,10 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
           pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteSilver];
        }
 #endif
+       if(gameInfo.variant == VariantSChess && (squareSize == 49 || squareSize == 72)) {
+          pieceBitmap[i][(int)WhiteAngel]    = pieceBitmap2[i][(int)WhiteFalcon];
+          pieceBitmap[i][(int)WhiteMarshall] = pieceBitmap2[i][(int)WhiteAlfil];
+       }
       }
     }
 #if HAVE_LIBXPM
@@ -1630,19 +1793,6 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
 }
 #endif
 
-void EscapeExpand(char *p, char *q)
-{      // [HGM] initstring: routine to shape up string arguments
-       while(*p++ = *q++) if(p[-1] == '\\')
-           switch(*q++) {
-               case 'n': p[-1] = '\n'; break;
-               case 'r': p[-1] = '\r'; break;
-               case 't': p[-1] = '\t'; break;
-               case '\\': p[-1] = '\\'; break;
-               case 0: *p = 0; return;
-               default: p[-1] = q[-1]; break;
-           }
-}
-
 int
 main(argc, argv)
      int argc;
@@ -1657,49 +1807,18 @@ main(argc, argv)
     char *p;
     XrmDatabase xdb;
     int forceMono = False;
-//define INDIRECTION
-#ifdef INDIRECTION
-    // [HGM] before anything else, expand any indirection files amongst options
-    char *argvCopy[1000]; // 1000 seems enough
-    char newArgs[10000];  // holds actual characters
-    int k = 0;
 
     srandom(time(0)); // [HGM] book: make random truly random
 
-    j = 0;
-    for(i=0; i<argc; i++) {
-       if(j >= 1000-2) { printf(_("too many arguments\n")); exit(-1); }
-//fprintf(stderr, "arg %s\n", argv[i]);
-       if(argv[i][0] != '@') argvCopy[j++] = argv[i]; else {
-           char c;
-           FILE *f = fopen(argv[i]+1, "rb");
-           if(f == NULL) { fprintf(stderr, _("ignore %s\n"), argv[i]); continue; } // do not expand non-existing
-           argvCopy[j++] = newArgs + k; // get ready for first argument from file
-           while((c = fgetc(f)) != EOF) { // each line of file inserts 1 argument in the list
-               if(c == '\n') {
-                   if(j >= 1000-2) { printf(_("too many arguments\n")); exit(-1); }
-                   newArgs[k++] = 0;  // terminate current arg
-                   if(k >= 10000-1) { printf(_("too long arguments\n")); exit(-1); }
-                   argvCopy[j++] = newArgs + k; // get ready for next
-               } else {
-                   if(k >= 10000-1) { printf(_("too long arguments\n")); exit(-1); }
-                   newArgs[k++] = c;
-               }
-           }
-           newArgs[k] = 0;
-           j--;
-           fclose(f);
-       }
-    }
-    argvCopy[j] = NULL;
-    argv = argvCopy;
-    argc = j;
-#endif
-
     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);
+    }
+
     programName = strrchr(argv[0], '/');
     if (programName == NULL)
       programName = argv[0];
@@ -1836,7 +1955,14 @@ main(argc, argv)
        fontPxlSize = szd->fontPxlSize;
        smallLayout = szd->smallLayout;
        tinyLayout = szd->tinyLayout;
+       // [HGM] font: use defaults from settings file if available and not overruled
     }
+    if(!fontSet[CLOCK_FONT] && fontValid[CLOCK_FONT][squareSize])
+       appData.clockFont = fontTable[CLOCK_FONT][squareSize];
+    if(!fontSet[MESSAGE_FONT] && fontValid[MESSAGE_FONT][squareSize])
+       appData.font = fontTable[MESSAGE_FONT][squareSize];
+    if(!fontSet[COORD_FONT] && fontValid[COORD_FONT][squareSize])
+       appData.coordFont = fontTable[COORD_FONT][squareSize];
 
     /* Now, using squareSize as a hint, find a good XPM/XIM set size */
     if (strlen(appData.pixmapDirectory) > 0) {
@@ -1856,6 +1982,7 @@ XBoard square size (hint): %d\n\
            fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
        }
     }
+    if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
 
     /* [HR] height treated separately (hacked) */
     boardWidth = lineGap + BOARD_WIDTH * (squareSize + lineGap);
@@ -1972,7 +2099,7 @@ XBoard square size (hint): %d\n\
     if (forceMono) {
       fprintf(stderr, _("%s: too few colors available; trying monochrome mode\n"),
              programName);
-      
+
       if (appData.bitmapDirectory == NULL ||
              appData.bitmapDirectory[0] == NULLCHAR)
            appData.bitmapDirectory = DEF_BITMAP_DIR;
@@ -1982,7 +2109,7 @@ XBoard square size (hint): %d\n\
       vFrom.addr = (caddr_t) appData.lowTimeWarningColor;
       vFrom.size = strlen(appData.lowTimeWarningColor);
       XtConvert(shellWidget, XtRString, &vFrom, XtRPixel, &vTo);
-      if (vTo.addr == NULL) 
+      if (vTo.addr == NULL)
                appData.monoMode = True;
       else
                lowTimeWarningColor = *(Pixel *) vTo.addr;
@@ -2339,6 +2466,11 @@ XBoard square size (hint): %d\n\
                                   "menuOptions.Highlight Last Move"),
                    args, 1);
     }
+    if (appData.highlightMoveWithArrow) {
+       XtSetValues(XtNameToWidget(menuBarWidget,
+                                  "menuOptions.Arrow"),
+                   args, 1);
+    }
     if (appData.icsAlarm) {
        XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.ICS Alarm"),
                    args, 1);
@@ -2347,9 +2479,9 @@ XBoard square size (hint): %d\n\
        XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Move Sound"),
                    args, 1);
     }
-    if (appData.oldSaveStyle) {
+    if (appData.oneClick) {
        XtSetValues(XtNameToWidget(menuBarWidget,
-                                  "menuOptions.Old Save Style"), args, 1);
+                                  "menuOptions.OneClick"), args, 1);
     }
     if (appData.periodicUpdates) {
        XtSetValues(XtNameToWidget(menuBarWidget,
@@ -2432,6 +2564,8 @@ XBoard square size (hint): %d\n\
       CreatePieces();
     } else {
       CreateXPMPieces();
+      CreateXPMBoard(appData.liteBackTextureFile, 1);
+      CreateXPMBoard(appData.darkBackTextureFile, 0);
     }
 #else
     CreateXIMPieces();
@@ -2455,10 +2589,24 @@ XBoard square size (hint): %d\n\
 
     /* Why is the following needed on some versions of X instead
      * of a translation? */
-    XtAddEventHandler(boardWidget, ExposureMask, False,
+    XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
                      (XtEventHandler) EventProc, NULL);
     /* end why */
 
+    /* [AS] Restore layout */
+    if( wpMoveHistory.visible ) {
+      HistoryPopUp();
+    }
+
+    if( wpEvalGraph.visible )
+      {
+       EvalGraphPopUp();
+      };
+
+    if( wpEngineOutput.visible ) {
+      EngineOutputPopUp();
+    }
+
     InitBackEnd2();
 
     if (errorExitStatus == -1) {
@@ -2480,6 +2628,7 @@ XBoard square size (hint): %d\n\
     }
     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
     InitPosition(TRUE);
+    XtSetKeyboardFocus(shellWidget, formWidget);
 
     XtAppMainLoop(appContext);
     if (appData.debugMode) fclose(debugFP); // [DM] debug
@@ -2541,22 +2690,33 @@ CmailSigHandlerCallBack(isr, closure, message, count, error)
 void
 ICSInitScript()
 {
-    FILE *f;
-    char buf[MSG_SIZ];
-    char *p;
+  /* try to open the icsLogon script, either in the location given
+   * or in the users HOME directory
+   */
+
+  FILE *f;
+  char buf[MSG_SIZ];
+  char *homedir;
 
-    f = fopen(appData.icsLogon, "r");
-    if (f == NULL) {
-       p = getenv("HOME");
-       if (p != NULL) {
-           strcpy(buf, p);
-           strcat(buf, "/");
-           strcat(buf, appData.icsLogon);
-           f = fopen(buf, "r");
+  f = fopen(appData.icsLogon, "r");
+  if (f == NULL)
+    {
+      homedir = getenv("HOME");
+      if (homedir != NULL)
+       {
+         safeStrCpy(buf, homedir, sizeof(buf)/sizeof(buf[0]) );
+         strncat(buf, "/", MSG_SIZ - strlen(buf) - 1);
+         strncat(buf, appData.icsLogon,  MSG_SIZ - strlen(buf) - 1);
+         f = fopen(buf, "r");
        }
     }
-    if (f != NULL)
-      ProcessICSInitScript(f);
+
+  if (f != NULL)
+    ProcessICSInitScript(f);
+  else
+    printf("Warning: Couldn't open icsLogon file (checked %s and %s).\n", appData.icsLogon, buf);
+
+  return;
 }
 
 void
@@ -2579,9 +2739,15 @@ GreyRevert(grey)
 {
     Widget w;
     if (!menuBarWidget) return;
-    w = XtNameToWidget(menuBarWidget, "menuStep.Revert");
+    w = XtNameToWidget(menuBarWidget, "menuEdit.Revert");
+    if (w == NULL) {
+      DisplayError("menuEdit.Revert", 0);
+    } else {
+      XtSetSensitive(w, !grey);
+    }
+    w = XtNameToWidget(menuBarWidget, "menuEdit.Annotate");
     if (w == NULL) {
-      DisplayError("menuStep.Revert", 0);
+      DisplayError("menuEdit.Annotate", 0);
     } else {
       XtSetSensitive(w, !grey);
     }
@@ -2613,13 +2779,16 @@ Enables icsEnables[] = {
     { "menuMode.Analyze File", False },
     { "menuMode.Two Machines", False },
 #ifndef ZIPPY
-    { "menuHelp.Hint", False },
-    { "menuHelp.Book", False },
-    { "menuStep.Move Now", False },
+    { "menuEngine.Hint", False },
+    { "menuEngine.Book", False },
+    { "menuEngine.Move Now", False },
     { "menuOptions.Periodic Updates", False },
     { "menuOptions.Hide Thinking", False },
     { "menuOptions.Ponder Next Move", False },
+    { "menuEngine.Engine #1 Settings", False },
 #endif
+    { "menuEngine.Engine #2 Settings", False },
+    { "menuEdit.Annotate", False },
     { NULL, False }
 };
 
@@ -2632,11 +2801,14 @@ Enables ncpEnables[] = {
     { "menuMode.Analyze File", False },
     { "menuMode.Two Machines", False },
     { "menuMode.ICS Client", False },
-    { "menuMode.ICS Input Box", False },
+    { "menuView.ICS Input Box", False },
     { "Action", False },
-    { "menuStep.Revert", False },
-    { "menuStep.Move Now", False },
-    { "menuStep.Retract Move", False },
+    { "menuEdit.Revert", False },
+    { "menuEdit.Annotate", False },
+    { "menuEngine.Engine #1 Settings", False },
+    { "menuEngine.Engine #2 Settings", False },
+    { "menuEngine.Move Now", False },
+    { "menuEngine.Retract Move", False },
     { "menuOptions.Auto Comment", False },
     { "menuOptions.Auto Flag", False },
     { "menuOptions.Auto Flip View", False },
@@ -2649,21 +2821,23 @@ Enables ncpEnables[] = {
     { "menuOptions.Hide Thinking", False },
     { "menuOptions.Periodic Updates", False },
     { "menuOptions.Ponder Next Move", False },
-    { "menuHelp.Hint", False },
-    { "menuHelp.Book", False },
+    { "menuEngine.Hint", False },
+    { "menuEngine.Book", False },
     { NULL, False }
 };
 
 Enables gnuEnables[] = {
     { "menuMode.ICS Client", False },
-    { "menuMode.ICS Input Box", False },
+    { "menuView.ICS Input Box", False },
     { "menuAction.Accept", False },
     { "menuAction.Decline", False },
     { "menuAction.Rematch", False },
     { "menuAction.Adjourn", False },
     { "menuAction.Stop Examining", False },
     { "menuAction.Stop Observing", False },
-    { "menuStep.Revert", False },
+    { "menuAction.Upload to Examine", False },
+    { "menuEdit.Revert", False },
+    { "menuEdit.Annotate", False },
     { "menuOptions.Auto Comment", False },
     { "menuOptions.Auto Observe", False },
     { "menuOptions.Auto Raise Board", False },
@@ -2696,60 +2870,60 @@ Enables cmailEnables[] = {
 Enables trainingOnEnables[] = {
   { "menuMode.Edit Comment", False },
   { "menuMode.Pause", False },
-  { "menuStep.Forward", False },
-  { "menuStep.Backward", False },
-  { "menuStep.Forward to End", False },
-  { "menuStep.Back to Start", False },
-  { "menuStep.Move Now", False },
-  { "menuStep.Truncate Game", False },
+  { "menuEdit.Forward", False },
+  { "menuEdit.Backward", False },
+  { "menuEdit.Forward to End", False },
+  { "menuEdit.Back to Start", False },
+  { "menuEngine.Move Now", False },
+  { "menuEdit.Truncate Game", False },
   { NULL, False }
 };
 
 Enables trainingOffEnables[] = {
   { "menuMode.Edit Comment", True },
   { "menuMode.Pause", True },
-  { "menuStep.Forward", True },
-  { "menuStep.Backward", True },
-  { "menuStep.Forward to End", True },
-  { "menuStep.Back to Start", True },
-  { "menuStep.Move Now", True },
-  { "menuStep.Truncate Game", True },
+  { "menuEdit.Forward", True },
+  { "menuEdit.Backward", True },
+  { "menuEdit.Forward to End", True },
+  { "menuEdit.Back to Start", True },
+  { "menuEngine.Move Now", True },
+  { "menuEdit.Truncate Game", True },
   { NULL, False }
 };
 
 Enables machineThinkingEnables[] = {
   { "menuFile.Load Game", False },
-  { "menuFile.Load Next Game", False },
-  { "menuFile.Load Previous Game", False },
-  { "menuFile.Reload Same Game", False },
-  { "menuFile.Paste Game", False },
+//  { "menuFile.Load Next Game", False },
+//  { "menuFile.Load Previous Game", False },
+//  { "menuFile.Reload Same Game", False },
+  { "menuEdit.Paste Game", False },
   { "menuFile.Load Position", False },
-  { "menuFile.Load Next Position", False },
-  { "menuFile.Load Previous Position", False },
-  { "menuFile.Reload Same Position", False },
-  { "menuFile.Paste Position", False },
+//  { "menuFile.Load Next Position", False },
+//  { "menuFile.Load Previous Position", False },
+//  { "menuFile.Reload Same Position", False },
+  { "menuEdit.Paste Position", False },
   { "menuMode.Machine White", False },
   { "menuMode.Machine Black", False },
   { "menuMode.Two Machines", False },
-  { "menuStep.Retract Move", False },
+  { "menuEngine.Retract Move", False },
   { NULL, False }
 };
 
 Enables userThinkingEnables[] = {
   { "menuFile.Load Game", True },
-  { "menuFile.Load Next Game", True },
-  { "menuFile.Load Previous Game", True },
-  { "menuFile.Reload Same Game", True },
-  { "menuFile.Paste Game", True },
+//  { "menuFile.Load Next Game", True },
+//  { "menuFile.Load Previous Game", True },
+//  { "menuFile.Reload Same Game", True },
+  { "menuEdit.Paste Game", True },
   { "menuFile.Load Position", True },
-  { "menuFile.Load Next Position", True },
-  { "menuFile.Load Previous Position", True },
-  { "menuFile.Reload Same Position", True },
-  { "menuFile.Paste Position", True },
+//  { "menuFile.Load Next Position", True },
+//  { "menuFile.Load Previous Position", True },
+//  { "menuFile.Reload Same Position", True },
+  { "menuEdit.Paste Position", True },
   { "menuMode.Machine White", True },
   { "menuMode.Machine Black", True },
   { "menuMode.Two Machines", True },
-  { "menuStep.Retract Move", True },
+  { "menuEngine.Retract Move", True },
   { NULL, False }
 };
 
@@ -2757,7 +2931,7 @@ void SetICSMode()
 {
   SetMenuEnables(icsEnables);
 
-#ifdef ZIPPY
+#if ZIPPY
   if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
 #endif
@@ -2824,6 +2998,51 @@ SetMachineThinkingEnables()
   }
 }
 
+// [HGM] code borrowed from winboard.c (which should thus go to backend.c!)
+#define HISTORY_SIZE 64
+static char *history[HISTORY_SIZE];
+int histIn = 0, histP = 0;
+
+void
+SaveInHistory(char *cmd)
+{
+  if (history[histIn] != NULL) {
+    free(history[histIn]);
+    history[histIn] = NULL;
+  }
+  if (*cmd == NULLCHAR) return;
+  history[histIn] = StrSave(cmd);
+  histIn = (histIn + 1) % HISTORY_SIZE;
+  if (history[histIn] != NULL) {
+    free(history[histIn]);
+    history[histIn] = NULL;
+  }
+  histP = histIn;
+}
+
+char *
+PrevInHistory(char *cmd)
+{
+  int newhp;
+  if (histP == histIn) {
+    if (history[histIn] != NULL) free(history[histIn]);
+    history[histIn] = StrSave(cmd);
+  }
+  newhp = (histP - 1 + HISTORY_SIZE) % HISTORY_SIZE;
+  if (newhp == histIn || history[newhp] == NULL) return NULL;
+  histP = newhp;
+  return history[histP];
+}
+
+char *
+NextInHistory()
+{
+  if (histP == histIn) return NULL;
+  histP = (histP + 1) % HISTORY_SIZE;
+  return history[histP];   
+}
+// end of borrowed code
+
 #define Abs(n) ((n)<0 ? -(n) : (n))
 
 /*
@@ -2835,7 +3054,8 @@ SetMachineThinkingEnables()
  * The return value should be freed with XtFree when no
  * longer needed.
  */
-char *FindFont(pattern, targetPxlSize)
+char *
+FindFont(pattern, targetPxlSize)
      char *pattern;
      int targetPxlSize;
 {
@@ -2850,7 +3070,7 @@ char *FindFont(pattern, targetPxlSize)
     XFontStruct **fnt_list;
 
     base_fnt_lst = calloc(1, strlen(pattern) + 3);
-    sprintf(strInt, "%d", targetPxlSize);
+    snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
     p = strstr(pattern, "--");
     strncpy(base_fnt_lst, pattern, p - pattern + 2);
     strcat(base_fnt_lst, strInt);
@@ -2909,8 +3129,8 @@ char *FindFont(pattern, targetPxlSize)
        while (isdigit(*scalableTail)) scalableTail++;
        sprintf(p, "%.*s%d%s", headlen, scalable, targetPxlSize, scalableTail);
     } else {
-        p = (char *) XtMalloc(strlen(best) + 1);
-        strcpy(p, best);
+        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"),
@@ -3200,6 +3420,16 @@ void CreateXIMPieces()
 }
 
 #if HAVE_LIBXPM
+void CreateXPMBoard(char *s, int kind)
+{
+    XpmAttributes attr;
+    attr.valuemask = 0;
+    if(s == NULL || *s == 0 || *s == '*') return;
+    if (XpmReadFileToPixmap(xDisplay, xBoardWindow, s, &(xpmBoardBitmap[kind]), NULL, &attr) == 0) {
+       useTexture |= kind + 1; textureW[kind] = attr.width; textureH[kind] = attr.height;
+    }
+}
+
 void CreateXPMPieces()
 {
     int piece, kind, r;
@@ -3296,7 +3526,7 @@ void CreateXPMPieces()
                        exit(1);
                    }
                }
-               if(piece <= (int) WhiteKing) 
+               if(piece <= (int) WhiteKing)
                    xpmPieceBitmap[kind][piece] = xpmPieceBitmap2[kind][piece];
            }
        }
@@ -3350,12 +3580,12 @@ void CreatePieces()
 
     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
        for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
-           sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
-                   pieceBitmapNames[piece],
-                   ss, kind == SOLID ? 's' : 'o');
-           ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
-           if(piece <= (int)WhiteKing)
-               pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
+         snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
+                  pieceBitmapNames[piece],
+                  ss, kind == SOLID ? 's' : 'o');
+         ReadBitmap(&pieceBitmap2[kind][piece], buf, NULL, ss, ss);
+         if(piece <= (int)WhiteKing)
+           pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
        }
     }
 
@@ -3378,13 +3608,13 @@ void CreatePieces()
 
     for (kind = SOLID; kind <= (appData.monoMode ? OUTLINE : SOLID); kind++) {
        for (piece = (int) WhitePawn; piece <= (int) WhiteKing + 4; piece++) {
-           sprintf(buf, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
-                   pieceBitmapNames[piece],
-                   ss, kind == SOLID ? 's' : 'o');
-           ReadBitmap(&pieceBitmap2[kind][piece], buf,
-                      bib->bits[kind][piece], ss, ss);
-           if(piece <= (int)WhiteKing)
-               pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
+         snprintf(buf, MSG_SIZ, "%s%c%u%c.bm", piece > (int)WhiteKing ? "w" : "",
+                  pieceBitmapNames[piece],
+                  ss, kind == SOLID ? 's' : 'o');
+         ReadBitmap(&pieceBitmap2[kind][piece], buf,
+                    bib->bits[kind][piece], ss, ss);
+         if(piece <= (int)WhiteKing)
+           pieceBitmap[kind][piece] = pieceBitmap2[kind][piece];
        }
     }
 
@@ -3405,12 +3635,12 @@ void ReadBitmap(pm, name, bits, wreq, hreq)
     char msg[MSG_SIZ], fullname[MSG_SIZ];
 
     if (*appData.bitmapDirectory != NULLCHAR) {
-        strcpy(fullname, appData.bitmapDirectory);
-       strcat(fullname, "/");
-       strcat(fullname, name);
-       errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
-                                 &w, &h, pm, &x_hot, &y_hot);
-    fprintf(stderr, "load %s\n", name);
+      safeStrCpy(fullname, appData.bitmapDirectory, sizeof(fullname)/sizeof(fullname[0]) );
+      strncat(fullname, "/", MSG_SIZ - strlen(fullname) - 1);
+      strncat(fullname, name, MSG_SIZ - strlen(fullname) - 1);
+      errcode = XReadBitmapFile(xDisplay, xBoardWindow, fullname,
+                               &w, &h, pm, &x_hot, &y_hot);
+      fprintf(stderr, "load %s\n", name);
        if (errcode != BitmapSuccess) {
            switch (errcode) {
              case BitmapOpenFailed:
@@ -3500,8 +3730,8 @@ void CreateMenuBarPopup(parent, name, mb)
            entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
                                          menu, args, j);
        } else {
-          XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
-           entry = XtCreateManagedWidget(mi->string, smeBSBObjectClass,
+          XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
+           entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
                                          menu, args, j+1);
            XtAddCallback(entry, XtNcallback,
                          (XtCallbackProc) MenuBarSelect,
@@ -3527,18 +3757,18 @@ Widget CreateMenuBar(mb)
                             formWidget, args, j);
 
     while (mb->name != NULL) {
-       strcpy(menuName, "menu");
-       strcat(menuName, mb->name);
+        safeStrCpy(menuName, "menu", sizeof(menuName)/sizeof(menuName[0]) );
+       strncat(menuName, mb->ref, MSG_SIZ - strlen(menuName) - 1);
        j = 0;
        XtSetArg(args[j], XtNmenuName, XtNewString(menuName));  j++;
        if (tinyLayout) {
            char shortName[2];
-            shortName[0] = _(mb->name)[0];
+            shortName[0] = mb->name[0];
            shortName[1] = NULLCHAR;
            XtSetArg(args[j], XtNlabel, XtNewString(shortName)); j++;
        }
       else {
-          XtSetArg(args[j], XtNlabel, XtNewString(_(mb->name))); j++;
+          XtSetArg(args[j], XtNlabel, XtNewString(mb->name)); j++;
       }
 
        XtSetArg(args[j], XtNborderWidth, 0);                   j++;
@@ -3686,39 +3916,18 @@ void PieceMenuPopup(w, event, params, num_params)
      String *params;
      Cardinal *num_params;
 {
-    String whichMenu;
-    if (event->type != ButtonPress) return;
-    if (errorUp) ErrorPopDown();
-    switch (gameMode) {
-      case EditPosition:
-      case IcsExamining:
-       whichMenu = params[0];
-       break;
-      case IcsPlayingWhite:
-      case IcsPlayingBlack:
-      case EditGame:
-      case MachinePlaysWhite:
-      case MachinePlaysBlack:
-       if (appData.testLegality &&
-           gameInfo.variant != VariantBughouse &&
-           gameInfo.variant != VariantCrazyhouse) return;
-       SetupDropMenu();
-       whichMenu = "menuD";
-       break;
-      default:
-       return;
-    }
-
-    if (((pmFromX = EventToSquare(event->xbutton.x, BOARD_WIDTH)) < 0) ||
-       ((pmFromY = EventToSquare(event->xbutton.y, BOARD_HEIGHT)) < 0)) {
-       pmFromX = pmFromY = -1;
-       return;
+    String whichMenu; int menuNr;
+    if (event->type == ButtonRelease)
+        menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
+    else if (event->type == ButtonPress)
+        menuNr = RightClick(Press,   event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
+    switch(menuNr) {
+      case 0: whichMenu = params[0]; break;
+      case 1: SetupDropMenu(); whichMenu = "menuD"; break;
+      case 2:
+      case -1: if (errorUp) ErrorPopDown();
+      default: return;
     }
-    if (flipView)
-      pmFromX = BOARD_WIDTH - 1 - pmFromX;
-    else
-      pmFromY = BOARD_HEIGHT - 1 - pmFromY;
-
     XtPopupSpringLoaded(XtNameToWidget(boardWidget, whichMenu));
 }
 
@@ -3746,11 +3955,7 @@ void WhiteClock(w, event, prms, nprms)
      String *prms;
      Cardinal *nprms;
 {
-    if (gameMode == EditPosition || gameMode == IcsExamining) {
-       SetWhiteToPlayEvent();
-    } else if (gameMode == IcsPlayingBlack || gameMode == MachinePlaysWhite) {
-       CallFlagEvent();
-    }
+    ClockClick(0);
 }
 
 void BlackClock(w, event, prms, nprms)
@@ -3759,11 +3964,7 @@ void BlackClock(w, event, prms, nprms)
      String *prms;
      Cardinal *nprms;
 {
-    if (gameMode == EditPosition || gameMode == IcsExamining) {
-       SetBlackToPlayEvent();
-    } else if (gameMode == IcsPlayingWhite || gameMode == MachinePlaysBlack) {
-       CallFlagEvent();
-    }
+    ClockClick(1);
 }
 
 
@@ -3799,7 +4000,7 @@ static void drawHighlight(file, rank, gc)
 {
     int x, y;
 
-    if (lineGap == 0 || appData.blindfold) return;
+    if (lineGap == 0) return;
 
     if (flipView) {
        x = lineGap/2 + ((BOARD_WIDTH-1)-file) *
@@ -3826,14 +4027,18 @@ SetHighlights(fromX, fromY, toX, toY)
        if (hi1X >= 0 && hi1Y >= 0) {
            drawHighlight(hi1X, hi1Y, lineGC);
        }
+    } // [HGM] first erase both, then draw new!
+    if (hi2X != toX || hi2Y != toY) {
+       if (hi2X >= 0 && hi2Y >= 0) {
+           drawHighlight(hi2X, hi2Y, lineGC);
+       }
+    }
+    if (hi1X != fromX || hi1Y != fromY) {
        if (fromX >= 0 && fromY >= 0) {
            drawHighlight(fromX, fromY, highlineGC);
        }
     }
     if (hi2X != toX || hi2Y != toY) {
-       if (hi2X >= 0 && hi2Y >= 0) {
-           drawHighlight(hi2X, hi2Y, lineGC);
-       }
        if (toX >= 0 && toY >= 0) {
            drawHighlight(toX, toY, highlineGC);
        }
@@ -3883,11 +4088,34 @@ ClearPremoveHighlights()
   SetPremoveHighlights(-1, -1, -1, -1);
 }
 
-static void BlankSquare(x, y, color, piece, dest)
-     int x, y, color;
+static int CutOutSquare(x, y, x0, y0, kind)
+     int x, y, *x0, *y0, kind;
+{
+    int W = BOARD_WIDTH, H = BOARD_HEIGHT;
+    int nx = x/(squareSize + lineGap), ny = y/(squareSize + lineGap);
+    *x0 = 0; *y0 = 0;
+    if(textureW[kind] < squareSize || textureH[kind] < squareSize) return 0;
+    if(textureW[kind] < W*squareSize)
+       *x0 = (textureW[kind] - squareSize) * nx/(W-1);
+    else
+       *x0 = textureW[kind]*nx / W + (textureW[kind] - W*squareSize) / (2*W);
+    if(textureH[kind] < H*squareSize)
+       *y0 = (textureH[kind] - squareSize) * ny/(H-1);
+    else
+       *y0 = textureH[kind]*ny / H + (textureH[kind] - H*squareSize) / (2*H);
+    return 1;
+}
+
+static void BlankSquare(x, y, color, piece, dest, fac)
+     int x, y, color, fac;
      ChessSquare piece;
      Drawable dest;
-{
+{   // [HGM] extra param 'fac' for forcing destination to (0,0) for copying to animation buffer
+    int x0, y0;
+    if (useImages && color != 2 && (useTexture & color+1) && CutOutSquare(x, y, &x0, &y0, color)) {
+       XCopyArea(xDisplay, xpmBoardBitmap[color], dest, wlPieceGC, x0, y0,
+                 squareSize, squareSize, x*fac, y*fac);
+    } else
     if (useImages && useImageSqs) {
        Pixmap pm;
        switch (color) {
@@ -3903,7 +4131,7 @@ static void BlankSquare(x, y, color, piece, dest)
            break;
        }
        XCopyArea(xDisplay, pm, dest, wlPieceGC, 0, 0,
-                 squareSize, squareSize, x, y);
+                 squareSize, squareSize, x*fac, y*fac);
     } else {
        GC gc;
        switch (color) {
@@ -3918,7 +4146,7 @@ static void BlankSquare(x, y, color, piece, dest)
            gc = jailSquareGC;
            break;
        }
-       XFillRectangle(xDisplay, dest, gc, x, y, squareSize, squareSize);
+       XFillRectangle(xDisplay, dest, gc, x*fac, y*fac, squareSize, squareSize);
     }
 }
 
@@ -4011,7 +4239,7 @@ static void colorDrawPieceImage(piece, square_color, x, y, dest)
      int square_color, x, y;
      Drawable dest;
 {
-    int kind;
+    int kind, p = piece;
 
     switch (square_color) {
       case 1: /* light */
@@ -4033,6 +4261,15 @@ static void colorDrawPieceImage(piece, square_color, x, y, dest)
        }
        break;
     }
+    if(appData.upsideDown && flipView) kind ^= 2; // swap white and black pieces
+    if(useTexture & square_color+1) {
+        BlankSquare(x, y, square_color, piece, dest, 1); // erase previous contents with background
+       XSetClipMask(xDisplay, wlPieceGC, xpmMask[p]);
+       XSetClipOrigin(xDisplay, wlPieceGC, x, y);
+       XCopyArea(xDisplay, xpmPieceBitmap[kind][piece], dest, wlPieceGC, 0, 0, squareSize, squareSize, x, y);
+       XSetClipMask(xDisplay, wlPieceGC, None);
+       XSetClipOrigin(xDisplay, wlPieceGC, 0, 0);
+    } else
     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
              dest, wlPieceGC, 0, 0,
              squareSize, squareSize, x, y);
@@ -4106,13 +4343,15 @@ void DrawSquare(row, column, piece, do_flash)
          (squareSize + lineGap);
     }
 
+    if(twoBoards && partnerUp) x += hOffset; // [HGM] dual: draw second board
+
     square_color = SquareColor(row, column);
 
     if ( // [HGM] holdings: blank out area between board and holdings
                  column == BOARD_LEFT-1 ||  column == BOARD_RGHT
               || (column == BOARD_LEFT-2 && row < BOARD_HEIGHT-gameInfo.holdingsSize)
                  || (column == BOARD_RGHT+1 && row >= gameInfo.holdingsSize) ) {
-                       BlankSquare(x, y, 2, EmptySquare, xBoardWindow);
+                       BlankSquare(x, y, 2, EmptySquare, xBoardWindow, 1);
 
                        // [HGM] print piece counts next to holdings
                        string[1] = NULLCHAR;
@@ -4144,7 +4383,7 @@ void DrawSquare(row, column, piece, do_flash)
                        }
     } else {
            if (piece == EmptySquare || appData.blindfold) {
-                       BlankSquare(x, y, square_color, piece, xBoardWindow);
+                       BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
            } else {
                        drawfunc = ChooseDrawFunc();
                        if (do_flash && appData.flashCount > 0) {
@@ -4154,7 +4393,7 @@ void DrawSquare(row, column, piece, do_flash)
                                        XSync(xDisplay, False);
                                        do_flash_delay(flash_delay);
 
-                                       BlankSquare(x, y, square_color, piece, xBoardWindow);
+                                       BlankSquare(x, y, square_color, piece, xBoardWindow, 1);
                                        XSync(xDisplay, False);
                                        do_flash_delay(flash_delay);
                            }
@@ -4191,6 +4430,10 @@ void DrawSquare(row, column, piece, do_flash)
                        x + 2, y + font_ascent + 1, string, 1);
        }
     }
+    if(!partnerUp && marker[row][column]) {
+       XFillArc(xDisplay, xBoardWindow, marker[row][column] == 2 ? prelineGC : highlineGC,
+               x + squareSize/4, y+squareSize/4, squareSize/2, squareSize/2, 0, 64*360);
+    }
 }
 
 
@@ -4207,7 +4450,14 @@ void EventProc(widget, unused, event)
       case Expose:
        if (event->xexpose.count > 0) return;  /* no clipping is done */
        XDrawPosition(widget, True, NULL);
+       if(twoBoards) { // [HGM] dual: draw other board in other orientation
+           flipView = !flipView; partnerUp = !partnerUp;
+           XDrawPosition(widget, True, NULL);
+           flipView = !flipView; partnerUp = !partnerUp;
+       }
        break;
+      case MotionNotify:
+        if(SeekGraphClick(Press, event->xbutton.x, event->xbutton.y, 1)) break;
       default:
        return;
     }
@@ -4288,7 +4538,37 @@ static int check_castle_draw(newb, oldb, rrow, rcol)
     return 0;
 }
 
-static int damage[BOARD_RANKS][BOARD_FILES];
+// [HGM] seekgraph: some low-level drawing routines cloned from xevalgraph
+void DrawSeekAxis( int x, int y, int xTo, int yTo )
+{
+      XDrawLine(xDisplay, xBoardWindow, lineGC, x, y, xTo, yTo);
+}
+
+void DrawSeekBackground( int left, int top, int right, int bottom )
+{
+    XFillRectangle(xDisplay, xBoardWindow, lightSquareGC, left, top, right-left, bottom-top);
+}
+
+void DrawSeekText(char *buf, int x, int y)
+{
+    XDrawString(xDisplay, xBoardWindow, coordGC, x, y+4, buf, strlen(buf));
+}
+
+void DrawSeekDot(int x, int y, int colorNr)
+{
+    int square = colorNr & 0x80;
+    GC color;
+    colorNr &= 0x7F;
+    color = colorNr == 0 ? prelineGC : colorNr == 1 ? darkSquareGC : highlineGC;
+    if(square)
+       XFillRectangle(xDisplay, xBoardWindow, color,
+               x-squareSize/9, y-squareSize/9, 2*squareSize/9, 2*squareSize/9);
+    else
+       XFillArc(xDisplay, xBoardWindow, color,
+               x-squareSize/8, y-squareSize/8, squareSize/4, squareSize/4, 0, 64*360);
+}
+
+static int damage[2][BOARD_RANKS][BOARD_FILES];
 
 /*
  * event handler for redrawing the board
@@ -4300,18 +4580,21 @@ void XDrawPosition(w, repaint, board)
 {
     int i, j, do_flash;
     static int lastFlipView = 0;
-    static int lastBoardValid = 0;
-    static Board lastBoard;
+    static int lastBoardValid[2] = {0, 0};
+    static Board lastBoard[2];
     Arg args[16];
     int rrow, rcol;
+    int nr = twoBoards*partnerUp;
+
+    if(DrawSeekGraph()) return; // [HGM] seekgraph: suppress any drawing if seek graph up
 
     if (board == NULL) {
-       if (!lastBoardValid) return;
-       board = lastBoard;
+       if (!lastBoardValid[nr]) return;
+       board = lastBoard[nr];
     }
-    if (!lastBoardValid || lastFlipView != flipView) {
+    if (!lastBoardValid[nr] || (nr == 0 && lastFlipView != flipView)) {
        XtSetArg(args[0], XtNleftBitmap, (flipView ? xMarkPixmap : None));
-       XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Flip View"),
+       XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Flip View"),
                    args, 1);
     }
 
@@ -4320,19 +4603,20 @@ void XDrawPosition(w, repaint, board)
      * but this causes a very distracting flicker.
      */
 
-    if (!repaint && lastBoardValid && lastFlipView == flipView) {
+    if ( lineGap && IsDrawArrowEnabled()) repaint = True;
+    if (!repaint && lastBoardValid[nr] && (nr == 1 || lastFlipView == flipView)) {
 
        /* If too much changes (begin observing new game, etc.), don't
           do flashing */
-       do_flash = too_many_diffs(board, lastBoard) ? 0 : 1;
+       do_flash = too_many_diffs(board, lastBoard[nr]) ? 0 : 1;
 
        /* Special check for castling so we don't flash both the king
           and the rook (just flash the king). */
        if (do_flash) {
-           if (check_castle_draw(board, lastBoard, &rrow, &rcol)) {
+           if (check_castle_draw(board, lastBoard[nr], &rrow, &rcol)) {
                /* Draw rook with NO flashing. King will be drawn flashing later */
                DrawSquare(rrow, rcol, board[rrow][rcol], 0);
-               lastBoard[rrow][rcol] = board[rrow][rcol];
+               lastBoard[nr][rrow][rcol] = board[rrow][rcol];
            }
        }
 
@@ -4341,32 +4625,34 @@ void XDrawPosition(w, repaint, board)
           is flashing on its new square */
        for (i = 0; i < BOARD_HEIGHT; i++)
          for (j = 0; j < BOARD_WIDTH; j++)
-           if ((board[i][j] != lastBoard[i][j] && board[i][j] == EmptySquare)
-               || damage[i][j]) {
+           if ((board[i][j] != lastBoard[nr][i][j] && board[i][j] == EmptySquare)
+               || damage[nr][i][j]) {
                DrawSquare(i, j, board[i][j], 0);
-               damage[i][j] = False;
+               damage[nr][i][j] = False;
            }
 
        /* Second pass -- Draw piece(s) in new position and flash them */
        for (i = 0; i < BOARD_HEIGHT; i++)
          for (j = 0; j < BOARD_WIDTH; j++)
-           if (board[i][j] != lastBoard[i][j]) {
+           if (board[i][j] != lastBoard[nr][i][j]) {
                DrawSquare(i, j, board[i][j], do_flash);
            }
     } else {
        if (lineGap > 0)
          XDrawSegments(xDisplay, xBoardWindow, lineGC,
+                       twoBoards & partnerUp ? secondSegments : // [HGM] dual
                        gridSegments, BOARD_HEIGHT + BOARD_WIDTH + 2);
 
        for (i = 0; i < BOARD_HEIGHT; i++)
          for (j = 0; j < BOARD_WIDTH; j++) {
              DrawSquare(i, j, board[i][j], 0);
-             damage[i][j] = False;
+             damage[nr][i][j] = False;
          }
     }
 
-    CopyBoard(lastBoard, board);
-    lastBoardValid = 1;
+    CopyBoard(lastBoard[nr], board);
+    lastBoardValid[nr] = 1;
+  if(nr == 0) { // [HGM] dual: no highlights on second board yet
     lastFlipView = flipView;
 
     /* Draw highlights */
@@ -4382,7 +4668,8 @@ void XDrawPosition(w, repaint, board)
     if (hi2X >= 0 && hi2Y >= 0) {
       drawHighlight(hi2X, hi2Y, highlineGC);
     }
-
+    DrawArrowHighlight(hi1X, hi1Y, hi2X, hi2Y);
+  }
     /* If piece being dragged around board, must redraw that too */
     DrawDragPiece();
 
@@ -4420,6 +4707,7 @@ void HandleUserMove(w, event, prms, nprms)
      Cardinal *nprms;
 {
     if (w != boardWidget || errorExitStatus != -1) return;
+    if(nprms) shiftKey = !strcmp(prms[0], "1");
 
     if (promotionUp) {
        if (event->type == ButtonPress) {
@@ -4444,6 +4732,12 @@ void AnimateUserMove (Widget w, XEvent * event,
     DragPieceMove(event->xmotion.x, event->xmotion.y);
 }
 
+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));
+}
+
 Widget CommentCreate(name, text, mutable, callback, lines)
      char *name, *text;
      int /*Boolean*/ mutable;
@@ -4495,6 +4789,7 @@ Widget CommentCreate(name, text, mutable, callback, lines)
     XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
     edit =
       XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
+    XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
 
     if (mutable) {
        j = 0;
@@ -4703,6 +4998,20 @@ Widget MiscCreate(name, text, mutable, callback, lines)
 
 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(index, title, text)
      int index;
      char *title, *text;
@@ -4735,7 +5044,9 @@ void EditCommentPopUp(index, title, text)
     editUp = True;
     j = 0;
     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
+    XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
+               args, j);
+    XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
                args, j);
 }
 
@@ -4784,7 +5095,9 @@ void EditCommentPopDown()
     editUp = False;
     j = 0;
     XtSetArg(args[j], XtNleftBitmap, None); j++;
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.Edit Comment"),
+    XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
+               args, j);
+    XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
                args, j);
 }
 
@@ -4821,7 +5134,7 @@ void ICSInputBoxPopUp()
     ICSInputBoxUp = True;
     j = 0;
     XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
+    XtSetValues(XtNameToWidget(menuBarWidget, "menuView.ICS Input Box"),
                args, j);
 }
 
@@ -4836,6 +5149,7 @@ void ICSInputSendText()
     j = 0;
     XtSetArg(args[j], XtNstring, &val); j++;
     XtGetValues(edit, args, j);
+    SaveInHistory(val);
     SendMultiLineToICS(val);
     XtCallActionProc(edit, "select-all", NULL, NULL, 0);
     XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
@@ -4852,7 +5166,7 @@ void ICSInputBoxPopDown()
     ICSInputBoxUp = False;
     j = 0;
     XtSetArg(args[j], XtNleftBitmap, None); j++;
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuMode.ICS Input Box"),
+    XtSetValues(XtNameToWidget(menuBarWidget, "menuView.ICS Input Box"),
                args, j);
 }
 
@@ -4863,6 +5177,7 @@ void CommentPopUp(title, text)
     int j;
     Widget edit;
 
+    savedIndex = currentMove; // [HGM] vari
     if (commentShell == NULL) {
        commentShell =
          CommentCreate(title, text, False, CommentCallback, 4);
@@ -4929,54 +5244,16 @@ void FileNamePopUp(label, def, proc, openMode)
      FileProc proc;
      char *openMode;
 {
-    Arg args[16];
-    Widget popup, layout, dialog, edit;
-    Window root, child;
-    int x, y, i;
-    int win_x, win_y;
-    unsigned int mask;
-
     fileProc = proc;           /* I can't see a way not */
     fileOpenMode = openMode;   /*   to use globals here */
-
-    i = 0;
-    XtSetArg(args[i], XtNresizable, True); i++;
-    XtSetArg(args[i], XtNwidth, DIALOG_SIZE); i++;
-    XtSetArg(args[i], XtNtitle, XtNewString(_("File name prompt"))); i++;
-    fileNameShell = popup =
-      XtCreatePopupShell("File name prompt", transientShellWidgetClass,
-                        shellWidget, args, i);
-
-    layout =
-      XtCreateManagedWidget(layoutName, formWidgetClass, popup,
-                           layoutArgs, XtNumber(layoutArgs));
-
-    i = 0;
-    XtSetArg(args[i], XtNlabel, label); i++;
-    XtSetArg(args[i], XtNvalue, def); i++;
-    XtSetArg(args[i], XtNborderWidth, 0); i++;
-    dialog = XtCreateManagedWidget("fileName", dialogWidgetClass,
-                                  layout, args, i);
-
-    XawDialogAddButton(dialog, _("ok"), FileNameCallback, (XtPointer) dialog);
-    XawDialogAddButton(dialog, _("cancel"), FileNameCallback,
-                      (XtPointer) dialog);
-
-    XtRealizeWidget(popup);
-    CatchDeleteWindow(popup, "FileNamePopDown");
-
-    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, XtGrabExclusive);
-    filenameUp = True;
-
-    edit = XtNameToWidget(dialog, "*value");
-    XtSetKeyboardFocus(popup, edit);
+    {   // [HGM] use file-selector dialog stolen from Ghostview
+       char *name;
+       int index; // this is not supported yet
+       FILE *f;
+       if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
+                          def, openMode, NULL, &name))
+         (void) (*fileProc)(f, index=0, name);
+    }
 }
 
 void FileNamePopDown()
@@ -5021,7 +5298,7 @@ void FileNameAction(w, event, prms, nprms)
     name = XawDialogGetValueString(w = XtParent(w));
 
     if ((name != NULL) && (*name != NULLCHAR)) {
-       strcpy(buf, name);
+        safeStrCpy(buf, name, sizeof(buf)/sizeof(buf[0]) );
        XtPopdown(w = XtParent(XtParent(w)));
        XtDestroyWidget(w);
        filenameUp = False;
@@ -5084,6 +5361,16 @@ void PromotionPopUp()
                                   layout, args, j);
 
   if(gameInfo.variant != VariantShogi) {
+   if(gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove)) {
+      XawDialogAddButton(dialog, _("Warlord"), PromotionCallback,
+                        (XtPointer) dialog);
+      XawDialogAddButton(dialog, _("General"), PromotionCallback,
+                        (XtPointer) dialog);
+      XawDialogAddButton(dialog, _("Lieutenant"), PromotionCallback,
+                        (XtPointer) dialog);
+      XawDialogAddButton(dialog, _("Captain"), PromotionCallback,
+                        (XtPointer) dialog);
+    } else {\r
     XawDialogAddButton(dialog, _("Queen"), PromotionCallback,
                       (XtPointer) dialog);
     XawDialogAddButton(dialog, _("Rook"), PromotionCallback,
@@ -5092,13 +5379,15 @@ void PromotionPopUp()
                       (XtPointer) dialog);
     XawDialogAddButton(dialog, _("Knight"), PromotionCallback,
                       (XtPointer) dialog);
+    }
     if (!appData.testLegality || gameInfo.variant == VariantSuicide ||
+        gameInfo.variant == VariantSpartan && !WhiteOnMove(currentMove) ||\r
         gameInfo.variant == VariantGiveaway) {
       XawDialogAddButton(dialog, _("King"), PromotionCallback,
                         (XtPointer) dialog);
     }
-    if(gameInfo.variant == VariantCapablanca || 
-       gameInfo.variant == VariantGothic || 
+    if(gameInfo.variant == VariantCapablanca ||
+       gameInfo.variant == VariantGothic ||
        gameInfo.variant == VariantCapaRandom) {
       XawDialogAddButton(dialog, _("Archbishop"), PromotionCallback,
                         (XtPointer) dialog);
@@ -5536,9 +5825,9 @@ void MailMoveProc(w, event, prms, nprms)
 }
 
 /* this variable is shared between CopyPositionProc and SendPositionSelection */
-static char *selected_fen_position=NULL;
+char *selected_fen_position=NULL;
 
-static Boolean
+Boolean
 SendPositionSelection(Widget w, Atom *selection, Atom *target,
                 Atom *type_return, XtPointer *value_return,
                 unsigned long *length_return, int *format_return)
@@ -5551,7 +5840,7 @@ SendPositionSelection(Widget w, Atom *selection, Atom *target,
      * 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);
-    strcpy(selection_tmp, selected_fen_position);
+    safeStrCpy(selection_tmp, selected_fen_position, strlen(selected_fen_position)+16 );
 
     *value_return=selection_tmp;
     *length_return=strlen(selection_tmp);
@@ -5590,6 +5879,7 @@ void CopyPositionProc(w, event, prms, nprms)
      * have a notion of a position that is selected but not copied.
      * See http://www.freedesktop.org/wiki/Specifications/ClipboardsWiki
      */
+    if(gameMode == EditPosition) EditPositionDone(TRUE);
     if (selected_fen_position) free(selected_fen_position);
     selected_fen_position = (char *)PositionToFEN(currentMove, NULL);
     if (!selected_fen_position) return;
@@ -5625,7 +5915,7 @@ void PastePositionProc(w, event, prms, nprms)
   String *prms;
   Cardinal *nprms;
 {
-    XtGetSelectionValue(menuBarWidget, 
+    XtGetSelectionValue(menuBarWidget,
       appData.pasteSelection ? XA_PRIMARY: XA_CLIPBOARD(xDisplay), XA_STRING,
       /* (XtSelectionCallbackProc) */ PastePositionCB,
       NULL, /* client_data passed to PastePositionCB */
@@ -5815,7 +6105,7 @@ void AnalyzeModeProc(w, event, prms, nprms)
     /* [DM] icsEngineAnalyze [HGM] This is horrible code; reverse the gameMode and isEngineAnalyze tests! */
     if (appData.icsActive) {
         if (gameMode != IcsObserving) {
-            sprintf(buf,_("You are not observing a game"));
+         snprintf(buf, MSG_SIZ, _("You are not observing a game"));
             DisplayError(buf, 0);
             /* secure check */
             if (appData.icsEngineAnalyze) {
@@ -6044,6 +6334,55 @@ void EnterKeyProc(w, event, prms, nprms)
       ICSInputSendText();
 }
 
+void UpKeyProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{   // [HGM] input: let up-arrow recall previous line from history
+    Widget edit;
+    int j;
+    Arg args[16];
+    String val;
+    XawTextBlock t;
+
+    if (!ICSInputBoxUp) return;
+    edit = XtNameToWidget(ICSInputShell, "*form.text");
+    j = 0;
+    XtSetArg(args[j], XtNstring, &val); j++;
+    XtGetValues(edit, args, j);
+    val = PrevInHistory(val);
+    XtCallActionProc(edit, "select-all", NULL, NULL, 0);
+    XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
+    if(val) {
+       t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
+       XawTextReplace(edit, 0, 0, &t);
+       XawTextSetInsertionPoint(edit, 9999);
+    }
+}
+
+void DownKeyProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{   // [HGM] input: let down-arrow recall next line from history
+    Widget edit;
+    String val;
+    XawTextBlock t;
+
+    if (!ICSInputBoxUp) return;
+    edit = XtNameToWidget(ICSInputShell, "*form.text");
+    val = NextInHistory();
+    XtCallActionProc(edit, "select-all", NULL, NULL, 0);
+    XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
+    if(val) {
+       t.ptr = val; t.firstPos = 0; t.length = strlen(val); t.format = XawFmt8Bit;
+       XawTextReplace(edit, 0, 0, &t);
+       XawTextSetInsertionPoint(edit, 9999);
+    }
+}
+
 void StopObservingProc(w, event, prms, nprms)
      Widget w;
      XEvent *event;
@@ -6062,6 +6401,15 @@ void StopExaminingProc(w, event, prms, nprms)
     StopExaminingEvent();
 }
 
+void UploadProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{
+    UploadGameEvent();
+}
+
 
 void ForwardProc(w, event, prms, nprms)
      Widget w;
@@ -6106,7 +6454,16 @@ void RevertProc(w, event, prms, nprms)
      String *prms;
      Cardinal *nprms;
 {
-    RevertEvent();
+    RevertEvent(False);
+}
+
+void AnnotateProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{
+    RevertEvent(True);
 }
 
 void TruncateGameProc(w, event, prms, nprms)
@@ -6444,6 +6801,25 @@ void HighlightLastMoveProc(w, event, prms, nprms)
                               "menuOptions.Highlight Last Move"), args, 1);
 }
 
+void HighlightArrowProc(w, event, prms, nprms)
+     Widget w;
+     XEvent *event;
+     String *prms;
+     Cardinal *nprms;
+{
+    Arg args[16];
+
+    appData.highlightMoveWithArrow = !appData.highlightMoveWithArrow;
+
+    if (appData.highlightMoveWithArrow) {
+       XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
+    } else {
+       XtSetArg(args[0], XtNleftBitmap, None);
+    }
+    XtSetValues(XtNameToWidget(menuBarWidget,
+                              "menuOptions.Arrow"), args, 1);
+}
+
 void IcsAlarmProc(w, event, prms, nprms)
      Widget w;
      XEvent *event;
@@ -6482,8 +6858,7 @@ void MoveSoundProc(w, event, prms, nprms)
                args, 1);
 }
 
-
-void OldSaveStyleProc(w, event, prms, nprms)
+void OneClickProc(w, event, prms, nprms)
      Widget w;
      XEvent *event;
      String *prms;
@@ -6491,14 +6866,14 @@ void OldSaveStyleProc(w, event, prms, nprms)
 {
     Arg args[16];
 
-    appData.oldSaveStyle = !appData.oldSaveStyle;
+    appData.oneClick = !appData.oneClick;
 
-    if (appData.oldSaveStyle) {
+    if (appData.oneClick) {
        XtSetArg(args[0], XtNleftBitmap, xMarkPixmap);
     } else {
        XtSetArg(args[0], XtNleftBitmap, None);
     }
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.Old Save Style"),
+    XtSetValues(XtNameToWidget(menuBarWidget, "menuOptions.OneClick"),
                args, 1);
 }
 
@@ -6807,23 +7182,23 @@ void DisplayMessage(message, extMessage)
      char *message, *extMessage;
 {
   /* display a message in the message widget */
-  
+
   char buf[MSG_SIZ];
   Arg arg;
-  
-  if (extMessage) 
+
+  if (extMessage)
     {
-      if (*message) 
+      if (*message)
        {
          snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
          message = buf;
-       } 
-      else 
+       }
+      else
        {
          message = extMessage;
        };
     };
-  
+
   /* need to test if messageWidget already exists, since this function
      can also be called during the startup, if for example a Xresource
      is not set up correctly */
@@ -6832,7 +7207,7 @@ void DisplayMessage(message, extMessage)
       XtSetArg(arg, XtNlabel, message);
       XtSetValues(messageWidget, &arg, 1);
     };
-  
+
   return;
 }
 
@@ -6853,8 +7228,8 @@ void DisplayTitle(text)
     }
 
     if (*text != NULLCHAR) {
-       strcpy(icon, text);
-       strcpy(title, text);
+      safeStrCpy(icon, text, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, text, sizeof(title)/sizeof(title[0]) );
     } else if (appData.icsActive) {
         snprintf(icon, sizeof(icon), "%s", appData.icsHost);
        snprintf(title, sizeof(title), "%s: %s", programName, appData.icsHost);
@@ -6864,19 +7239,19 @@ void DisplayTitle(text)
 #ifdef GOTHIC
     // [HGM] license: This stuff should really be done in back-end, but WinBoard already had a pop-up for it
     } else if (gameInfo.variant == VariantGothic) {
-       strcpy(icon, programName);
-       strcpy(title, GOTHIC);
+      safeStrCpy(icon,  programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, GOTHIC,     sizeof(title)/sizeof(title[0]) );
 #endif
 #ifdef FALCON
     } else if (gameInfo.variant == VariantFalcon) {
-       strcpy(icon, programName);
-       strcpy(title, FALCON);
+      safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, FALCON, sizeof(title)/sizeof(title[0]) );
 #endif
     } else if (appData.noChessProgram) {
-       strcpy(icon, programName);
-       strcpy(title, programName);
+      safeStrCpy(icon, programName, sizeof(icon)/sizeof(icon[0]) );
+      safeStrCpy(title, programName, sizeof(title)/sizeof(title[0]) );
     } else {
-       strcpy(icon, first.tidy);
+      safeStrCpy(icon, first.tidy, sizeof(icon)/sizeof(icon[0]) );
        snprintf(title,sizeof(title), "%s: %s", programName, first.tidy);
     }
     i = 0;
@@ -6886,7 +7261,8 @@ void DisplayTitle(text)
 }
 
 
-void DisplayError(message, error)
+void
+DisplayError(message, error)
      String message;
      int error;
 {
@@ -7034,10 +7410,10 @@ void AskQuestionReplyAction(w, event, prms, nprms)
     String reply;
 
     reply = XawDialogGetValueString(w = XtParent(w));
-    strcpy(buf, pendingReplyPrefix);
-    if (*buf) strcat(buf, " ");
-    strcat(buf, reply);
-    strcat(buf, "\n");
+    safeStrCpy(buf, pendingReplyPrefix, sizeof(buf)/sizeof(buf[0]) );
+    if (*buf) strncat(buf, " ", MSG_SIZ - strlen(buf) - 1);
+    strncat(buf, reply, MSG_SIZ - strlen(buf) - 1);
+    strncat(buf, "\n",  MSG_SIZ - strlen(buf) - 1);
     OutputToProcess(pendingReplyPR, buf, strlen(buf), &err);
     AskQuestionPopDown();
 
@@ -7072,7 +7448,7 @@ void AskQuestion(title, question, replyPrefix, pr)
     int win_x, win_y;
     unsigned int mask;
 
-    strcpy(pendingReplyPrefix, replyPrefix);
+    safeStrCpy(pendingReplyPrefix, replyPrefix, sizeof(pendingReplyPrefix)/sizeof(pendingReplyPrefix[0]) );
     pendingReplyPR = pr;
 
     i = 0;
@@ -7189,18 +7565,18 @@ Colorize(cc, continuation)
 
     if (textColors[(int)cc].bg > 0) {
        if (textColors[(int)cc].fg > 0) {
-           sprintf(buf, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
-                   textColors[(int)cc].fg, textColors[(int)cc].bg);
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%d;%dm", textColors[(int)cc].attr,
+                  textColors[(int)cc].fg, textColors[(int)cc].bg);
        } else {
-           sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
-                   textColors[(int)cc].bg);
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
+                  textColors[(int)cc].bg);
        }
     } else {
        if (textColors[(int)cc].fg > 0) {
-           sprintf(buf, "\033[0;%d;%dm", textColors[(int)cc].attr,
+         snprintf(buf, MSG_SIZ, "\033[0;%d;%dm", textColors[(int)cc].attr,
                    textColors[(int)cc].fg);
        } else {
-           sprintf(buf, "\033[0;%dm", textColors[(int)cc].attr);
+         snprintf(buf, MSG_SIZ, "\033[0;%dm", textColors[(int)cc].attr);
        }
     }
     count = strlen(buf);
@@ -7250,11 +7626,12 @@ char *UserName()
     return getpwuid(getuid())->pw_name;
 }
 
-static char *ExpandPathName(path)
+static char *
+ExpandPathName(path)
      char *path;
 {
-    static char static_buf[2000];
-    char *d, *s, buf[2000];
+    static char static_buf[4*MSG_SIZ];
+    char *d, *s, buf[4*MSG_SIZ];
     struct passwd *pwd;
 
     s = path;
@@ -7270,25 +7647,25 @@ static char *ExpandPathName(path)
 
     if (*s == '~') {
        if (*(s+1) == '/') {
-           strcpy(d, getpwuid(getuid())->pw_dir);
-           strcat(d, s+1);
+         safeStrCpy(d, getpwuid(getuid())->pw_dir, 4*MSG_SIZ );
+         strcat(d, s+1);
        }
        else {
-           strcpy(buf, s+1);
-           *strchr(buf, '/') = 0;
-           pwd = getpwnam(buf);
-           if (!pwd)
-             {
-                 fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
-                         buf, path);
-                 return NULL;
-             }
-           strcpy(d, pwd->pw_dir);
-           strcat(d, strchr(s+1, '/'));
+         safeStrCpy(buf, s+1, sizeof(buf)/sizeof(buf[0]) );
+         { char *p; if(p = strchr(buf, '/')) *p = 0; }
+         pwd = getpwnam(buf);
+         if (!pwd)
+           {
+             fprintf(stderr, _("ERROR: Unknown user %s (in path %s)\n"),
+                     buf, path);
+             return NULL;
+           }
+         safeStrCpy(d, pwd->pw_dir, 4*MSG_SIZ );
+         strcat(d, strchr(s+1, '/'));
        }
     }
     else
-      strcpy(d, s);
+      safeStrCpy(d, s, 4*MSG_SIZ );
 
     return static_buf;
 }
@@ -7463,16 +7840,16 @@ DisplayTimerLabel(w, color, timer, highlight)
     Pixel foregroundOrWarningColor = timerForegroundPixel;
 
     if (timer > 0 &&
-        appData.lowTimeWarning && 
+        appData.lowTimeWarning &&
         (timer / 1000) < appData.icsAlarmTime)
       foregroundOrWarningColor = lowTimeWarningColor;
 
     if (appData.clockMode) {
-       sprintf(buf, "%s: %s", color, TimeString(timer));
-       XtSetArg(args[0], XtNlabel, buf);
+      snprintf(buf, MSG_SIZ, "%s: %s", color, TimeString(timer));
+      XtSetArg(args[0], XtNlabel, buf);
     } else {
-       sprintf(buf, "%s  ", color);
-       XtSetArg(args[0], XtNlabel, buf);
+      snprintf(buf, MSG_SIZ, "%s  ", color);
+      XtSetArg(args[0], XtNlabel, buf);
     }
 
     if (highlight) {
@@ -7553,11 +7930,14 @@ int StartChildProcess(cmdLine, dir, pr)
        most simple-minded way possible.
        */
     i = 0;
-    strcpy(buf, cmdLine);
+    safeStrCpy(buf, cmdLine, sizeof(buf)/sizeof(buf[0]) );
     p = buf;
     for (;;) {
+       while(*p == ' ') p++;
        argv[i++] = p;
-       p = strchr(p, ' ');
+       if(*p == '"' || *p == '\'')
+            p = strchr(++argv[i-1], *p);
+       else p = strchr(p, ' ');
        if (p == NULL) break;
        *p++ = NULLCHAR;
     }
@@ -8364,6 +8744,7 @@ OverlayPiece(piece, clip, outline,  dest)
       kind = 0;
     else
       kind = 2;
+    if(appData.upsideDown && flipView) kind ^= 2;
     XCopyArea(xDisplay, xpmPieceBitmap[kind][piece],
              dest, clip,
              0, 0, squareSize, squareSize,
@@ -8383,7 +8764,7 @@ BeginAnimation(anim, piece, startColor, start)
   Pixmap mask;
 
   /* The old buffer is initialised with the start square (empty) */
-  BlankSquare(0, 0, startColor, EmptySquare, anim->saveBuf);
+  BlankSquare(start->x, start->y, startColor, EmptySquare, anim->saveBuf, 0);
   anim->prevFrame = *start;
 
   /* The piece will be drawn using its own bitmap as a matte   */
@@ -8488,6 +8869,29 @@ FrameSequence(anim, piece, startColor, start, finish, frames, nFrames)
   EndAnimation(anim, finish);
 }
 
+void
+AnimateAtomicCapture(Board board, int fromX, int fromY, int toX, int toY)
+{
+    int i, x, y;
+    ChessSquare piece = board[fromY][toY];
+    board[fromY][toY] = EmptySquare;
+    DrawPosition(FALSE, board);
+    if (flipView) {
+       x = lineGap + ((BOARD_WIDTH-1)-toX) * (squareSize + lineGap);
+       y = lineGap + toY * (squareSize + lineGap);
+    } else {
+       x = lineGap + toX * (squareSize + lineGap);
+       y = lineGap + ((BOARD_HEIGHT-1)-toY) * (squareSize + lineGap);
+    }
+    for(i=1; i<4*kFactor; i++) {
+       int r = squareSize * 9 * i/(20*kFactor - 5);
+       XFillArc(xDisplay, xBoardWindow, highlineGC,
+               x + squareSize/2 - r, y+squareSize/2 - r, 2*r, 2*r, 0, 64*360);
+       FrameDelay(appData.animSpeed);
+    }
+    board[fromY][toY] = piece;
+}
+
 /* Main control logic for deciding what to animate and how */
 
 void
@@ -8508,8 +8912,8 @@ AnimateMove(board, fromX, fromY, toX, toY)
   if (!appData.animate || appData.blindfold)
     return;
 
-  if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing || 
-     board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing) 
+  if(board[toY][toX] == WhiteRook && board[fromY][fromX] == WhiteKing ||
+     board[toY][toX] == BlackRook && board[fromY][fromX] == BlackKing)
        return; // [HGM] FRC: no animtion of FRC castlings, as to-square is not true to-square
 
   if (fromY < 0 || fromX < 0 || toX < 0 || toY < 0) return;
@@ -8519,7 +8923,7 @@ AnimateMove(board, fromX, fromY, toX, toY)
 #if DONT_HOP
   hop = FALSE;
 #else
-  hop = (piece == WhiteKnight || piece == BlackKnight);
+  hop = abs(fromX-toX) == 1 && abs(fromY-toY) == 2 || abs(fromX-toX) == 2 && abs(fromY-toY) == 1;
 #endif
 
   if (appData.debugMode) {
@@ -8531,12 +8935,12 @@ AnimateMove(board, fromX, fromY, toX, toY)
   ScreenSquare(toX, toY, &finish, &endColor);
 
   if (hop) {
-    /* Knight: make diagonal movement then straight */
+    /* Knight: make straight movement then diagonal */
     if (abs(toY - fromY) < abs(toX - fromX)) {
        mid.x = start.x + (finish.x - start.x) / 2;
-       mid.y = finish.y;
+       mid.y = start.y;
      } else {
-       mid.x = finish.x;
+       mid.x = start.x;
        mid.y = start.y + (finish.y - start.y) / 2;
      }
   } else {
@@ -8550,9 +8954,14 @@ AnimateMove(board, fromX, fromY, toX, toY)
   else
     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);
   FrameSequence(&game, piece, startColor, &start, &finish, frames, nFrames);
+  if(Explode(board, fromX, fromY, toX, toY)) { // mark as damaged
+    int i,j;
+    for(i=0; i<BOARD_WIDTH; i++) for(j=0; j<BOARD_HEIGHT; j++)
+      if((i-toX)*(i-toX) + (j-toY)*(j-toY) < 6) damage[0][j][i] = True;
+  }
 
   /* Be sure end square is redrawn */
-  damage[toY][toX] = True;
+  damage[0][toY][toX] = True;
 }
 
 void
@@ -8592,7 +9001,18 @@ DragPieceBegin(x, y)
            XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
                     corner.x, corner.y, squareSize, squareSize,
                     0, 0); // [HGM] zh: unstack in stead of grab
-       damage[boardY][boardX] = True;
+           if(gatingPiece != EmptySquare) {
+               /* Kludge alert: When gating we want the introduced
+                  piece to appear on the from square. To generate an
+                  image of it, we draw it on the board, copy the image,
+                  and draw the original piece again. */
+               ChessSquare piece = boards[currentMove][boardY][boardX];
+               DrawSquare(boardY, boardX, gatingPiece, 0);
+               XCopyArea(xDisplay, xBoardWindow, player.saveBuf, player.blitGC,
+                    corner.x, corner.y, squareSize, squareSize, 0, 0);
+               DrawSquare(boardY, boardX, piece, 0);
+           }
+       damage[0][boardY][boardX] = True;
     } else {
        player.dragActive = False;
     }
@@ -8616,7 +9036,7 @@ DragPieceMove(x, y)
     corner.x = x - player.mouseDelta.x;
     corner.y = y - player.mouseDelta.y;
     AnimationFrame(&player, &corner, player.dragPiece);
-#if HIGHDRAG
+#if HIGHDRAG*0
     if (appData.highlightDragging) {
        int boardX, boardY;
        BoardSquare(x, y, &boardX, &boardY);
@@ -8646,7 +9066,7 @@ DragPieceEnd(x, y)
     EndAnimation(&player, &corner);
 
     /* Be sure end square is redrawn */
-    damage[boardY][boardX] = True;
+    damage[0][boardY][boardX] = True;
 
     /* This prevents weird things happening with fast successive
        clicks which on my Sun at least can cause motion events
@@ -8667,9 +9087,9 @@ DrawDragPiece ()
      it's being dragged around the board. So we erase the square
      that the piece is on and draw it at the last known drag point. */
   BlankSquare(player.startSquare.x, player.startSquare.y,
-               player.startColor, EmptySquare, xBoardWindow);
+               player.startColor, EmptySquare, xBoardWindow, 1);
   AnimationFrame(&player, &player.prevFrame, player.dragPiece);
-  damage[player.startBoardY][player.startBoardX] = TRUE;
+  damage[0][player.startBoardY][player.startBoardX] = TRUE;
 }
 
 #include <sys/ioctl.h>
@@ -8692,17 +9112,214 @@ int get_term_width()
     return default_width;
 }
 
-void update_ics_width()
+void
+update_ics_width()
 {
-    static int old_width = 0;
-    int new_width = get_term_width();
+  static int old_width = 0;
+  int new_width = get_term_width();
 
-    if (old_width != new_width)
-       ics_printf("set width %d\n", new_width);
-    old_width = new_width;
+  if (old_width != new_width)
+    ics_printf("set width %d\n", new_width);
+  old_width = new_width;
 }
 
 void NotifyFrontendLogin()
 {
     update_ics_width();
 }
+
+/* [AS] Arrow highlighting support */
+
+static double A_WIDTH = 5; /* Width of arrow body */
+
+#define A_HEIGHT_FACTOR 6   /* Length of arrow "point", relative to body width */
+#define A_WIDTH_FACTOR  3   /* Width of arrow "point", relative to body width */
+
+static double Sqr( double x )
+{
+    return x*x;
+}
+
+static int Round( double x )
+{
+    return (int) (x + 0.5);
+}
+
+void SquareToPos(int rank, int file, int *x, int *y)
+{
+    if (flipView) {
+       *x = lineGap + ((BOARD_WIDTH-1)-file) * (squareSize + lineGap);
+       *y = lineGap + rank * (squareSize + lineGap);
+    } else {
+       *x = lineGap + file * (squareSize + lineGap);
+       *y = lineGap + ((BOARD_HEIGHT-1)-rank) * (squareSize + lineGap);
+    }
+}
+
+/* Draw an arrow between two points using current settings */
+void DrawArrowBetweenPoints( int s_x, int s_y, int d_x, int d_y )
+{
+    XPoint arrow[7];
+    double dx, dy, j, k, x, y;
+
+    if( d_x == s_x ) {
+        int h = (d_y > s_y) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
+
+        arrow[0].x = s_x + A_WIDTH + 0.5;
+        arrow[0].y = s_y;
+
+        arrow[1].x = s_x + A_WIDTH + 0.5;
+        arrow[1].y = d_y - h;
+
+        arrow[2].x = arrow[1].x + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
+        arrow[2].y = d_y - h;
+
+        arrow[3].x = d_x;
+        arrow[3].y = d_y;
+
+        arrow[5].x = arrow[1].x - 2*A_WIDTH + 0.5;
+        arrow[5].y = d_y - h;
+
+        arrow[4].x = arrow[5].x - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
+        arrow[4].y = d_y - h;
+
+        arrow[6].x = arrow[1].x - 2*A_WIDTH + 0.5;
+        arrow[6].y = s_y;
+    }
+    else if( d_y == s_y ) {
+        int w = (d_x > s_x) ? +A_WIDTH*A_HEIGHT_FACTOR : -A_WIDTH*A_HEIGHT_FACTOR;
+
+        arrow[0].x = s_x;
+        arrow[0].y = s_y + A_WIDTH + 0.5;
+
+        arrow[1].x = d_x - w;
+        arrow[1].y = s_y + A_WIDTH + 0.5;
+
+        arrow[2].x = d_x - w;
+        arrow[2].y = arrow[1].y + A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
+
+        arrow[3].x = d_x;
+        arrow[3].y = d_y;
+
+        arrow[5].x = d_x - w;
+        arrow[5].y = arrow[1].y - 2*A_WIDTH + 0.5;
+
+        arrow[4].x = d_x - w;
+        arrow[4].y = arrow[5].y - A_WIDTH*(A_WIDTH_FACTOR-1) + 0.5;
+
+        arrow[6].x = s_x;
+        arrow[6].y = arrow[1].y - 2*A_WIDTH + 0.5;
+    }
+    else {
+        /* [AS] Needed a lot of paper for this! :-) */
+        dy = (double) (d_y - s_y) / (double) (d_x - s_x);
+        dx = (double) (s_x - d_x) / (double) (s_y - d_y);
+
+        j = sqrt( Sqr(A_WIDTH) / (1.0 + Sqr(dx)) );
+
+        k = sqrt( Sqr(A_WIDTH*A_HEIGHT_FACTOR) / (1.0 + Sqr(dy)) );
+
+        x = s_x;
+        y = s_y;
+
+        arrow[0].x = Round(x - j);
+        arrow[0].y = Round(y + j*dx);
+
+        arrow[1].x = Round(arrow[0].x + 2*j);   // [HGM] prevent width to be affected by rounding twice
+        arrow[1].y = Round(arrow[0].y - 2*j*dx);
+
+        if( d_x > s_x ) {
+            x = (double) d_x - k;
+            y = (double) d_y - k*dy;
+        }
+        else {
+            x = (double) d_x + k;
+            y = (double) d_y + k*dy;
+        }
+
+        x = Round(x); y = Round(y); // [HGM] make sure width of shaft is rounded the same way on both ends
+
+        arrow[6].x = Round(x - j);
+        arrow[6].y = Round(y + j*dx);
+
+        arrow[2].x = Round(arrow[6].x + 2*j);
+        arrow[2].y = Round(arrow[6].y - 2*j*dx);
+
+        arrow[3].x = Round(arrow[2].x + j*(A_WIDTH_FACTOR-1));
+        arrow[3].y = Round(arrow[2].y - j*(A_WIDTH_FACTOR-1)*dx);
+
+        arrow[4].x = d_x;
+        arrow[4].y = d_y;
+
+        arrow[5].x = Round(arrow[6].x - j*(A_WIDTH_FACTOR-1));
+        arrow[5].y = Round(arrow[6].y + j*(A_WIDTH_FACTOR-1)*dx);
+    }
+
+    XFillPolygon(xDisplay, xBoardWindow, highlineGC, arrow, 7, Nonconvex, CoordModeOrigin);
+//    Polygon( hdc, arrow, 7 );
+}
+
+/* [AS] Draw an arrow between two squares */
+void DrawArrowBetweenSquares( int s_col, int s_row, int d_col, int d_row )
+{
+    int s_x, s_y, d_x, d_y, hor, vert, i;
+
+    if( s_col == d_col && s_row == d_row ) {
+        return;
+    }
+
+    /* Get source and destination points */
+    SquareToPos( s_row, s_col, &s_x, &s_y);
+    SquareToPos( d_row, d_col, &d_x, &d_y);
+
+    if( d_y > s_y ) {
+        d_y += squareSize / 2 - squareSize / 4; // [HGM] round towards same centers on all sides!
+    }
+    else if( d_y < s_y ) {
+        d_y += squareSize / 2 + squareSize / 4;
+    }
+    else {
+        d_y += squareSize / 2;
+    }
+
+    if( d_x > s_x ) {
+        d_x += squareSize / 2 - squareSize / 4;
+    }
+    else if( d_x < s_x ) {
+        d_x += squareSize / 2 + squareSize / 4;
+    }
+    else {
+        d_x += squareSize / 2;
+    }
+
+    s_x += squareSize / 2;
+    s_y += squareSize / 2;
+
+    /* Adjust width */
+    A_WIDTH = squareSize / 14.; //[HGM] make float
+
+    DrawArrowBetweenPoints( s_x, s_y, d_x, d_y );
+
+    if(lineGap == 0) {
+        // this is a good idea, but it only works when lineGap == 0, because 'damage' on grid lines is not repaired
+        hor = 64*s_col + 32; vert = 64*s_row + 32;
+        for(i=0; i<= 64; i++) {
+            damage[0][vert+6>>6][hor+6>>6] = True;
+            damage[0][vert-6>>6][hor+6>>6] = True;
+            damage[0][vert+6>>6][hor-6>>6] = True;
+            damage[0][vert-6>>6][hor-6>>6] = True;
+            hor += d_col - s_col; vert += d_row - s_row;
+        }
+    }
+}
+
+Boolean IsDrawArrowEnabled()
+{
+    return appData.highlightMoveWithArrow && squareSize >= 32;
+}
+
+void DrawArrowHighlight(int fromX, int fromY, int toX,int toY)
+{
+    if( IsDrawArrowEnabled() && fromX >= 0 && fromY >= 0 && toX >= 0 && toY >= 0)
+        DrawArrowBetweenSquares(fromX, fromY, toX, toY);
+}