Allow WinBoard to use bitmaps with alpha channel
[xboard.git] / winboard / winboard.c
index 176dc35..293b643 100644 (file)
@@ -5,7 +5,8 @@
  * Massachusetts.\r
  *\r
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
- * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc.\r
+ * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Free\r
+ * Software Foundation, Inc.\r
  *\r
  * Enhancements Copyright 2005 Alessandro Scotti\r
  *\r
  *------------------------------------------------------------------------\r
  ** See the file ChangeLog for a revision history.  */\r
 \r
+#ifndef WINVER\r
+#define WINVER 0x0500\r
+#endif\r
+\r
 #include "config.h"\r
 \r
 #include <windows.h>\r
@@ -1145,7 +1150,7 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
       broadcast.which = "broadcaster";\r
       broadcast.pr   = NoProc;\r
       broadcast.isr  = NULL;\r
-      broadcast.prog = c + 3;\r
+      broadcast.program = c + 3;\r
       broadcast.dir  = ".";\r
       broadcast.host = "localhost";\r
       StartChessProgram(&broadcast);\r
@@ -2192,7 +2197,7 @@ void CreatePiecesFromFont()
 HBITMAP\r
 DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)\r
 {\r
-  char name[128], buf[MSG_SIZ];\r
+  char name[128], buf[MSG_SIZ], *ids = "pnbrqfeicwmohajgdvlsukaacvdklnwpwnwlwswolfgnuzebracameltowersword", *p;\r
 \r
     snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
   if(appData.pieceDirectory[0]) {\r
@@ -2200,6 +2205,30 @@ DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
     snprintf(buf, MSG_SIZ, "%s\\%s.bmp", appData.pieceDirectory, name);\r
     res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
     if(res) return res;\r
+    p = strstr(ids, piece);\r
+    if(p) { // if we could reconstruct canonical piece number, try the pieceNNN_ format before falling back on built-ins\r
+      int n = p - ids;\r
+      switch(n) {\r
+       case 21: n = WhiteKing; break;\r
+       case 22: n = WhiteAngel; break;\r
+       case 24: n = WhiteSilver; break;\r
+       case 26: n = WhiteDragon; break;\r
+       case 28: n = WhiteLion; break;\r
+       case 30: n = WhiteTokin; break;\r
+       case 32: n = WhitePKnight; break;\r
+       case 34: n = WhitePLance; break;\r
+       case 36: n = WhitePSilver; break;\r
+       case 38: n = WhiteWolf; break;\r
+       case 42: n = WhiteGnu; break;\r
+       case 45: n = WhiteZebra; break;\r
+       case 50: n = WhiteCamel; break;\r
+       case 55: n = WhiteTower; break;\r
+       case 60: n = WhiteSword; break;\r
+      }\r
+      snprintf(buf, MSG_SIZ, "%s\\piece%d_%d%s.bmp", appData.pieceDirectory, n, squareSize, suffix);\r
+      res = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
+      if(res) return res;\r
+    }\r
   }\r
   if (gameInfo.event &&\r
       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
@@ -2714,6 +2743,27 @@ InitDrawingSizes(BoardSize boardSize, int flags)
     pieceBitmap[0][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
     pieceBitmap[1][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
     pieceBitmap[2][WhiteLion] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
+    pieceBitmap[0][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "s");\r
+    pieceBitmap[1][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "o");\r
+    pieceBitmap[2][WhiteCub] = DoLoadBitmap(hInst, "ln", squareSize, "w");\r
+    pieceBitmap[0][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "s");\r
+    pieceBitmap[1][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "o");\r
+    pieceBitmap[2][WhiteWolf] = DoLoadBitmap(hInst, "wolf", squareSize, "w");\r
+    pieceBitmap[0][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "s");\r
+    pieceBitmap[1][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "o");\r
+    pieceBitmap[2][WhiteCamel] = DoLoadBitmap(hInst, "camel", squareSize, "w");\r
+    pieceBitmap[0][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "s");\r
+    pieceBitmap[1][WhiteZebra] = DoLoadBitmap(hInst, "zebra", squareSize, "o");\r
+    pieceBitmap[2][WhiteZebra] = DoLoadBitmap(hInst, "n", squareSize, "w");\r
+    pieceBitmap[0][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "s");\r
+    pieceBitmap[1][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "o");\r
+    pieceBitmap[2][WhiteTower] = DoLoadBitmap(hInst, "tower", squareSize, "w");\r
+    pieceBitmap[0][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "s");\r
+    pieceBitmap[1][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "o");\r
+    pieceBitmap[2][WhiteSword] = DoLoadBitmap(hInst, "sword", squareSize, "w");\r
+    pieceBitmap[0][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "s");\r
+    pieceBitmap[1][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "o");\r
+    pieceBitmap[2][WhiteGnu] = DoLoadBitmap(hInst, "gnu", squareSize, "w");\r
 \r
     if(gameInfo.variant == VariantShogi && BOARD_HEIGHT != 7) { /* promoted Gold representations (but not in Tori!)*/\r
       pieceBitmap[0][WhiteCannon] = DoLoadBitmap(hInst, "wp", squareSize, "s");\r
@@ -2970,7 +3020,7 @@ VOID
 DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y, HDC tmphdc)\r
 {\r
   HBITMAP oldBitmap;\r
-  HBRUSH oldBrush;\r
+  HBRUSH oldBrush = NULL;\r
   int tmpSize;\r
 \r
   if (appData.blindfold) return;\r
@@ -3029,6 +3079,20 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
       y += squareSize - minorSize - 2;\r
       tmpSize = minorSize;\r
     }\r
+#if WINVER >= 0x0500\r
+    HBITMAP pbm = PieceBitmap(piece, color ? OUTLINE_PIECE : SOLID_PIECE);\r
+    BITMAP b;\r
+    GetObject(pbm, sizeof(BITMAP), &b);\r
+    if(b.bmBitsPixel == 32) { // for now this is a kludge to indicate bitmaps with alpha channel\r
+       BLENDFUNCTION bf;\r
+       bf.BlendOp = AC_SRC_OVER;\r
+       bf.BlendFlags = 0;\r
+       bf.SourceConstantAlpha = 0xFF;\r
+       bf.AlphaFormat = AC_SRC_ALPHA;\r
+       oldBitmap = SelectObject(tmphdc, pbm);\r
+       AlphaBlend(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, tmpSize, tmpSize, bf);\r
+    } else\r
+#endif\r
     if (color || appData.allWhite ) {\r
       oldBitmap = SelectObject(tmphdc, PieceBitmap(piece, WHITE_PIECE));\r
       if( color )\r
@@ -3065,7 +3129,7 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
       else\r
         BitBlt(hdc, x, y, tmpSize, tmpSize, tmphdc, 0, 0, 0x00B8074A);\r
     }\r
-    SelectObject(hdc, oldBrush);\r
+    if(oldBrush) SelectObject(hdc, oldBrush);\r
     SelectObject(tmphdc, oldBitmap);\r
   }\r
 }\r
@@ -3740,6 +3804,10 @@ void DrawSeekClose()
 {\r
 }\r
 \r
+\r
+\r
+\r
+\r
 VOID\r
 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
 {\r
@@ -3960,6 +4028,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
         explodes.  The old and new positions both had an empty square\r
         at the destination, but animation has drawn a piece there and\r
         we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
+\r
       lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
     }\r
   }\r
@@ -4093,7 +4162,12 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
   if(saveDiagFlag) { \r
     BITMAP b; int i, j=0, m, w, wb, fac=0; char *pData; \r
     BITMAPINFOHEADER bih; int color[16], nrColors=0;\r
+    HBITMAP src = bufferBitmap, obmp; HDC tmp = CreateCompatibleDC(hdc);\r
 \r
+    bufferBitmap = CreateCompatibleBitmap(hdc, boardRect.right-boardRect.left, Rect.bottom-Rect.top-2*OUTER_MARGIN);\r
+    obmp = SelectObject(tmp, bufferBitmap);\r
+    BitBlt(tmp, 0, 0, boardRect.right - boardRect.left, Rect.bottom - Rect.top - 2*OUTER_MARGIN,\r
+           tmphdc, boardRect.left, OUTER_MARGIN, SRCCOPY);\r
     GetObject(bufferBitmap, sizeof(b), &b);\r
     if(pData = malloc(b.bmWidthBytes*b.bmHeight + 10000)) {\r
        bih.biSize = sizeof(BITMAPINFOHEADER);\r
@@ -4157,10 +4231,14 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
        if(fac)\r
        for(i=0; i<16; i++) fputDW(diagFile, color[i]);\r
        // write bitmap data\r
+\r
        for(i=0; i<wb*(b.bmHeight - boardRect.top + OUTER_MARGIN); i++) \r
                fputc(pData[i], diagFile);\r
        free(pData);\r
      }\r
+     DeleteObject(bufferBitmap); bufferBitmap = src;\r
+     SelectObject(tmp, obmp);\r
+     DeleteDC(tmp);\r
   }\r
 \r
   SelectObject(tmphdc, oldBitmap);\r
@@ -6248,6 +6326,8 @@ InitComboStringsFromOption(HANDLE hwndCombo, char *str)
     str = buf1;\r
   }\r
 \r
