added some comments and formated code
[xboard.git] / xboard.c
index 7b1fe43..79b61a2 100644 (file)
--- a/xboard.c
+++ b/xboard.c
@@ -1347,7 +1347,7 @@ XtResource clientResources[] = {
        XtRImmediate, (XtPointer) "xboard.debug"},
     { "engineDebugOutput", "engineDebugOutput", XtRInt,
        sizeof(int), XtOffset(AppDataPtr, engineComments),
-       XtRImmediate, (XtPointer) 0},
+       XtRImmediate, (XtPointer) 1},
     { "noGUI", "noGUI", XtRBoolean,
        sizeof(Boolean), XtOffset(AppDataPtr, noGUI),
        XtRImmediate, (XtPointer) 0},
@@ -1401,6 +1401,12 @@ XtResource clientResources[] = {
     { "delayAfterQuit", "delayAfterQuit", XtRInt,
        sizeof(int), XtOffset(AppDataPtr, delayAfterQuit),
        XtRImmediate, (XtPointer) 0},
+    { "keepAlive", "keepAlive", XtRInt,
+       sizeof(int), XtOffset(AppDataPtr, keepAlive),
+       XtRImmediate, (XtPointer) 0},
+    { "forceIllegalMoves", "forceIllegalMoves", XtRBoolean,
+       sizeof(Boolean), XtOffset(AppDataPtr, forceIllegal),
+       XtRImmediate, (XtPointer) False},
 };
 
 XrmOptionDescRec shellOptions[] = {
@@ -1768,6 +1774,8 @@ XrmOptionDescRec shellOptions[] = {
     { "-secondOptions", "secondOptions", XrmoptionSepArg, NULL },
     { "-firstNeedsNoncompliantFEN", "firstNeedsNoncompliantFEN", XrmoptionSepArg, NULL },
     { "-secondNeedsNoncompliantFEN", "secondNeedsNoncompliantFEN", XrmoptionSepArg, NULL },
+    { "-keepAlive", "keepAlive", XrmoptionSepArg, NULL },
+    { "-forceIllegalMoves", "forceIllegalMoves", XrmoptionNoArg, "True" },
 };
 
 
@@ -2290,7 +2298,7 @@ void InitDrawingSizes(BoardSize boardSize, int flags)
        }
 #ifdef GOTHIC
        if(gameInfo.variant == VariantGothic) {
-           ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[i][(int)WhiteSilver];
+           ximMaskPm[(int)WhiteMarshall] = ximMaskPm2[(int)WhiteSilver];
        }
 #endif
 #endif
@@ -2411,25 +2419,35 @@ main(argc, argv)
       XtAppInitialize(&appContext, "XBoard", shellOptions,
                      XtNumber(shellOptions),
                      &argc, argv, xboardResources, NULL, 0);
-    if (argc > 1) {
+    if (argc > 1) 
+      { /* left over command line arguments, print out help and exit.
+        * Use two columns to print help
+        */
        fprintf(stderr, _("%s: unrecognized argument %s\n"),
                programName, argv[1]);
+
        fprintf(stderr, "Recognized options:\n");
-       for(i = 0; i < XtNumber(shellOptions); i++) {
+       for(i = 0; i < XtNumber(shellOptions); i++) 
+         {
+           /* print first column */
            j = fprintf(stderr, "  %s%s", shellOptions[i].option,
                        (shellOptions[i].argKind == XrmoptionSepArg
                         ? " ARG" : ""));
-           if (i++ < XtNumber(shellOptions)) {
+           /* print second column and end line */
+           if (++i < XtNumber(shellOptions)) 
+             {         
                fprintf(stderr, "%*c%s%s\n", 40 - j, ' ',
                        shellOptions[i].option,
                        (shellOptions[i].argKind == XrmoptionSepArg
                         ? " ARG" : ""));
-           } else {
+             } 
+           else 
+             {
                fprintf(stderr, "\n");
-           }
-       }
+             };
+         };
        exit(2);
-    }
+      };
 
     p = getenv("HOME");
     if (p == NULL) p = "/tmp";
