Lock game an position file during writing
[xboard.git] / xboard.c
index 7b55b63..e9c4347 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -238,11 +238,12 @@ typedef struct {
 
 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));
+               char *init_path, char *filter, 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((int redo));
+void CreateAnyPieces P((void));
 void CreateXIMPieces P((void));
 void CreateXPMPieces P((void));
 void CreateXPMBoard P((char *s, int n));
@@ -261,6 +262,7 @@ void CreateGrid P((void));
 int EventToSquare P((int x, int limit));
 void DrawSquare P((int row, int column, ChessSquare piece, int do_flash));
 void EventProc P((Widget widget, caddr_t unused, XEvent *event));
+void MoveTypeInProc P((Widget widget, caddr_t unused, XEvent *event));
 void HandleUserMove P((Widget w, XEvent *event,
                     String *prms, Cardinal *nprms));
 void AnimateUserMove P((Widget w, XEvent * event,
@@ -283,11 +285,9 @@ 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,
-                       XtPointer call_data));
 void ICSInputBoxPopUp P((void));
 void ICSInputBoxPopDown P((void));
-void FileNamePopUp P((char *label, char *def,
+void FileNamePopUp P((char *label, char *def, char *filter,
                      FileProc proc, char *openMode));
 void FileNamePopDown P((void));
 void FileNameCallback P((Widget w, XtPointer client_data,
@@ -302,9 +302,6 @@ void AskQuestionPopDown P((void));
 void PromotionPopDown P((void));
 void PromotionCallback P((Widget w, XtPointer client_data,
                          XtPointer call_data));
-void EditCommentPopDown P((void));
-void EditCommentCallback P((Widget w, XtPointer client_data,
-                           XtPointer call_data));
 void SelectCommand P((Widget w, XtPointer client_data, XtPointer call_data));
 void ResetProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void LoadGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
@@ -327,6 +324,7 @@ void CopyPositionProc P((Widget w, XEvent *event, String *prms,
 void PastePositionProc P((Widget w, XEvent *event, String *prms,
                          Cardinal *nprms));
 void CopyGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void CopyGameListProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void PasteGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void SaveGameProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void SavePositionProc P((Widget w, XEvent *event,
@@ -369,6 +367,7 @@ void ResignProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 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 TypeInProc 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));
@@ -454,6 +453,8 @@ void UciMenuProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void TimeControlProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void OptionsProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
 void NewVariantProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void IcsTextProc P((Widget w, XEvent *event, String *prms, Cardinal *nprms));
+void LoadEngineProc 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));
@@ -519,7 +520,7 @@ int squareSize, smallLayout = 0, tinyLayout = 0,
   fromX = -1, fromY = -1, toX, toY, commentUp = False, analysisUp = False,
   ICSInputBoxUp = False, askQuestionUp = False,
   filenameUp = False, promotionUp = False, pmFromX = -1, pmFromY = -1,
-  editUp = False, errorUp = False, errorExitStatus = -1, lineGap;
+  errorUp = False, errorExitStatus = -1, lineGap, defaultLineGap;
 Pixel timerForegroundPixel, timerBackgroundPixel;
 Pixel buttonForegroundPixel, buttonBackgroundPixel;
 char *chessDir, *programName, *programVersion,
@@ -541,6 +542,9 @@ WindowPlacement wpEngineOutput;
 WindowPlacement wpGameList;
 WindowPlacement wpTags;
 
+extern Widget shells[];
+extern Boolean shellUp[];
+
 #define SOLID 0
 #define OUTLINE 1
 Pixmap pieceBitmap[2][(int)BlackPawn];
@@ -619,6 +623,7 @@ MenuItem fileMenu[] = {
 MenuItem editMenu[] = {
     {N_("Copy Game    Ctrl+C"),        "Copy Game", CopyGameProc},
     {N_("Copy Position Ctrl+Shift+C"), "Copy Position", CopyPositionProc},
+    {N_("Copy Game List"),        "Copy Game List", CopyGameListProc},
     {"----", NULL, NothingProc},
     {N_("Paste Game    Ctrl+V"),        "Paste Game", PasteGameProc},
     {N_("Paste Position Ctrl+Shift+V"), "Paste Position", PastePositionProc},
@@ -646,6 +651,7 @@ MenuItem viewMenu[] = {
     {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},
+    {N_("ICS text menu"), "ICStex", IcsTextProc},
     {"----", NULL, NothingProc},
     {N_("Tags"),             "Show Tags", EditTagsProc},
     {N_("Comments"),         "Show Comments", EditCommentProc},
@@ -694,6 +700,8 @@ MenuItem actionMenu[] = {
 };
 
 MenuItem engineMenu[] = {
+    {N_("Load New Engine ..."), "Load Engine", LoadEngineProc},
+    {"----", NULL, NothingProc},
     {N_("Engine #1 Settings ..."), "Engine #1 Settings", FirstSettingsProc},
     {N_("Engine #2 Settings ..."), "Engine #2 Settings", SecondSettingsProc},
     {"----", NULL, NothingProc},
@@ -913,6 +921,7 @@ XtActionsRec boardActions[] = {
     { "CopyPositionProc", CopyPositionProc },
     { "PastePositionProc", PastePositionProc },
     { "CopyGameProc", CopyGameProc },
+    { "CopyGameListProc", CopyGameListProc },
     { "PasteGameProc", PasteGameProc },
     { "SaveGameProc", SaveGameProc },
     { "SavePositionProc", SavePositionProc },
@@ -947,6 +956,7 @@ XtActionsRec boardActions[] = {
     { "AdjuWhiteProc", AdjuWhiteProc },
     { "AdjuBlackProc", AdjuBlackProc },
     { "AdjuDrawProc", AdjuDrawProc },
+    { "TypeInProc", TypeInProc },
     { "EnterKeyProc", EnterKeyProc },
     { "UpKeyProc", UpKeyProc },
     { "DownKeyProc", DownKeyProc },
@@ -1002,7 +1012,6 @@ XtActionsRec boardActions[] = {
     { "NothingProc", NothingProc },
     { "CommentClick", (XtActionProc) CommentClick },
     { "CommentPopDown", (XtActionProc) CommentPopDown },
-    { "EditCommentPopDown", (XtActionProc) EditCommentPopDown },
     { "TagsPopDown", (XtActionProc) TagsPopDown },
     { "ErrorPopDown", (XtActionProc) ErrorPopDown },
     { "ICSInputBoxPopDown", (XtActionProc) ICSInputBoxPopDown },
@@ -1257,8 +1266,8 @@ xpm_closest_to(dirname, size, ext)
     fprintf(stderr, _("\
 Warning: No DIR structure found on this system --\n\
          Unable to autosize for XPM/XIM pieces.\n\
-   Please report this error to frankm@hiwaay.net.\n\
-   Include system type & operating system in message.\n"));
+   Please report this error to %s.\n\
+   Include system type & operating system in message.\n"), PACKAGE_BUGREPORT););
     return size;
 }
 #endif /* HAVE_DIR_STRUCT */
@@ -1504,7 +1513,7 @@ SaveFontArg(FILE *f, ArgDescriptor *ad)
        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]);
+    fprintf(f, OPTCHAR "%s" SEPCHAR "\"size%d:%s\"\n", ad->argName, i, fontTable[n][i]);
 }
 
 void
@@ -1537,7 +1546,7 @@ ParseCommPortSettings(char *s)
 }
 
 extern Widget engineOutputShell;
-extern Widget tagsShell, editTagsShell;
+
 void
 GetActualPlacement(Widget wg, WindowPlacement *wp)
 {
@@ -1569,10 +1578,8 @@ GetWindowCoords()
   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(shellUp[1]) GetActualPlacement(shells[1], &wpComment);
+  if(shellUp[2]) GetActualPlacement(shells[2], &wpTags);
 }
 
 void
@@ -1895,6 +1902,28 @@ int MakeColors()
     return forceMono;
 }
 
+void
+CreateAnyPieces()
+{   // [HGM] taken out of main
+#if HAVE_LIBXPM
+    if (appData.monoMode && // [HGM] no sense to go on to certain doom
+       (appData.bitmapDirectory == NULL || appData.bitmapDirectory[0] == NULLCHAR))
+           appData.bitmapDirectory = DEF_BITMAP_DIR;
+
+    if (appData.bitmapDirectory[0] != NULLCHAR) {
+      CreatePieces();
+    } else {
+      CreateXPMPieces();
+      CreateXPMBoard(appData.liteBackTextureFile, 1);
+      CreateXPMBoard(appData.darkBackTextureFile, 0);
+    }
+#else
+    CreateXIMPieces();
+    /* Create regular pieces */
+    if (!useImages) CreatePieces();
+#endif
+}
+
 int
 main(argc, argv)
      int argc;
@@ -1953,8 +1982,8 @@ main(argc, argv)
 
     { // [HGM] initstring: kludge to fix bad bug. expand '\n' characters in init string and computer string.
        static char buf[MSG_SIZ];
-       EscapeExpand(buf, appData.initString);
-       appData.initString = strdup(buf);
+       EscapeExpand(buf, appData.firstInitString);
+       appData.firstInitString = strdup(buf);
        EscapeExpand(buf, appData.secondInitString);
        appData.secondInitString = strdup(buf);
        EscapeExpand(buf, appData.firstComputerString);
@@ -2084,6 +2113,7 @@ XBoard square size (hint): %d\n\
            fprintf(stderr, _("Closest %s size: %d\n"), IMAGE_EXT, squareSize);
        }
     }
+    defaultLineGap = lineGap;
     if(appData.overrideLineGap >= 0) lineGap = appData.overrideLineGap;
 
     /* [HR] height treated separately (hacked) */
@@ -2134,10 +2164,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;
+       appData.monoMode = True;
     }
 
     if (appData.lowTimeWarning && !appData.monoMode) {
@@ -2547,19 +2574,7 @@ XBoard square size (hint): %d\n\
 
     CreateGCs(False);
     CreateGrid();
-#if HAVE_LIBXPM
-    if (appData.bitmapDirectory[0] != NULLCHAR) {
-      CreatePieces();
-    } else {
-      CreateXPMPieces();
-      CreateXPMBoard(appData.liteBackTextureFile, 1);
-      CreateXPMBoard(appData.darkBackTextureFile, 0);
-    }
-#else
-    CreateXIMPieces();
-    /* Create regular pieces */
-    if (!useImages) CreatePieces();
-#endif
+    CreateAnyPieces();
 
     CreatePieceMenus();
 
@@ -2580,6 +2595,8 @@ XBoard square size (hint): %d\n\
     XtAddEventHandler(boardWidget, ExposureMask|PointerMotionMask, False,
                      (XtEventHandler) EventProc, NULL);
     /* end why */
+    XtAddEventHandler(formWidget, KeyPressMask, False,
+                     (XtEventHandler) MoveTypeInProc, NULL);
 
     /* [AS] Restore layout */
     if( wpMoveHistory.visible ) {
@@ -2616,7 +2633,8 @@ XBoard square size (hint): %d\n\
     }
     gameInfo.boardWidth = 0; // [HGM] pieces: kludge to ensure InitPosition() calls InitDrawingSizes()
     InitPosition(TRUE);
-    XtSetKeyboardFocus(shellWidget, formWidget);
+//    XtSetKeyboardFocus(shellWidget, formWidget);
+    XSetInputFocus(xDisplay, XtWindow(formWidget), RevertToPointerRoot, CurrentTime);
 
     XtAppMainLoop(appContext);
     if (appData.debugMode) fclose(debugFP); // [DM] debug
@@ -2711,7 +2729,6 @@ void
 ResetFrontEnd()
 {
     CommentPopDown();
-    EditCommentPopDown();
     TagsPopDown();
     return;
 }
@@ -2776,10 +2793,12 @@ Enables icsEnables[] = {
     { "menuOptions.Hide Thinking", False },
     { "menuOptions.Ponder Next Move", False },
 #endif
-    { "menuEngine.Engine #1 Settings", False },
 #endif
+    { "menuEngine.Engine #1 Settings", False },
     { "menuEngine.Engine #2 Settings", False },
+    { "menuEngine.Load Engine", False },
     { "menuEdit.Annotate", False },
+    { "menuOptions.Match", False },
     { NULL, False }
 };
 
@@ -2793,6 +2812,7 @@ Enables ncpEnables[] = {
     { "menuMode.Two Machines", False },
     { "menuMode.Machine Match", False },
     { "menuMode.ICS Client", False },
+    { "menuView.ICStex", False },
     { "menuView.ICS Input Box", False },
     { "Action", False },
     { "menuEdit.Revert", False },
@@ -2801,10 +2821,10 @@ Enables ncpEnables[] = {
     { "menuEngine.Engine #2 Settings", False },
     { "menuEngine.Move Now", False },
     { "menuEngine.Retract Move", False },
+    { "menuOptions.ICS", False },
 #ifndef OPTIONSDIALOG
     { "menuOptions.Auto Flag", False },
     { "menuOptions.Auto Flip View", False },
-    { "menuOptions.ICS", False },
 //    { "menuOptions.ICS Alarm", False },
     { "menuOptions.Move Sound", False },
     { "menuOptions.Hide Thinking", False },
@@ -2818,6 +2838,7 @@ Enables ncpEnables[] = {
 
 Enables gnuEnables[] = {
     { "menuMode.ICS Client", False },
+    { "menuView.ICStex", False },
     { "menuView.ICS Input Box", False },
     { "menuAction.Accept", False },
     { "menuAction.Decline", False },
@@ -2836,6 +2857,20 @@ Enables gnuEnables[] = {
 
     { "menuFile.Mail Move", False },
     { "menuFile.Reload CMail Message", False },
+    // [HGM] The following have been added to make a switch from ncp to GNU mode possible
+    { "menuMode.Machine White", True },
+    { "menuMode.Machine Black", True },
+    { "menuMode.Analysis Mode", True },
+    { "menuMode.Analyze File", True },
+    { "menuMode.Two Machines", True },
+    { "menuMode.Machine Match", True },
+    { "menuEngine.Engine #1 Settings", True },
+    { "menuEngine.Engine #2 Settings", True },
+    { "menuEngine.Hint", True },
+    { "menuEngine.Book", True },
+    { "menuEngine.Move Now", True },
+    { "menuEngine.Retract Move", True },
+    { "Action", True },
     { NULL, False }
 };
 
@@ -2919,8 +2954,10 @@ void SetICSMode()
   SetMenuEnables(icsEnables);
 
 #if ZIPPY
-  if (appData.zippyPlay && !appData.noChessProgram)   /* [DM] icsEngineAnalyze */
+  if (appData.zippyPlay && !appData.noChessProgram) { /* [DM] icsEngineAnalyze */
      XtSetSensitive(XtNameToWidget(menuBarWidget, "menuMode.Analysis Mode"), True);
+     XtSetSensitive(XtNameToWidget(menuBarWidget, "menuEngine.Engine #1 Settings"), True);
+  }
 #endif
 }
 
@@ -3055,7 +3092,6 @@ FindFont(pattern, targetPxlSize)
     char *def_string, *base_fnt_lst, strInt[3];
     XFontSet fntSet;
     XFontStruct **fnt_list;
-
     base_fnt_lst = calloc(1, strlen(pattern) + 3);
     snprintf(strInt, sizeof(strInt)/sizeof(strInt[0]), "%d", targetPxlSize);
     p = strstr(pattern, "--");
@@ -3139,6 +3175,7 @@ void DeleteGCs()
     XtReleaseGC(shellWidget, highlineGC);
     XtReleaseGC(shellWidget, lightSquareGC);
     XtReleaseGC(shellWidget, darkSquareGC);
+    XtReleaseGC(shellWidget, lineGC);
     if (appData.monoMode) {
        if (DefaultDepth(xDisplay, xScreen) == 1) {
            XtReleaseGC(shellWidget, wbPieceGC);
@@ -3173,10 +3210,6 @@ void CreateGCs(int redo)
     DeleteGCs(); // called a second time; clean up old GCs first
   } else { // [HGM] grid and font GCs created on first call only
     gc_values.foreground = XBlackPixel(xDisplay, xScreen);
-    gc_values.background = XBlackPixel(xDisplay, xScreen);
-    lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
-
-    gc_values.foreground = XBlackPixel(xDisplay, xScreen);
     gc_values.background = XWhitePixel(xDisplay, xScreen);
     coordGC = XtGetGC(shellWidget, value_mask, &gc_values);
     XSetFont(xDisplay, coordGC, coordFontID);
@@ -3187,6 +3220,10 @@ void CreateGCs(int redo)
     countGC = XtGetGC(shellWidget, value_mask, &gc_values);
     XSetFont(xDisplay, countGC, countFontID);
   }
+    gc_values.foreground = XBlackPixel(xDisplay, xScreen);
+    gc_values.background = XBlackPixel(xDisplay, xScreen);
+    lineGC = XtGetGC(shellWidget, value_mask, &gc_values);
+
     if (appData.monoMode) {
        gc_values.foreground = XWhitePixel(xDisplay, xScreen);
        gc_values.background = XWhitePixel(xDisplay, xScreen);
@@ -3435,6 +3472,8 @@ void CreateXIMPieces()
     XSynchronize(xDisplay, False); /* Work-around for xlib/xt buffering bug */
 }
 
+static VariantClass oldVariant = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
+
 #if HAVE_LIBXPM
 void CreateXPMBoard(char *s, int kind)
 {
@@ -3593,6 +3632,7 @@ void CreateXPMPieces()
        xpmJailSquare = xpmLightSquare;
        fprintf(stderr, _("Done.\n"));
     }
+    oldVariant = -1; // kludge to force re-makig of animation masks
     XSynchronize(xDisplay, False); /* Work-around for xlib/xt
                                      buffering bug */
 }
@@ -3758,10 +3798,10 @@ void CreateMenuBarPopup(parent, name, mb)
     mi = mb->mi;
     while (mi->string != NULL) {
        if (strcmp(mi->string, "----") == 0) {
-           entry = XtCreateManagedWidget(mi->string, smeLineObjectClass,
+         entry = XtCreateManagedWidget(_(mi->string), smeLineObjectClass,
                                          menu, args, j);
        } else {
-          XtSetArg(args[j], XtNlabel, XtNewString(mi->string));
+          XtSetArg(args[j], XtNlabel, XtNewString(_(mi->string)));
            entry = XtCreateManagedWidget(mi->ref, smeBSBObjectClass,
                                          menu, args, j+1);
            XtAddCallback(entry, XtNcallback,
@@ -3799,7 +3839,7 @@ Widget CreateMenuBar(mb)
            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++;
@@ -3947,7 +3987,8 @@ void PieceMenuPopup(w, event, params, num_params)
      String *params;
      Cardinal *num_params;
 {
-    String whichMenu; int menuNr;
+    String whichMenu; int menuNr = -2;
+    shiftKey = strcmp(params[0], "menuW"); // used to indicate black
     if (event->type == ButtonRelease)
         menuNr = RightClick(Release, event->xbutton.x, event->xbutton.y, &pmFromX, &pmFromY);
     else if (event->type == ButtonPress)
@@ -4762,6 +4803,7 @@ void HandleUserMove(w, event, prms, nprms)
 void AnimateUserMove (Widget w, XEvent * event,
                      String * params, Cardinal * nParams)
 {
+    if(!PromoScroll(event->xmotion.x, event->xmotion.y))
     DragPieceMove(event->xmotion.x, event->xmotion.y);
 }
 
@@ -4771,170 +4813,6 @@ void HandlePV (Widget w, XEvent * event,
     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;
-     XtCallbackProc callback;
-     int lines;
-{
-    Arg args[16];
-    Widget shell, layout, form, edit, b_ok, b_cancel, b_clear, b_close, b_edit;
-    Dimension bw_width;
-    int j;
-
-    j = 0;
-    XtSetArg(args[j], XtNwidth, &bw_width);  j++;
-    XtGetValues(boardWidget, args, j);
-
-    j = 0;
-    XtSetArg(args[j], XtNresizable, True);  j++;
-#if TOPLEVEL
-    shell =
-      XtCreatePopupShell(name, topLevelShellWidgetClass,
-                        shellWidget, args, j);
-#else
-    shell =
-      XtCreatePopupShell(name, transientShellWidgetClass,
-                        shellWidget, args, j);
-#endif
-    layout =
-      XtCreateManagedWidget(layoutName, formWidgetClass, shell,
-                           layoutArgs, XtNumber(layoutArgs));
-    form =
-      XtCreateManagedWidget("form", formWidgetClass, layout,
-                           formArgs, XtNumber(formArgs));
-
-    j = 0;
-    if (mutable) {
-       XtSetArg(args[j], XtNeditType, XawtextEdit);  j++;
-       XtSetArg(args[j], XtNuseStringInPlace, False);  j++;
-    }
-    XtSetArg(args[j], XtNstring, text);  j++;
-    XtSetArg(args[j], XtNtop, XtChainTop);  j++;
-    XtSetArg(args[j], XtNbottom, XtChainBottom);  j++;
-    XtSetArg(args[j], XtNleft, XtChainLeft);  j++;
-    XtSetArg(args[j], XtNright, XtChainRight);  j++;
-    XtSetArg(args[j], XtNresizable, True);  j++;
-    XtSetArg(args[j], XtNwidth, bw_width);  j++; /*force wider than buttons*/
-    /* !!Work around an apparent bug in XFree86 4.0.1 (X11R6.4.3) */
-    XtSetArg(args[j], XtNscrollVertical, XawtextScrollAlways);  j++;
-    XtSetArg(args[j], XtNautoFill, True);  j++;
-    XtSetArg(args[j], XtNwrap, XawtextWrapWord); j++;
-    edit =
-      XtCreateManagedWidget("text", asciiTextWidgetClass, form, args, j);
-    XtOverrideTranslations(edit, XtParseTranslationTable(commentTranslations));
-
-    if (mutable) {
-       j = 0;
-       XtSetArg(args[j], XtNfromVert, edit);  j++;
-       XtSetArg(args[j], XtNtop, XtChainBottom); j++;
-       XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
-       XtSetArg(args[j], XtNleft, XtChainLeft); j++;
-       XtSetArg(args[j], XtNright, XtChainLeft); j++;
-       b_ok =
-         XtCreateManagedWidget(_("ok"), commandWidgetClass, form, args, j);
-       XtAddCallback(b_ok, XtNcallback, callback, (XtPointer) 0);
-
-       j = 0;
-       XtSetArg(args[j], XtNfromVert, edit);  j++;
-       XtSetArg(args[j], XtNfromHoriz, b_ok);  j++;
-       XtSetArg(args[j], XtNtop, XtChainBottom); j++;
-       XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
-       XtSetArg(args[j], XtNleft, XtChainLeft); j++;
-       XtSetArg(args[j], XtNright, XtChainLeft); j++;
-       b_cancel =
-         XtCreateManagedWidget(_("cancel"), commandWidgetClass, form, args, j);
-       XtAddCallback(b_cancel, XtNcallback, callback, (XtPointer) 0);
-
-       j = 0;
-       XtSetArg(args[j], XtNfromVert, edit);  j++;
-       XtSetArg(args[j], XtNfromHoriz, b_cancel);  j++;
-       XtSetArg(args[j], XtNtop, XtChainBottom); j++;
-       XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
-       XtSetArg(args[j], XtNleft, XtChainLeft); j++;
-       XtSetArg(args[j], XtNright, XtChainLeft); j++;
-       b_clear =
-         XtCreateManagedWidget(_("clear"), commandWidgetClass, form, args, j);
-       XtAddCallback(b_clear, XtNcallback, callback, (XtPointer) 0);
-    } else {
-       j = 0;
-       XtSetArg(args[j], XtNfromVert, edit);  j++;
-       XtSetArg(args[j], XtNtop, XtChainBottom); j++;
-       XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
-       XtSetArg(args[j], XtNleft, XtChainLeft); j++;
-       XtSetArg(args[j], XtNright, XtChainLeft); j++;
-       b_close =
-         XtCreateManagedWidget(_("close"), commandWidgetClass, form, args, j);
-       XtAddCallback(b_close, XtNcallback, callback, (XtPointer) 0);
-
-       j = 0;
-       XtSetArg(args[j], XtNfromVert, edit);  j++;
-       XtSetArg(args[j], XtNfromHoriz, b_close);  j++;
-       XtSetArg(args[j], XtNtop, XtChainBottom); j++;
-       XtSetArg(args[j], XtNbottom, XtChainBottom); j++;
-       XtSetArg(args[j], XtNleft, XtChainLeft); j++;
-       XtSetArg(args[j], XtNright, XtChainLeft); j++;
-       b_edit =
-         XtCreateManagedWidget(_("edit"), commandWidgetClass, form, args, j);
-       XtAddCallback(b_edit, XtNcallback, callback, (XtPointer) 0);
-    }
-
-    XtRealizeWidget(shell);
-
-    if (commentX == -1) {
-       int xx, yy;
-       Window junk;
-       Dimension pw_height;
-       Dimension ew_height;
-
-       j = 0;
-       XtSetArg(args[j], XtNheight, &ew_height);  j++;
-       XtGetValues(edit, args, j);
-
-       j = 0;
-       XtSetArg(args[j], XtNheight, &pw_height);  j++;
-       XtGetValues(shell, args, j);
-       commentH = pw_height + (lines - 1) * ew_height;
-       commentW = bw_width - 16;
-
-       XSync(xDisplay, False);
-#ifdef NOTDEF
-       /* This code seems to tickle an X bug if it is executed too soon
-          after xboard starts up.  The coordinates get transformed as if
-          the main window was positioned at (0, 0).
-          */
-       XtTranslateCoords(shellWidget,
-                         (bw_width - commentW) / 2, 0 - commentH / 2,
-                         &commentX, &commentY);
-#else  /*!NOTDEF*/
-        XTranslateCoordinates(xDisplay, XtWindow(shellWidget),
-                             RootWindowOfScreen(XtScreen(shellWidget)),
-                             (bw_width - commentW) / 2, 0 - commentH / 2,
-                             &xx, &yy, &junk);
-       commentX = xx;
-       commentY = yy;
-#endif /*!NOTDEF*/
-       if (commentY < 0) commentY = 0; /*avoid positioning top offscreen*/
-    }
-
-    if(wpComment.width > 0) {
-      commentX = wpComment.x;
-      commentY = wpComment.y;
-      commentW = wpComment.width;
-      commentH = wpComment.height;
-    }
-
-    j = 0;
-    XtSetArg(args[j], XtNheight, commentH);  j++;
-    XtSetArg(args[j], XtNwidth, commentW);  j++;
-    XtSetArg(args[j], XtNx, commentX);  j++;
-    XtSetArg(args[j], XtNy, commentY);  j++;
-    XtSetValues(shell, args, j);
-    XtSetKeyboardFocus(shell, edit);
-
-    return shell;
-}
-
 static int savedIndex;  /* gross that this is global */
 
 void CommentClick (Widget w, XEvent * event, String * params, Cardinal * nParams)
@@ -4955,89 +4833,9 @@ void EditCommentPopUp(index, title, text)
      int index;
      char *title, *text;
 {
-    Widget edit;
-    Arg args[16];
-    int j;
-
     savedIndex = index;
     if (text == NULL) text = "";
-
-    if (editShell == NULL) {
-       editShell =
-         CommentCreate(title, text, True, EditCommentCallback, 4);
-       XtRealizeWidget(editShell);
-       CatchDeleteWindow(editShell, "EditCommentPopDown");
-    } else {
-       edit = XtNameToWidget(editShell, "*form.text");
-       j = 0;
-       XtSetArg(args[j], XtNstring, text); j++;
-       XtSetValues(edit, args, j);
-       j = 0;
-       XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
-       XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
-       XtSetValues(editShell, args, j);
-    }
-
-    XtPopup(editShell, XtGrabNone);
-
-    editUp = True;
-    j = 0;
-    XtSetArg(args[j], XtNleftBitmap, xMarkPixmap); j++;
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
-               args, j);
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
-               args, j);
-}
-
-void EditCommentCallback(w, client_data, call_data)
-     Widget w;
-     XtPointer client_data, call_data;
-{
-    String name, val;
-    Arg args[16];
-    int j;
-    Widget edit;
-
-    j = 0;
-    XtSetArg(args[j], XtNlabel, &name);  j++;
-    XtGetValues(w, args, j);
-
-    if (strcmp(name, _("ok")) == 0) {
-       edit = XtNameToWidget(editShell, "*form.text");
-       j = 0;
-       XtSetArg(args[j], XtNstring, &val); j++;
-       XtGetValues(edit, args, j);
-       ReplaceComment(savedIndex, val);
-       EditCommentPopDown();
-    } else if (strcmp(name, _("cancel")) == 0) {
-       EditCommentPopDown();
-    } else if (strcmp(name, _("clear")) == 0) {
-       edit = XtNameToWidget(editShell, "*form.text");
-       XtCallActionProc(edit, "select-all", NULL, NULL, 0);
-       XtCallActionProc(edit, "kill-selection", NULL, NULL, 0);
-    }
-}
-
-void EditCommentPopDown()
-{
-    Arg args[16];
-    int j;
-
-    if (!editUp) return;
-    j = 0;
-    XtSetArg(args[j], XtNx, &commentX); j++;
-    XtSetArg(args[j], XtNy, &commentY); j++;
-    XtSetArg(args[j], XtNheight, &commentH); j++;
-    XtSetArg(args[j], XtNwidth, &commentW); j++;
-    XtGetValues(editShell, args, j);
-    XtPopdown(editShell);
-    editUp = False;
-    j = 0;
-    XtSetArg(args[j], XtNleftBitmap, None); j++;
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"),
-               args, j);
-    XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"),
-               args, j);
+    NewCommentPopup(title, text, index);
 }
 
 void ICSInputBoxPopUp()
@@ -5072,74 +4870,19 @@ void ICSInputBoxPopDown()
 void CommentPopUp(title, text)
      char *title, *text;
 {
-    Arg args[16];
-    int j;
-    Widget edit;
-
     savedIndex = currentMove; // [HGM] vari
-    if (commentShell == NULL) {
-       commentShell =
-         CommentCreate(title, text, False, CommentCallback, 4);
-       XtRealizeWidget(commentShell);
-       CatchDeleteWindow(commentShell, "CommentPopDown");
-    } else {
-       edit = XtNameToWidget(commentShell, "*form.text");
-       j = 0;
-       XtSetArg(args[j], XtNstring, text); j++;
-       XtSetValues(edit, args, j);
-       j = 0;
-       XtSetArg(args[j], XtNiconName, (XtArgVal) title);   j++;
-       XtSetArg(args[j], XtNtitle, (XtArgVal) title);      j++;
-       XtSetValues(commentShell, args, j);
-    }
-
-    XtPopup(commentShell, XtGrabNone);
-    XSync(xDisplay, False);
-
-    commentUp = True;
-}
-
-void CommentCallback(w, client_data, call_data)
-     Widget w;
-     XtPointer client_data, call_data;
-{
-    String name;
-    Arg args[16];
-    int j;
-
-    j = 0;
-    XtSetArg(args[j], XtNlabel, &name);  j++;
-    XtGetValues(w, args, j);
-
-    if (strcmp(name, _("close")) == 0) {
-       CommentPopDown();
-    } else if (strcmp(name, _("edit")) == 0) {
-       CommentPopDown();
-       EditCommentEvent();
-    }
+    NewCommentPopup(title, text, currentMove);
 }
 
-
 void CommentPopDown()
 {
-    Arg args[16];
-    int j;
-
-    if (!commentUp) return;
-    j = 0;
-    XtSetArg(args[j], XtNx, &commentX); j++;
-    XtSetArg(args[j], XtNy, &commentY); j++;
-    XtSetArg(args[j], XtNwidth, &commentW); j++;
-    XtSetArg(args[j], XtNheight, &commentH); j++;
-    XtGetValues(commentShell, args, j);
-    XtPopdown(commentShell);
-    XSync(xDisplay, False);
-    commentUp = False;
+    PopDown(1);
 }
 
-void FileNamePopUp(label, def, proc, openMode)
+void FileNamePopUp(label, def, filter, proc, openMode)
      char *label;
      char *def;
+     char *filter;
      FileProc proc;
      char *openMode;
 {
@@ -5150,7 +4893,7 @@ void FileNamePopUp(label, def, proc, openMode)
        int index; // this is not supported yet
        FILE *f;
        if(f = XsraSelFile(shellWidget, label, NULL, NULL, "could not open: ",
-                          def, openMode, NULL, &name))
+                          (def[0] ? def : NULL), filter, openMode, NULL, &name))
          (void) (*fileProc)(f, index=0, name);
     }
 }
@@ -5614,7 +5357,7 @@ void LoadGameProc(w, event, prms, nprms)
     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
        Reset(FALSE, TRUE);
     }
-    FileNamePopUp(_("Load game file name?"), "", LoadGamePopUp, "rb");
+    FileNamePopUp(_("Load game file name?"), "", ".pgn .game", LoadGamePopUp, "rb");
 }
 
 void LoadNextGameProc(w, event, prms, nprms)
@@ -5680,7 +5423,7 @@ void LoadPositionProc(w, event, prms, nprms)
     if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
        Reset(FALSE, TRUE);
     }
-    FileNamePopUp(_("Load position file name?"), "", LoadPosition, "rb");
+    FileNamePopUp(_("Load position file name?"), "", ".fen .epd .pos", LoadPosition, "rb");
 }
 
 void SaveGameProc(w, event, prms, nprms)
@@ -5691,6 +5434,7 @@ void SaveGameProc(w, event, prms, nprms)
 {
     FileNamePopUp(_("Save game file name?"),
                  DefaultFileName(appData.oldSaveStyle ? "game" : "pgn"),
+                 appData.oldSaveStyle ? ".game" : ".pgn",
                  SaveGame, "a");
 }
 
@@ -5702,6 +5446,7 @@ void SavePositionProc(w, event, prms, nprms)
 {
     FileNamePopUp(_("Save position file name?"),
                  DefaultFileName(appData.oldSaveStyle ? "pos" : "fen"),
+                 appData.oldSaveStyle ? ".pos" : ".fen",
                  SavePosition, "a");
 }
 
@@ -5873,20 +5618,10 @@ SendGameSelection(Widget w, Atom *selection, Atom *target,
   }
 }
 
-/* note: when called from menu all parameters are NULL, so no clue what the
- * Widget which was clicked on was, or what the click event was
- */
-void CopyGameProc(w, event, prms, nprms)
-  Widget w;
-  XEvent *event;
-  String *prms;
-  Cardinal *nprms;
+void CopySomething()
 {
   int ret;
 
-  ret = SaveGameToFile(gameCopyFilename, FALSE);
-  if (!ret) return;
-
   /*
    * Set both PRIMARY (the selection) and CLIPBOARD, since we don't
    * have a notion of a game that is selected but not copied.
@@ -5904,6 +5639,33 @@ void CopyGameProc(w, event, prms, nprms)
                 NULL/* transfer_done_proc */);
 }
 