+\r
+\r
   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
 \r
   for (;;) {\r
@@ -8246,8 +8326,8 @@ ModeHighlight()
     nowChecked = 0;\r
     break;\r
   }\r
-  if(prevChecked == IDM_TwoMachine) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
-    EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED)\r
+  if(prevChecked == IDM_TwoMachines) // [HGM] 'Machine Match' might have gotten disabled when stopping match\r
+    EnableMenuItem(GetMenu(hwndMain), IDM_Match, MF_BYCOMMAND|MF_ENABLED);\r
   CheckMark(prevChecked, MF_UNCHECKED);\r
   CheckMark(nowChecked, MF_CHECKED);\r
   CheckMark(IDM_Match, matchMode && matchGame < appData.matchGames ? MF_CHECKED : MF_UNCHECKED);\r
@@ -8492,6 +8572,7 @@ DisplayFatalError(char *str, int error, int exitStatus)
     fprintf(debugFP, "%s: %s\n", label, str);\r
   }\r
   if (appData.popupExitMessage) {\r
+    if(appData.icsActive) SendToICS("logout\n"); // [HGM] make sure no new games will be started!\r
     (void) MessageBox(hwndMain, str, label, MB_OK|\r
                      (exitStatus ? MB_ICONSTOP : MB_ICONINFORMATION));\r
   }\r