@@ -5127,10 +5145,40 @@ void HandleUserMove(w, event, prms, nprms)
 {
     int x, y;
     Boolean saveAnimate;
-    static int second = 0;
+    static int second = 0, promotionChoice = 0;
+    ChessMove moveType;
 
     if (w != boardWidget || errorExitStatus != -1) return;
 
+    x = EventToSquare(event->xbutton.x, BOARD_WIDTH);
+    y = EventToSquare(event->xbutton.y, BOARD_HEIGHT);
+    if (!flipView && y >= 0) {
+       y = BOARD_HEIGHT - 1 - y;
+    }
+    if (flipView && x >= 0) {
+       x = BOARD_WIDTH - 1 - x;
+    }
+
+    if(promotionChoice) { // we are waiting for a click to indicate promotion piece
+       if(event->type == ButtonRelease) return; // ignore upclick of click-click destination
+       promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel
+       if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);
+       if(gameInfo.holdingsWidth && 
+               (WhiteOnMove(currentMove) 
+                       ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0
+                       : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {
+           // click in right holdings, for determining promotion piece
+           ChessSquare p = boards[currentMove][y][x];
+           if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);
+           if(p != EmptySquare) {
+               FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));
+               fromX = fromY = -1;
+               return;
+           }
+       }
+       DrawPosition(FALSE, boards[currentMove]);
+       return;
+    }
     if (event->type == ButtonPress) ErrorPopDown();
 
     if (promotionUp) {
@@ -5145,15 +5193,6 @@ void HandleUserMove(w, event, prms, nprms)
        }
     }
 