+/* note: when called from menu all parameters are NULL, so no clue what the
+ * Widget which was clicked on was, or what the click event was
+ */
+void CopyGameProc(w, event, prms, nprms)
+  Widget w;
+  XEvent *event;
+  String *prms;
+  Cardinal *nprms;
+{
+  int ret;
+
+  ret = SaveGameToFile(gameCopyFilename, FALSE);
+  if (!ret) return;
+
+  CopySomething();
+}
+
+void CopyGameListProc(w, event, prms, nprms)
+  Widget w;
+  XEvent *event;
+  String *prms;
+  Cardinal *nprms;
+{
+  if(!SaveGameListAsText(fopen(gameCopyFilename, "w"))) return;
+  CopySomething();
+}
+
 /* function called when the data to Paste is ready */
 static void
 PasteGameCB(Widget w, XtPointer client_data, Atom *selection,
@@ -6052,7 +5814,7 @@ void AnalyzeFileProc(w, event, prms, nprms)
       ShowThinkingProc(w,event,prms,nprms);
 #endif
     AnalyzeFileEvent();
-    FileNamePopUp(_("File to analyze"), "", LoadGamePopUp, "rb");
+    FileNamePopUp(_("File to analyze"), "", ".pgn .game", LoadGamePopUp, "rb");
     AnalysisPeriodicEvent(1);
 }
 