@@ -8505,6 +8586,11 @@ DisplayInformation(char *str)
   (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
 }\r
 \r
+char *\r
+Shorten (char *s)\r
+{\r
+  return s;\r
+}\r
 \r
 VOID\r
 DisplayNote(char *str)\r
@@ -9114,6 +9200,7 @@ IDLE_PRIORITY_CLASS         0x00000040
 */\r
         if (nice < -15) return 0x00000080;\r
         if (nice < 0)   return 0x00008000;\r
+\r
         if (nice == 0)  return 0x00000020;\r
         if (nice < 15)  return 0x00004000;\r
         return 0x00000040;\r
@@ -10024,8 +10111,8 @@ AnimateMove(board, fromX, fromY, toX, toY)
      int toX;\r
      int toY;\r
 {\r
-  ChessSquare piece;\r
-  int x = toX, y = toY;\r
+  ChessSquare piece, victim = EmptySquare, victim2 = EmptySquare;\r
+  int x = toX, y = toY, x2 = kill2X;\r
   POINT start, finish, mid;\r
   POINT frames[kFactor * 2 + 1];\r
   int nFrames, n;\r
@@ -10038,7 +10125,11 @@ AnimateMove(board, fromX, fromY, toX, toY)
   piece = board[fromY][fromX];\r
   if (piece >= EmptySquare) return;\r
 \r
-  if(killX >= 0) toX = killX, toY = killY; // [HGM] lion: first to kill square\r
+  if(x2 >= 0) toX = kill2X, toY = kill2Y,  victim = board[killY][killX], victim2 = board[kill2Y][kill2X]; else\r
+  if(killX >= 0) toX = killX, toY = killY, victim = board[killY][killX]; // [HGM] lion: first to kill square\r
+\r
+  animInfo.from.x = fromX;\r
+  animInfo.from.y = fromY;\r
 \r
 again:\r
 \r
@@ -10066,22 +10157,30 @@ again:
   else\r
     Tween(&start, &mid, &finish, kFactor, frames, &nFrames);\r
 \r
-  animInfo.from.x = fromX;\r
-  animInfo.from.y = fromY;\r
   animInfo.to.x = toX;\r
   animInfo.to.y = toY;\r
   animInfo.lastpos = start;\r
   animInfo.piece = piece;\r
   for (n = 0; n < nFrames; n++) {\r
     animInfo.pos = frames[n];\r
-    DrawPosition(FALSE, NULL);\r
+    DrawPosition(FALSE, board);\r
     animInfo.lastpos = animInfo.pos;\r
     Sleep(appData.animSpeed);\r
   }\r
   animInfo.pos = finish;\r
-  DrawPosition(FALSE, NULL);\r
-\r
-  if(toX != x || toY != y) { fromX = toX; fromY = toY; toX = x; toY = y; goto again; } // second leg\r
+  DrawPosition(FALSE, board);\r
+\r
+  if(toX == x2 && toY == kill2Y) {\r
+    fromX = toX; fromY = toY; toX = killX; toY = killY; x2 = -1;\r
+    board[kill2Y][kill2X] = EmptySquare; goto again;\r
+  } // second leg\r
+  if(toX != x || toY != y) {\r
+    fromX = toX; fromY = toY; toX = x; toY = y;\r
+    board[killY][killX] = EmptySquare; goto again;\r
+  } // second leg\r
+\r
+if(victim2 != EmptySquare) board[kill2Y][kill2X] = victim2;\r
+if(victim  != EmptySquare) board[killY][killX] = victim;\r
 \r
   animInfo.piece = EmptySquare;\r
   Explode(board, fromX, fromY, toX, toY);\r