-    x = EventToSquare(event->xbutton.x, BOARD_WIDTH);
-    y = EventToSquare(event->xbutton.y, BOARD_HEIGHT);
-    if (!flipView && y >= 0) {
-       y = BOARD_HEIGHT - 1 - y;
-    }
-    if (flipView && x >= 0) {
-       x = BOARD_WIDTH - 1 - x;
-    }
-
     /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */
     if(event->type == ButtonPress
             && ( x == BOARD_LEFT-1 || x == BOARD_RGHT
@@ -5163,7 +5202,7 @@ void HandleUserMove(w, event, prms, nprms)
 
     if (fromX == -1) {
        if (event->type == ButtonPress) {
-           /* First square */
+           /* First square, prepare to drag */
            if (OKToStartUserMove(x, y)) {
                fromX = x;
                fromY = y;
@@ -5178,39 +5217,8 @@ void HandleUserMove(w, event, prms, nprms)
     }
 
     /* fromX != -1 */
-    if (event->type == ButtonPress && gameMode != EditPosition &&
-       x >= 0 && y >= 0) {
-       ChessSquare fromP;
-       ChessSquare toP;
-       int frc;
-
-       /* Check if clicking again on the same color piece */
-       fromP = boards[currentMove][fromY][fromX];
-       toP = boards[currentMove][y][x];
-       frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom;
-       if ((WhitePawn <= fromP && fromP <= WhiteKing && // [HGM] this test should go, as UserMoveTest now does it.
-            WhitePawn <= toP && toP <= WhiteKing &&
-            !(fromP == WhiteKing && toP == WhiteRook && frc)) ||   
-           (BlackPawn <= fromP && fromP <= BlackKing && 
-            BlackPawn <= toP && toP <= BlackKing &&
-            !(fromP == BlackKing && toP == BlackRook && frc))) {
-           /* Clicked again on same color piece -- changed his mind */
-           second = (x == fromX && y == fromY);
-           if (appData.highlightDragging) {
-               SetHighlights(x, y, -1, -1);
-           } else {
-               ClearHighlights();
-           }
-           if (OKToStartUserMove(x, y)) {
-               fromX = x;
-               fromY = y;
-               DragPieceBegin(event->xbutton.x, event->xbutton.y);
-           }
-           return;
-       }
-    }
-
     if (event->type == ButtonRelease &&        x == fromX && y == fromY) {
+    /* Click on single square in stead of drag-drop */
        DragPieceEnd(event->xbutton.x, event->xbutton.y);
        if (appData.animateDragging) {
            /* Undo animation damage if any */
@@ -5230,7 +5238,34 @@ void HandleUserMove(w, event, prms, nprms)
        return;
     }
 
-    /* Completed move */
+    moveType = UserMoveTest(fromX, fromY, x, y, NULLCHAR, event->type == ButtonRelease);
+
+    if (moveType == Comment) { // kludge for indicating capture-own on Press
+      /* Clicked again on same color piece -- changed his mind */
+      /* note that re-clicking same square always hits same color piece */
+      second = (x == fromX && y == fromY);
+      if (appData.highlightDragging) {
+       SetHighlights(x, y, -1, -1);
+      } else {
+       ClearHighlights();
+      }
+      if (OKToStartUserMove(x, y)) {
+       fromX = x;
+       fromY = y;
+       DragPieceBegin(event->xbutton.x, event->xbutton.y);
+      }
+      return;
+    }
+
+    if(moveType == AmbiguousMove) { // kludge to indicate edit-position move
+      fromX = fromY = -1; 
+      ClearHighlights();
+      DragPieceEnd(event->xbutton.x, event->xbutton.y);
+      DrawPosition(FALSE, boards[currentMove]);
+      return;
+    }
+
+    /* Complete move; (x,y) is now different from (fromX, fromY) on both Press and Release */
     toX = x;
     toY = y;
     saveAnimate = appData.animate;
@@ -5252,22 +5287,38 @@ void HandleUserMove(w, event, prms, nprms)
        /* Don't animate move and drag both */
        appData.animate = FALSE;
     }
-    if (IsPromotion(fromX, fromY, toX, toY)) {
-       if (appData.alwaysPromoteToQueen) {
-           UserMoveEvent(fromX, fromY, toX, toY, 'q');
+    if (moveType == WhitePromotionKnight || moveType == BlackPromotionKnight ||
+        (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen) &&
+            appData.alwaysPromoteToQueen) { // promotion, but no choice
+      FinishMove(moveType, fromX, fromY, toX, toY, 'q');
+    } else
+    if (moveType == WhitePromotionQueen || moveType == BlackPromotionQueen ) {
+       SetHighlights(fromX, fromY, toX, toY);
+       if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+           // [HGM] super: promotion to captured piece selected from holdings
+           ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
+           promotionChoice = TRUE;
+           // kludge follows to temporarily execute move on display, without promoting yet
+           boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank
+           boards[currentMove][toY][toX] = p;
+           DrawPosition(FALSE, boards[currentMove]);
+           boards[currentMove][fromY][fromX] = p; // take back, but display stays
+           boards[currentMove][toY][toX] = q;
+           DisplayMessage("Click in holdings to choose piece", "");
+           return;
+       }
+       PromotionPopUp();
+       goto skipClearingFrom; // the skipped stuff is done asynchronously by PromotionCallback
+    } else
+    if(moveType != ImpossibleMove) { // valid move, but no promotion
+      FinishMove(moveType, fromX, fromY, toX, toY, NULLCHAR);
+    } else { // invalid move; could have set premove
+      ClearHighlights();
+    }
            if (!appData.highlightLastMove || gotPremove) ClearHighlights();
            if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
            fromX = fromY = -1;
-       } else {
-           SetHighlights(fromX, fromY, toX, toY);
-           PromotionPopUp();
-       }
-    } else {
-       UserMoveEvent(fromX, fromY, toX, toY, NULLCHAR);
-       if (!appData.highlightLastMove || gotPremove) ClearHighlights();
-       if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
-       fromX = fromY = -1;
-    }
+skipClearingFrom:
     appData.animate = saveAnimate;
     if (appData.animate || appData.animateDragging) {
        /* Undo animation damage if needed */
@@ -6052,7 +6103,7 @@ void PromotionCallback(w, client_data, call_data)
        promoChar = ToLower(name[0]);
     }
 
-    UserMoveEvent(fromX, fromY, toX, toY, promoChar);
+    FinishMove(NormalMove, fromX, fromY, toX, toY, promoChar);
 
     if (!appData.highlightLastMove || gotPremove) ClearHighlights();
     if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
@@ -7637,19 +7688,34 @@ void Iconify(w, event, prms, nprms)
 void DisplayMessage(message, extMessage)
      char *message, *extMessage;
 {
-    char buf[MSG_SIZ];
-    Arg arg;
-
-    if (extMessage) {
-       if (*message) {
-           snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
-           message = buf;
-       } else {
-           message = extMessage;
-       }
-    }
-    XtSetArg(arg, XtNlabel, message);
-    XtSetValues(messageWidget, &arg, 1);
+  /* display a message in the message widget */
+  
+  char buf[MSG_SIZ];
+  Arg arg;
+  
+  if (extMessage) 
+    {
+      if (*message) 
+       {
+         snprintf(buf, sizeof(buf), "%s  %s", message, extMessage);
+         message = buf;
+       } 
+      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 */
+  if(messageWidget)
+    {
+      XtSetArg(arg, XtNlabel, message);
+      XtSetValues(messageWidget, &arg, 1);
+    };
+  
+  return;
 }
 
 void DisplayTitle(text)
@@ -8140,6 +8206,9 @@ void
 ScheduleDelayedEvent(cb, millisec)
      DelayedEventCallback cb; long millisec;
 {
+    if(delayedEventTimerXID && delayedEventCallback == cb)
+       // [HGM] alive: replace, rather than add or flush identical event
+       XtRemoveTimeOut(delayedEventTimerXID);
     delayedEventCallback = cb;
     delayedEventTimerXID =
       XtAppAddTimeOut(appContext, millisec,
@@ -9483,4 +9552,4 @@ SetProgramStats( FrontEndProgramStats * stats )
   // [HR] TODO
   // [HGM] done, but perhaps backend should call this directly?
     EngineOutputUpdate( stats );
-}
\ No newline at end of file
+}