@@ -6072,11 +5834,8 @@ void MatchProc(w, event, prms, nprms)
      Cardinal *nprms;
 {
     if(gameMode != BeginningOfGame) { DisplayError(_("You can only start a match from the initial position."), 0); return; }
-    matchMode = 2; // This is back-end, really\r
-    appData.matchGames = appData.defaultMatchGames;\r
-    matchGame = 1;\r
-    first.matchWins = second.matchWins = 0;\r
-    TwoMachinesEvent();
+    appData.matchGames = appData.defaultMatchGames;
+    MatchEvent(2);\r
 }
 
 void IcsClientProc(w, event, prms, nprms)
@@ -6121,11 +5880,15 @@ void EditCommentProc(w, event, prms, nprms)
      String *prms;
      Cardinal *nprms;
 {
-    if (editUp) {
-       EditCommentPopDown();
-    } else {
+    Arg args[5];
+    int j;
+    if (PopDown(1)) { // popdown succesful
+       j = 0;
+       XtSetArg(args[j], XtNleftBitmap, None); j++;
+       XtSetValues(XtNameToWidget(menuBarWidget, "menuEdit.Edit Comment"), args, j);
+       XtSetValues(XtNameToWidget(menuBarWidget, "menuView.Show Comments"), args, j);
+    } else // was not up
        EditCommentEvent();
-    }
 }
 
 void IcsInputBoxProc(w, event, prms, nprms)
@@ -7001,6 +6764,8 @@ void DisplayMessage(message, extMessage)
        };
     };
 
+    safeStrCpy(lastMsg, message, MSG_SIZ); // [HGM] make available
+
   /* 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 */
@@ -7856,50 +7621,40 @@ int OpenTCP(host, port, pr)
 #if OMIT_SOCKETS
     DisplayFatalError(_("Socket support is not configured in"), 0, 2);
 #else  /* !OMIT_SOCKETS */
+    struct addrinfo hints;
+    struct addrinfo *ais, *ai;
+    int error;
     int s;
-    struct sockaddr_in sa;
-    struct hostent     *hp;
-    unsigned short uport;
     ChildProc *cp;
 
-    if ((s = socket(AF_INET, SOCK_STREAM, 6)) < 0) {
-       return errno;
-    }
-
-    memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
-    sa.sin_family = AF_INET;
-    sa.sin_addr.s_addr = INADDR_ANY;
-    uport = (unsigned short) 0;
-    sa.sin_port = htons(uport);
-    if (bind(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
-       return errno;
-    }
-
-    memset((char *) &sa, (int)0, sizeof(struct sockaddr_in));
-    if (!(hp = gethostbyname(host))) {
-       int b0, b1, b2, b3;
-       if (sscanf(host, "%d.%d.%d.%d", &b0, &b1, &b2, &b3) == 4) {
-           hp = (struct hostent *) calloc(1, sizeof(struct hostent));
-           hp->h_addrtype = AF_INET;
-           hp->h_length = 4;
-           hp->h_addr_list = (char **) calloc(2, sizeof(char *));
-           hp->h_addr_list[0] = (char *) malloc(4);
-           hp->h_addr_list[0][0] = b0;
-           hp->h_addr_list[0][1] = b1;
-           hp->h_addr_list[0][2] = b2;
-           hp->h_addr_list[0][3] = b3;
-       } else {
-           return ENOENT;
-       }
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+
+    error = getaddrinfo(host, port, &hints, &ais);
+    if (error != 0) {
+      /* a getaddrinfo error is not an errno, so can't return it */
+      fprintf(debugFP, "getaddrinfo(%s, %s): %s\n",
+             host, port, gai_strerror(error));
+      return ENOENT;
+    }
+     
+    for (ai = ais; ai != NULL; ai = ai->ai_next) {
+      if ((s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
+       error = errno;
+       continue;
+      }
+      if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
+       error = errno;
+       continue;
+      }
+      error = 0;
+      break;
     }
-    sa.sin_family = hp->h_addrtype;
-    uport = (unsigned short) atoi(port);
-    sa.sin_port = htons(uport);
-    memcpy((char *) &sa.sin_addr, hp->h_addr, hp->h_length);
+    freeaddrinfo(ais);
 
-    if (connect(s, (struct sockaddr *) &sa,
-               sizeof(struct sockaddr_in)) < 0) {
-       return errno;
+    if (error != 0) {
+      return error;
     }
 
     cp = (ChildProc *) calloc(1, sizeof(ChildProc));
@@ -7908,7 +7663,6 @@ int OpenTCP(host, port, pr)
     cp->fdFrom = s;
     cp->fdTo = s;
     *pr = (ProcRef) cp;
-
 #endif /* !OMIT_SOCKETS */
 
     return 0;
@@ -8265,11 +8019,10 @@ InitAnimState (anim, info)
 static void
 CreateAnimVars ()
 {
-  static VariantClass old = (VariantClass) -1; // [HGM] pieces: redo every time variant changes
   XWindowAttributes info;
 
-  if (xpmDone && gameInfo.variant == old) return;
-  if(xpmDone) old = gameInfo.variant; // first time pieces might not be created yet
+  if (xpmDone && gameInfo.variant == oldVariant) return;
+  if(xpmDone) oldVariant = gameInfo.variant; // first time pieces might not be created yet
   XGetWindowAttributes(xDisplay, xBoardWindow, &info);
 
   InitAnimState(&game, &info);
@@ -8277,8 +8030,7 @@ CreateAnimVars ()
 
   /* For XPM pieces, we need bitmaps to use as masks. */
   if (useImages)
-    CreateAnimMasks(info.depth);
-   xpmDone = 1;
+    CreateAnimMasks(info.depth), xpmDone = 1;
 }
 
 #ifndef HAVE_USLEEP
@@ -8823,6 +8575,16 @@ DragPieceBegin(x, y)
     }
 }
 
+void
+ChangeDragPiece(ChessSquare piece)
+{
+  Pixmap mask;
+  player.dragPiece = piece;
+  /* The piece will be drawn using its own bitmap as a matte   */
+  SelectGCMask(piece, &player.pieceGC, &player.outlineGC, &mask);
+  XSetClipMask(xDisplay, player.pieceGC, mask);
+}
+
 static void
 DragPieceMove(x, y)
      int x; int y;